数据目录表的第二项为导入表
导入表 即 应用程序需要调用函数的地址一个表
导入表结构
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk;//指向INT表 导入名字表
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;//指向IAT表 导入地址表
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
导入表结构是 多个连续的IMAGE_IMPORT_DESCRIPTOR结构体,可以用Name来判断是否遍历完.
OriginalFirstThunk 指向INT表
IMAGE_THUNK_DATA32结构为4字节大小 如果这个值的最高位(32位)为1 那么剩下的31位为所需要函数的导出序号(该函数在dll中是以序号导出的),如果为0,那么这个值是IMAGE_IMPORT_BY_NAME结构的RVA
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
该结构的前两个字节为函数在地址表的索引(没多大用),第三个字节是函数名字的开始。
Name
DLL文件的名字RVA,从结果可以看出导入表的dll名字都是挨在一起的。
FirstThunk
指向IAT表 ,一般情况下,在程序加载前IAT表的值与INT表一样。在加载后,IAT表的值为函数的地址。
在加载时,通过INT表找到第一个函数的名字,然后通过GetProcAddress得到函数的地址,写入IAT表同样索引的位置。然后找第二个函数的地址,遍历完INT表,IAT表的地址也就写入完成。
还有一种情况是IAT表在加载前就直接写成了绝对地址,所需DLL的ImageBase设置成不同的值,这个表即可在程序加载前直接写入。通过_IMAGE_IMPORT_DESCRIPTOR的TimeDateStamp(时间戳)可以判断导入表是否被绑定,当这个值为0时,未绑定,这个值为-1时,被绑定。
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp;
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs;
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
绑定导入表,位于数据目录表的第12项。它的TimeDateStamp记录了绑定的时间。
OffsetModuleName 模块的名字RVA
NumberOfModuleForwarderRefs 这个模块所依赖的DLL个数,有几个DLL依赖项,它的后面就跟着几个_IMAGE_BOUND_FORWARDER_REF结构
typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp;时间戳
WORD OffsetModuleName; DLL文件名的偏移
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
OffsetModuleName真正的文件名=第一个绑定导入表的开始地址+这个偏移
最后一个绑定导入表为全0结构
修复IAT表