菜鸟也学输入表
关于导入表:
可执行文件使用来自于其他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{DWORD VirtualAdress; DWORD Size;};
该结构第一个成员是指向.idata区段首地址。
2.节表 .idata
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
};
第五个成员表示该区段在文件中的起始地址。
3. .idata区段中

参考附件中的图片。
? 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 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中和输入表相关的数据结构



