导入表(IAT) word最后一个字母为D表示为2个字节,dword为2*2=4个字节
word最后一个字母为D表示为2个字节,dword为2*2=4个字节
导入表是由一系列的IMAGE_IMPORT_DESCRIPTOR结构组成的。结构的个
数由文件引用的DLL个数决定,文件引用了多少个DLL就有多少个IMAGE_IMPORT_DESCRIPTOR
结构,最后还有一个全为零的IMAGE_IMPORT_DESCRIPTOR作为结束。(隐式加载才有输入节) //每个IID20个字节.
输入表为若干个IID组成以及最后一个都为0的IID结构. 每个IID为20个字节
一个dll对应一个IMAGE_IMPORT_DESCRIPTOR结构。一个function对应一个IMAGE_THUNK_DATA结构。
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //指向INT
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; //DLL名字 IID结构的第四个元素为DLL的名字,没有固定字节数.
DWORD FirstThunk; //指向IAT
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA {
union {
PBYTE ForwarderString;
PDWORD Function;//函数RVA地址
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;//函数名或序号
} ;
} IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA; //2个字节。一个函数的地址。 一个DLL有很多的函数。
OriginalFirstThunk和FirstThunk在一个PE没有加载到内存中的时候是一样的,都是指向一
个IMAGE_THUNK_DATA结构数组。最后以一个内容为0的结构结束。其实这个结构就是一个双 //前面一个字为有内容的IMAGE_THUNK_DATE
字。这个结构很有意思,因为在不同的时候这个结构代表着不同的含义。当这个双字的最高 //后面一个字为都是0的IMAGE_THUNK_DATE
位为1时,表示函数是以序号的方式导入的;当最高位为0时,表示函数是以名称方式导入的,
这是这个双字是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构,这个结构用来指定导入函数
名称。
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //前面两个字节是序号,不是函数名
BYTE Name[1]; //字节不固定
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Hint字段表示一个序号,不过因为是按名称导入,所以这个序号一般为零。
Name字段是函数的名称
IMAGE-IMPORT-DESCRIPTOR和IMAGE-THUNK-DATA分别对应于DLL和函数。
一个PE没有被加载到内存中的时候IMAGE_IMPORT_DESCRIPTOR中的
OriginalFirstThunk和FirstThunk是相同的,那么为什么需要占用两个字段呢?其实
是这样的,在PE文件被PE加载器加载到内存中的时候这个加载器会自动把FirstThunk的值替
换为API函数的真正入口,也就是那个前面jmp的真正地址,而OriginalFirstThunk只不过是
用来反向查找函数名而已。