节表数据
导入表
导入表查看
在 IMAGE_OPTIONAL_HEADER 结构体中 DataDirectory 字段,给出了数据目录结构体,定义如下
//
// Directory format.
//
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 实际上数据目录的 RVA
DWORD Size; // 数据目录项的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
VirtualAddress:PE 文件载入内存之后的 RVA(相对虚拟地址)。
Size:导入表大小
导入表的 FOA:地址转换看PE 结构的三种地址
导入表的结构
IMAGE_IMPORT_DESCRIPTOR 结构体定义如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
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;
IMAGE_IMPORT_DESCRIPTOR(汇编)
IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics dd ?
OriginalFirstThunk dd ?
ends
TimeDateStamp dd ?
ForwarderChain dd ?
Name1 dd ?
FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS
OriginalFirstThunk:保存了指向导入表函数名称(序号)的 RVA 表,这个表是个 IMAGE_THUNK_DATA 结构体。
Name:导入模块名称的 RVA。
FirstThunk:指向导入地址表的 RVA,在 PE 文件没有被装载前它的内容与 OriginalFirstThunk 指向相同的内容,装载内存之后,它的值发生变化,里面保存导入函数的实际地址。
IMAGE_THUNK_DATA 结构体,具有 32 位和 64 位,定义如下:
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
IMAGE_THUNK_DATA32(汇编)
IMAGE_THUNK_DATA32 STRUCT
union u1
ForwarderString dd ?
Function dd ?
Ordinal dd ?
AddressOfData dd ?
ends
IMAGE_THUNK_DATA32 ENDS
Oridinal:导入函数的序号,当 IMAGE_THUNK_DATA 的最到位为 1 时,该值有效。
AddressOfData:指向 IMAGE_IMPORT_BY_NAME 结构体的 RVA,当 IMAGE_THUNK_DATA 的最到位不为 1 时,该值有效。
通过这两个字段的解释可以明白,Oridinal 和 AddressOfData 本质上是一个值,但是在使用时取决于 IMAGE_THUNK_DATA 的最高位。当 IMAGE_THUNK_DATA 的最到位为 1 时,使用的是序号进行导入的函数,导入函数的序号是 Oridinal 的低 31 位;当最高位不为 1 时,说明导入函数是通过名称进行导入的,而 AddressOfData 保存了指向 IMAGE_IMPORT_BY_NAME 的 RVA。
IMAGE_IMPORT_BY_NAME 结构体的定义如下:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
IMAGE_IMPORT_BY_NAME(汇编)
IMAGE_IMPORT_BY_NAME STRUCT
Hint dw ?
Name1 db ?
IMAGE_IMPORT_BY_NAME ENDS
Hint:表示该函数在导出函数表中导出函数名称对应的序号,该值不是必须的。
Name:导入函数的函数名称。导入函数是一个以 ASCII 编码保存的字符串,并以 NULL 结尾。IMAGE_IMPORT_BY_NAME 中使用 NAME[1] 来定义该字段,表示该字段是一个只有 1 字节长度的字符串数组。在实际中函数名称不可能只有 1 个字节的长度,其实这是一种编程的技巧,通过数组越界来进行访问变长字符串的功能。
OriginalFirstThunk 和 FirstThunk 的区别
在 IMAGE_IMPORT_DESCRIPTOR 结构体中的 OriginalFirstThunk 和 FirstThunk 都指向了 IMAGE_THUNK_DATA 结构体,但是两者是有区别的。当 PE 文件在磁盘上时,两者都指向的 IMAGE_THUNK_DATA 结构体中保存的是相同的内容,而当文件被载入内存后,两者指向的 IMAGE_THUNK_DATA 结构体中保存的内容就不相同了。
PE 文件在磁盘上时,OriginalFirstThunk 指向的 IMAGE_THUNK_DATA 中保存的是导入函数的序号或者指向导入函数名称的 RVA,因此称为导入名称表。即 INT。FirstThunk 指向的 IMAGE_THUNK_DATA 中保存的也是导入函数的序号或者指向导入函数名称的 RVA。此时,它们在磁盘上是没有区别的。
当 PE 文件从磁盘装载入内存中后,OriginalFirstThunk 指向的 IMAGE_THUNK_DATA 中保存的仍然是导入函数的序号或者指向导入函数名称的 RVA。而 FirstThunk 指向的 IMAGE_THUNK_DATA 中则被 Windows 操作系统的 PE 装载器填充为导入函数的地址,因此这里被称为导入地址表,即 IAT。所以 FirstThunk 所指向的 IMAGE_THUNK_DATA 在内存中保存的是导入函数实际地址。
手动分析导入表
重定位表
重定位表查找同导入表一样,在数据目录索引为 5 的位置。
IMAGE_BASE_RELOCATION 结构体定义如下:
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
IMAGE_BASE_RELOCATION(汇编)
IMAGE_BASE_RELOCATION STRUCT
VirtualAddress dd ?
SizeOfBlock dd ?
IMAGE_BASE_RELOCATION ENDS
重定位表由多个 IMAGE_BASE_RELOCATION 组合而成。
VirtualAddress:重定位数据的起始 RVA 地址。
SizeOfBlock:当前区段重定位结构的大小。该大小不是 IMAGE_BASE_RELOCATION 结构体的大小,IMAGE_BASE_RELOCATION 结构体的大小是 8 字节,该字段的大小还包括重定位数据的大小,即 8 字节 + N*WORD。
TypeOffset:重定位数据的数组。每个 TypeOffset 都是 WORD 类型,占用 16 个字节。高 4 位表示该 TypeOffset 的类型,低 12 位表示“区段”内需要重定位的“偏移地址”,由低 12 位与 VirtualAddress 相加就是需要进行重定位的 RVA 地址。在 Win32 下,所有的重定位类型都是 IMAGE_REL_BASED_HIGHLOW。在 Winnt.h 头文件中,TypeOffset 是被注释掉的。
TypeOffset 取值
//
// Based relocation types.
//
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2
#define IMAGE_REL_BASED_HIGHLOW 3
#define IMAGE_REL_BASED_HIGHADJ 4
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5 5
#define IMAGE_REL_BASED_RESERVED 6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7 7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8 8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9 9
#define IMAGE_REL_BASED_DIR64 10