PE结构-导入表

在PE当中,含有导出表和导入表,两者的功能是什么呢?

一.功能

导入表:在PE文件中供自己使用的函数在一张表当中,便于寻找和使用

导出表:在PE文件中供其他PE文件使用的函数在一张表中,便于其他PE文件进行使用

今天着重介绍导入表

二.误区

在大众对PE的认知当中,可能会认为exe文件只有导入表,没有导出表(即只有供自己使用的函数),dll即有导入表又有导出表,其实并不然,exe在本质原理上是可以具有导出表的,只是在使用时并没有那方面的需求,所以就没有提供导出表。

三.具体分析

要想分析导出表具体内容,首先要知道如何去定位导出表

在PE可选头中,观察最后一个属性,数据项目录

 在该属性中,含有16个结构体大小为8的结构体

前两个结构体分别代表导出表和导入表的RVA值及大小(注意,这里是RVA,即我们在文件中观察时,要明确内存对齐和文件对齐,即RVA 和 FOA 的转换)

这里写了一个简单的DLL,进行到=导出表的分析(该DLL实现了加减乘除四个功能),下面给出DLL源码

// newdll.cpp: implementation of the newdll class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "newdll.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
int __stdcall Plus(int x,int y){
    return x+y;
}
int __stdcall Min (int x,int y){
    return x-y;
}
int __stdcall Mul (int x,int y){
    return x*y;
}
int __stdcall Div (int x,int y){
    return x/y;
}

extern "C" __declspec(dllexport) __stdcall int Plus(int x,int y);
extern "C" __declspec(dllexport) __stdcall int Min(int x,int y);
extern "C" __declspec(dllexport) __stdcall int Mul(int x,int y);
extern "C" __declspec(dllexport) __stdcall int Div(int x,int y);

用Winhex打开该dll文件进行分析,这里要求必须熟练掌握PE结构

通过查找发现,从000001C8开始是节表区域,因为可选头与节表是无缝连接的,所以我们可以从节表开始区向前找,因为数据目录项中,每一个结构体为8个字节,有16个结构体,根据该特征向前查找即可

标记区域即数据目录项,因为前面了解到,前8个字节分别代表导出表的RVA和大小,所以得知,导出表的RVA为2DF10(该DLL内存对齐和文件对齐相同,所以不需要RVA 和 FOA 的转换)

转到2DF10查看,该处即为导出表

 要理解表中数据的含义,就要求掌握导出表中的属性

 着重介绍几个属性

Name                    //dll的名称
Base                    //基数索号 = 序号-基数
NumberOfFunctions                 //函数的总数
NumberOfNames                    //有名函数的总数(在dll中函数可以用名称命名也可以用名称命名)
AddressOfFunctions               //导入函数地址表RVA
AddressOfNames                  //导入函数名称表RVA
AddressOfNameOrdinals       //导入函数序号表RVA

在导入表中,又存在着三张表,即导入函数地址表,导入函数名称表,导入函数序号表,这三张表至关重要,是获取函数的路径

 当得出这个表关系时,就可以真正理解当通过函数名称查询函数地址时,它是经过怎样的步骤进行查询的

首先,如果要查询Mul,就会先去函数名称表当中查询Mul所在表中的索引号是多少,这里可以看到为2,然后拿着这个2去函数序号表找索引为2的内容,可以发现为3,然后拿着3去找函数地址表中所对应的索引内容,即0000100A,这样就找到了函数的地址

如果是按照函数的序号查询,这里就会用到基数(Base)的概念,因为在默认情况下(即不改变函数序号的情况下),函数序号都是从1开始的,如果改变了,就需要基数的介入,即在函数序号中最小的数当作0,依次加1,其实就相当于0,1,2,3以此类推,这样就可以直接拿序号去函数地址表通过相应的基数进行查找,就能找到相应的函数地址

           

原文地址:https://www.cnblogs.com/Virus-Faker/p/12552777.html