一、前言
二、PE整体结构
三、DOS头
四、NT头
五、区段头
六、导出表
七、导入表
八、资源表
九、其他表
1.概念
导入 是PE文件(EXE)在运行时,使用到了其他PE文件(DLL)中的函数、变量、类等这样的行为。
导入表 存储的是从其他PE文件(DLL)导入过来的函数名、序号。在加载到内存后,还存储这些函数的地址。
导入表结构 导入表是一个结构体数组,最后以一个结构体大小的全零元素结尾。每一个数组元素代表一个PE文件的导入信息。
PE文件(EXE)通常需要多个PE文件(DLL)的支持,所以导入表一般有多个。
2.导入表定位
前面提到的 NT头->扩展头->数据目录表->第二个元素->相对虚拟地址(RVA)
还用010Editor打开百度云盘看一下
从扩展头里的数据,我们就可以得到导入表的定位信息RVA
再啰嗦一下RVA定位数据的使用
如果是在文件里用,需要用FOA
导入表FOA = 导入表RVA - 区段RVA + 区段FOA
如果是加载到内存里使用,需要用VA
导入表VA = 加载基址BaseImage + 导入表RVA
实践一下:
我们是在文件里操作,所以要先得到FOA
要得到FOA先要得到导入表所在.rdata区段信息
区段信息怎么得到还记得吗?
对,在区段头表里有相关信息,忘了就看看前面的文章吧
导入表FOA = 导入表RVA - 区段RVA + 区段FOA = 5706C0H - 45F000H + 45D800H = 56EEC0H
3.导入表结构
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // 指向 导入名称表INT(ImportNameTable)的RVA
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // 指向 DLL名称的地址 RVA
DWORD FirstThunk; // 指向 导入地址表IAT(ImportAddressTable)的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
导入表结构体大小为20个字节。
有几个模块,就有几个结构体,从起始地址开始,每个20字节,遇到20个0结束。
实践一下:
上一环节,我们找到了导入表FOA,我们来看看FOA对应的数据
图中圈除了三个元素,每个20字节
我们再寻找一下结束的位置,20个0
果然找到了20个0,0x56F207,应该就是导入表结束的位置。
我们在看看其他的一些数据
导入表大小=结束位置-起始位置=0x56F208-0x56EEC0=0x348=840
导入表元素个数=导入表大小/元素大小=840/20=42
也就是导入了42个DLL
4.导入名称表、导入地址表
4.1.表内数据元素结构
导入名称表和导入地址表结构完全相同。
INT和IAT两张表内的元素都是4字节大小的结构体IMAGE_THUNK_DATA
数据元素列表遇到4字节0结束
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function; // 导入函数地址
DWORD Ordinal; // 序号导入用
DWORD AddressOfData; // 函数名导入用,指向PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
4.2.导入名称表、导入地址表 关系
在磁盘文件中,OriginalFirstThunk指向的INT 与 FirstThunk指向的IAT 两表中的数据是相同的。
加载到内存后,输入地址表会由加载器把对应的PE文件中的函数地址覆盖到这里。
4.3.使用规则
通过结构体定义我们可以看到,结构体里面是一个联合体
也就是说,其实是同一个数据,看你用哪种形式使用
如果加载到内存中,Function函数地址启用
如果磁盘文件中,只有后两个字段起作用Ordinal、AddressOfData。当数据最高位为1时Ordinal序号起作用,当数据最高位为0时AddressOfData函数名起作用,指向结构体。
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; // 如不为空,值为函数在导出表中的索引
CHAR Name[1]; // 函数名的第一字节,到0结束
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
实践一下:
不按上面写的那么复杂的去做了,简单的在文件中看一下
找到导入表第一个元素,找到第一个元素内的INT地址和IAT地址
INT表FOA = 0x5715CC - 0x45F000 + 0x45D800 = 0x56FDCC
函数名FOA = 0x571634 - 0x45F000 + 0x45D800 = 0x56FE34