PE文件结构(导入表)
导入表
什么是导入表?
导入表就是pe文件需要依赖哪些模块以及依赖这些模块中的哪些函数
回想我们导出表的内容,导出表的位置和大小是保存在扩展pe头最后一个结构体数组当中的
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]
第一个成员是我们的导出表,第二成员就是我们的导入表了
通过分析pe文件,得出导入表的位置在0x20400
(RVA)
我们来看一下导入表的结构
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {DWORD Characteristics; // 0 for terminating null import descriptorDWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name; // RVA,指向dll名字
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
导入表-确定依赖模块
通过rva到foa的转换后得到第一个导入表的地址:0xD600
(这里的导入表并不是只有一个,一般来说程序使用了多少个模块就有多少个导入表,那么怎么看导入表结束的位置呢)
我们先看第一个导入表的name属性:0x20876(RVA),转为FOA为:0xDA76
我们已经查询出来了第一个导入表对应的名称是user32.dll
导入表并不是只有一个,导入表结构体的大小为25个字节,从第一个
0xD600
开始,每25个字节为一个新的导入表,直到下一个导入表的name属性为0x0000
表示着最后一个导入表的结束
导入表-确定依赖函数
确定导入表中都使用了模块中的哪些方法
关注导入表OriginalFirstThunk和FirstThunk中的属性,OriginalFirstThunk指向的是 INT(导入名称) 表,FirstThunk指向的是 IAT (导入地址)表
IAT结构体
typedef struct _IMAGE_THUNK_DATA64 {union {ULONGLONG ForwarderString; // PBYTE (无需关注)ULONGLONG Function; // PDWORD(无需关注)ULONGLONG Ordinal; // 序号ULONGLONG AddressOfData; // 指向PIMAGE_IMPORT_BY_NAME} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;
我们看下导入表结构体中这个属性OriginalFirstThunk为:0x205B8
(RVA)
转为FOA后:0xD4B0
可以得到以下IAT信息(RVA)
IMAGE_THUNK_DATA |
---|
0x20732 |
0x20718 |
0x20B04 |
0x206FC |
0x206DC |
0x206BE |
0x206A8 |
0x20688 |
0x20670 |
接下来我们来分析IAT结构体中的数据
// IMAGE_IMPORT_BY_NAME
typedef struct _IMAGE_IMPORT_BY_NAME {WORD Hint; // 可能为空,编译器决定如果不为空则为函数在导出表中的索引CHAR Name[1]; // 函数名称以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
首先我们分析 0x20732(在二进制下)的最高位是否为1,很显然并不为1
再把这个地址(0x20732 )RVA 转为FOA得到 0xD732 (_IMAGE_IMPORT_BY_NAME) 结构体所在的地址
第一个成员 Hint对应 0x31
,第二个成员Name,指模块中的方法名以0结尾
导入表-确定函数地址
我们再看最后一个导入表成员FirstThunk属性
FirstThunk和OriginalFirstThunk在程序未执行之前,它们两指向的都是同的结构体数组,但是一但程序运行起来后,FirstThunk指向的是要运行方法的地址