http://hi.baidu.com/mymillet/blog/item/0c09123debead3c19f3d6249.html
输入表结构我们知道,程序调用外部的dll函数通常都是下面这种形式:
call my_label
...
my_label: jmp dword ptr [xxxxxxxx]
对一个dll中的函数的调用总是通过一个地址间接的调用的.这些地址就放在输入表里(所以如果你改动了输入表, 可能也需要改my_label: jmp dword ptr [xxxxxxxx]).
输入表(Import Table),简而言之,就是描述该pe文件从哪几个动态连接库导入了什么函数的一组结构数组.在这里我希望能用最简洁的语言让你明白什么是输入表.输入表的组成并不复杂,只用到三个结构.它们是:IMAGE_IMPORT_DESCRIPTOR,IMAGE_THUNK_DATA,IMAGE_IMPORT_BY_NAME.
我们先看一下框图.
IMAGE_IMPORT_DESCRIPTOR
|---------------------------|
|---------------------| OriginalFirstThunk |
| |---------------------------|
| | TimeDateStamp |
| |---------------------------|
| | ForwarderChain |
| |---------------------------|
| | Name |----> "USER32.DLL"
| |---------------------------|
| | FirstThunk |---------------------------------------------------------------------|
| |---------------------------|(装入内存后FirstThunk指向的结构数组会被修改) |
| |
| Hint-name table IMAGE_IMPORT_BY_NAME import address table(IAT) |
| |--------------------------------| |--------------------------------| |--------------------------------| |
|-> | IMAGE_THUNK_DATA |-->| 44 | "GetMessage" |<--| IMAGE_THUNK_DATA|<---|
|--------------------------------| |-----|--------------------------| |--------------------------------|
| IMAGE_THUNK_DATA |-->| 72 | "LoadIcon" |<--| IMAGE_THUNK_DATA |
|--------------------------------| |-----|--------------------------| |--------------------------------|
| ...... |-->| .. | ...... |<--| ...... |
|--------------------------------| |------|--------------------------| |---------------------------------|
| NULL | | NULL |
|--------------------------------| |----------------------------------|
当然,这是描述从一个dll中引入函数的情形.从几个dll中引入函数,那么就有几个这样的结构.同时,这也是磁盘文件上的结构.装入内存后FirstThunk指向的结构数组会被修改.可以看下面的图.
我们先来熟悉一下这三个结构的定义:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
};
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;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //指出函数在所在的dll的输出表中的序号
BYTE Name[1]; //指出要输入的函数的函数名
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
IMAGE_IMPORT_DESCRIPTOR结构的各个域的含义:
1)union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
这个联合指向一个 IMAGE_THUNK_DATA 类型的结构数组.这个联合不是很重要,可以为0.
2)TimeDateStamp
该dll的时间日期戳,一般为0.
3)ForwarderChain
正向连接索引.一般为0.
4)Name
dll名字的RVA.
5)FirstThunk
这个域也是一个RVA,指向一个DWORD数组,数组以NULL结束.数组中的每个DWORD实际上是一个IMAGE_THUNK_DATA结构的联合体。IMAGE_THUNK_DATA联合体通常被解释为一个指向IMAGE_IMPORT_BY_NAME结构的RVA.
从上图我们可以看出有两个并行的指针数组都指向IMAGE_IMPORT_BY_NAME结构.事实上,OriginalFirstThunk指向的IMAGE_THUNK_DATA结构数组从来不被修改,该数组有时也叫提示名表(Hint-name table),提示名表总是指向IMAGE_IMPORT_BY_NAME结构数组.而FirstThunk指向的IMAGE_THUNK_DATA结构数组在该pe文件被加载时,加载程序会修改该数组的内容.加载程序迭代搜索数组的每一个指针,找到每一个IMAGE_IMPORT_BY_NAME结构所对应的输入函数的地址,然后加载程序用找到的地址修改相应的IMAGE_THUNK_DATA结构.(这是理解加载过程的关键).
如前面提到的
call my_label
...
my_label: jmp dword ptr [xxxxxxxx]
其中的xxxxxxxx就是FirstThunk指向的IMAGE_THUNK_DATA数组中的一个的值.因为FirstThunk所指向的数组在加载后是所有输入函数的地址,因此它被称为输入地址表(Import Address Table,IAT).
pe文件加载后输入表的情形如下:
IMAGE_IMPORT_DESCRIPTOR
|--------------------------|
|---------------------| OriginalFirstThunk |
| |---------------------------|
| | TimeDateStamp |
| |---------------------------|
| | ForwarderChain |
| |---------------------------|
| | Name |----> "USER32.DLL"
| |---------------------------|
| | FirstThunk |-----------------------------------------------------------|
| |---------------------------| |
| |
| Hint-name table IMAGE_IMPORT_BY_NAME import address table(IAT) |
| |--------------------------------| |-----------------------------| |------------------------------| |
|->| IMAGE_THUNK_DATA |-->| 44 | "GetMessage" | |ptr of GetMessage |<----|
|---------------------------------| |----|------------------------| |------------------------------|
| IMAGE_THUNK_DATA |--> | 72 | "LoadIcon" | | ptr of LoadIcon |
|---------------------------------| |----|------------------------| |------------------------------|
| ...... |-->| .. | ...... | | ...... |
|---------------------------------| |----|------------------------| |------------------------------|
| NULL |