关于导入表:
可执行文件使用来自于其他dll的代码或数据时,称为输入。当PE文件装入时,
Windows加载器的工作之一就是定位所有被输入的函数和数据,并且让正在被装入的
PE文件可以使用那些地址。这个过程是通过PE文件的输入表(Import Tab 也称之为
导入表)完成的,输入表中保存的是函数名和其驻留的dll名等,动态连接所需输信息,
输入表在
软件外壳
技术上的地位十分重要,因此在研究外壳的
技术时一定要掌握这部分
知识。
学习输入表要先从整个PE文件说起,
-------------*-------------------------------------------------*
|
DOS Header(IMAGE_
DOS_HEADER) | -->64 Byte
DOS头部 --------------------------------------------------
|
DOS Stub | -->112 Byte
-------------*-------------------------------------------------*
| "PE"00 (Signature) | -->4 Byte
-------------------------------------------------
| IMAGE_FILE_HEADER | -->20 Byte
PE文件头--------------------------------------------------
| IMAGE_OPTIONAL_HEADER32 | -->96 Byte
---------------------------------------------------
| 数据目录表 | -->128 Byte
-------------*--------------------------------------------------*
| IMAGE_SECTION_HEADER | -->40 Byte
---------------------------------------------------
块表 | IMAGE_SECTION_HEADER | -->40 Byte
--------------------------------------------------
| IMAGE_SECTION_HEADER | -->40 Byte
-------------*--------------------------------------------------*
|.text | -->512 Byte
---------------------------------------------------
块 |.rdata | -->512 Byte
---------------------------------------------------
|.data | -->512 Byte
-------------*-------------------------------------------------*
| COFF行号 | -->NULL
---------------------------------------------------
调试信息| COFF符号表 | -->NULL
---------------------------------------------------
| Code View 调试信息 | -->NULL
-------------*--------------------------------------------------*
--------->>>摘自
互联网(方舟子把我们搞的人心惶惶的,我等菜鸟写点东西都心惊胆战)上面就是一个简单的PE结构图(附件里我会附带一个比较详细的PE结构图供菜菜们娱乐),
我们来找出和输入表有关的区段和数据结构以及他们在PE文件中的位置。
1.IMAGE_NT_HEADER->IMAGE_OPTIONAL_HEADER32->第104个字节(也就是数据目录表的IMAGE_DIRECTORY_ENTRY_IMPORT) :struct _IMAGE_DATA_DIRECTORY{D
word VirtualAdress; D
word Size;};
该结构第一个成员是指向.idata区段首地址。
2.节表 .idata
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
D
word PhysicalAddress;
D
word VirtualSize;
} Misc;
D
word VirtualAddress;
D
word SizeOfRawData;
D
word Po
interToRawData;
D
word PointerToRelocations;
D
word PointerToLinenumbers;
word NumberOfRelocations;
word NumberOfLinenumbers;
D
word Characteristics;
};
第五个成员表示该区段在文件中的起始地址。
3. .idata区段中
参考附件中的图片。
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
D
word Characteristics; // 0 for terminating null import descriptor
D
word OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
};
D
word TimeDateStamp; // 0 if not bound,
// -1 if bound, and real datetime stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
D
word ForwarderChain; // -1 if no forwarders
D
word Name;
D
word FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
//传说中的IID结构。
这个结构很重要,我们一个一个成员来分析
1)指向输入名称表(简称INT)的RVA,INT是一个IMAGE_THUNK_DATA结构数组,数组中的每一个IMAGE_THUNK_DATA结构指向IMAGE_IMPORT_BY_NAME结构,数组最后一个内容是内容为0的IMAGE_THUNK_DATA。
typedef struct _IMAGE_IMPORT_BY_NAME {
word Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
成员1:
word Hint;指示本函数在其所驻留dll的输入表中的序号。该域被PE装载器
用来在DLL的输出表里快速查询函数。该值不是必须的,一些链接器将此值设为0.
成员2:
BYTE Name[1];含有输入函数的函数名,函数名是一个ASCII码字符串
2)32位时间标志。
3)这个是第一个被转向的API的索引,一般为0。
4)DLL的名字的指针,是一个以00结尾的ASCII字符的RVA地址,例如”kernel32.dll”
5)包含指向输入地址表(IAT)的RVA。IAT是一个IMAGE_THUNK_DATA数组。
成员1同成员5非常相似,他们指向两个本质上相同的数组IMAGE_THUNK_DATA。
以上便是PE中和输入表相关的数据结构