PE文件结构
导入表
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA 指向 INT (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名字,以0结尾
DWORD FirstThunk; // RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //可能为空,编译器决定,如果不为空,是函数在导出表的索引
BYTE Name[1]; //函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
导入表是一个数组,每一个元素都是一个IMAGE_IMPORT_DESCRIPTOR
(最后一个元素为空来表示数组的结束,后面的数组都是这样的),每一个IMAGE_IMPORT_DESCRIPTOR
都代表了一个dll
,Name
存放着dll
名称的地址,OriginalFirstThunk
存放着INT
的地址,FirstThunk
存放着IAT
的地址。
INT
也是一个数组,每一个元素都是一个ULONGLONG AddressOfData;
,AddressOfData
存放着IMAGE_IMPORT_BY_NAME
的地址。
IAT
也是一个数组,每一个元素都是一个ULONGLONG Function;
,Function
存放着导入函数的地址。
INT
和IAT
的元素是根据索引一一对应的
IAT
填充
下面介绍在PE加载过程中操作系统如何对IAT
进行填充
- 获取模块句柄
通过IMAGE_IMPORT_DESCRIPTOR
的成员Name
获取dll名称地址
调用GetModuleHandle(dll函数名称)
,获取该dll模块句柄
- 获取函数地址
首先通过OriginalFirstThunk
,找到INT
,通过INT
的元素AddressOfData
找到IMAGE_IMPORT_BY_NAME
,若IMAGE_IMPORT_BY_NAME.Name
不为空,则通过调用GetProcAddress(IMAGE_IMPORT_BY_NAME.Name)
获取该函数地址
,若IMAGE_IMPORT_BY_NAME.Name
为空,则通过调用GetProcAddress(IMAGE_IMPORT_BY_NAME.Hint)
获取该函数地址
- 填充
IAT
通过INT
的元素AddressOfData
的索引,找到IAT
中对应的元素Function
,将获取到的函数地址
填充到Function
中