文章目录
- 本篇内容介绍
- 导出表 IMAGE_EXPORT_DIRECTORY
- 导入表 IMAGE_IMPORT_DESCRIPTOR
- 资源 IMAGE_RESOURCE_DIRECTORY
本篇内容介绍
上节文章记录了PE文件的结构概述,我们知道了PE头中第三个字段IMAGE_OPTIONAL_HEADER32
结构体中变量DataDirectory
数组中的数据类型为IMAGE_DATA_DIRECTORY
,定义如下,而sdk中定义了其指向的一些不同类型的成员信息。本篇文章就记录一些比较重要的成员的信息。
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //数据块起始RVA地址
DWORD Size; //数据块大小
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
补充: 这个数组大小为16,sdk定义了15个(值为0~14)类型信息,剩下一个预留保持全零。
导出表 IMAGE_EXPORT_DIRECTORY
导出表是PE文件为其他应用程序提供API的一种函数示例导出方式。Windows下存在导出表的可执行文件以指定自身的一些变量、函数以及类,并将其导出,以便提供给其他程序使用。
导出表结构为IMAGE_EXPORT_DIRECTORY
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用,总为0
DWORD TimeDateStamp; // 文件创建时间戳
WORD MajorVersion; // 总为0
WORD MinorVersion; // 总为0
DWORD Name; // 指向一个代表此dll名字的ASCII字符串的RVA
DWORD Base; // 函数的起始序号
DWORD NumberOfFunctions; // 导出函数的总数
DWORD NumberOfNames; // 以名称方式导出的函数的总数
DWORD AddressOfFunctions; // 指向输出函数地址的RVA,是以 4 字节为一个单位的数组元素,每个元素代表函数入口
DWORD AddressOfNames; // 指向输出函数名字的RVA,是以 4 字节为一个单位的数组元素,每个元素代表一个指向字符串的 RVA
DWORD AddressOfNameOrdinals; // 指向输出函数序号的RVA,是以 2 字节为一个单位的数组元素,每个元素代表对应名字在 AddressOfFunctions 中的序号
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
注意: 文件中保存的序号并不是调用函数时使用的,平时调用函数使用的序号减去Base(序号基数)的值才能得到文件中保存的序号。也将文件中保存的序号称为原始序号,而将调用函数时使用的序号称为调用序号。
另外从逻辑上讲,导出表由3部分构成,分别是名称表、函数表与序号表,其中函数表与序号表是必须要有的,而名称表则是可选的。序号表与名称表的作用就是索引,引导调用者找到真正需要的函数表,而函数表中保存的就是这个被导出的函数地址信息。
示例:分析一个dll文件的内容
用LordPE查看区段信息。
01edit中找到导入表的位置,0x00002570是导入表的RVA,0x00000089是其大小。
注意: 这里的0x00000089大小和导出表结构体40字节大小貌似没有关系,这个大小到底指的是什么意思以后再回来探索。如下图为DataDirectory数组内容
导出表在文件中的偏移=0x00002570-0x00002000+0x00000E00=0x1370,
找到0x1370位置数据跟IMAGE_EXPORT_DIRECTORY
对应
结构体IMAGE_EXPORT_DIRECTORY中的变量Name
0x000025C0 --> 文件偏移位置0x000025C0-0x00002000+0x00000E00=0x13C0 --> PEDemo.dll
结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfFunctions(函数入口地址),按照上面的信息断定有4个导出函数
0x00002598 --> 0x00002598-0x00002000+0x00000E00=0x1398
结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfNames
0x000025a8 --> 0x000025A8-0x00002000+0x00000E00=0x13a8
查看0x000025cb-0x00002000+0x00000e00=0x13cb,可以看到导出的函数名
结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfNameOrdinals
0x000025b8 --> 0x000025b8-0x00002000+0x00000E00=0x13b8
导出表结构
导入表 IMAGE_IMPORT_DESCRIPTOR
导入表本身仅相当于一个引导者的角色,并不能完成PE文件中的整个导入工作,只负责引导系统找到真正保存有导入信息的其他两个结构,这两个结构分别为IMAGE_THUNK_DATA
与 IMAGE_IMPORT_BY_NAME
。
IMAGE_IMPORT_DESCRIPTOR结构的个数是由导入映像文件的个数决定的,需要从多少个映像文件中导入函数,就要有多少个结构,最后以一个空的 IMAGE_IMPORT_DESCRIPTOR结构结束。
IMAGE_IMPORT_DESCRIPTOR结构定义如下
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // 包含指向INT(输入名称表)RVA 的结构数组
};
DWORD TimeDateStamp; //时间标识
DWORD ForwarderChain; //第一个被转向的API索引
DWORD Name; //指向被导入的DLL名称
DWORD FirstThunk; //指向输入地址表(IAT)RVA,IAT是一个IMAGE_THUNK_DATA结构的数组
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
OriginalFirstThunk指向导入名称表的数组,数组元素为IMAGE_THUNK_DATA。数组以空的结构体结束。
一般情况下,数组中的每个元素会再指向IMAGE_IMPORT_BY_NAME结构体。
IMAGE_IMPORT_BY_NAME结构以一个结构体数组形式存在,并以一个空的结构体结束。
IMAGE_THUNK_DATA定义如下
{
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData; //指向IMAGE_IMPORT_BY_NAME结构,当以上三个值都无效时,此值有效。
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 *PIMAGE_THUNK_DATA32;
IMAGE_IMPORT_BY_NAME结构定义如下
typedef struct _IMAGE_IMPORT_BY_NAME {
USHORT Hint; //导入函数的序号
UCHAR Name[1]; //导入函数的名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
导入表结构
资源 IMAGE_RESOURCE_DIRECTORY
资源在PE文件中是以目录结构的形式存在的,一般情况下这个目录分为3层,从根目录开始分别为资源类型、目录资源ID与资源代码页。
这3层目录结构都是由一个 IMAGE_RESOURCE_DIRECTORY结构为头部的,并且在其后面跟着一个IMAGE_RESOURCE _DIRECTORY_ENTRY结构数组。
IMAGE_RESOURCE_DIRECTORY结构主要负责指出后面结构数组的成员个数,而后面结构数组的每个成员则分别指向下一层目录结构(或资源数据)。
IMAGE_RESOURCE_DIRECTORY定义如下
{
DWORD Characteristics; //资源属性标识
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries; //资源名称个数
WORD NumberOfIdEntries; //资源id个数
} IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;
IMAGE_RESOURCE_DIRECTORY_ENTRY定义如下
{
__C89_NAMELESS union {
__C89_NAMELESS struct {
DWORD NameOffset:31; //资源名偏移(指向一个结构体)
DWORD NameIsString:1; //值为1时,NameOffset指向IMAGE_RESOURCE_DIR_STRING_U结构体,其中保存资源名称
}DUMMYSTRUCTNAME;
DWORD Name; //位于第一层目录时保存资源类型的值,位于第三层目录时保存资源语言区域类型值
WORD Id; //资源的id
}DUMMYUNIONNAME;
__C89_NAMELESS union {
DWORD OffsetToData; //数据偏移RAV
__C89_NAMELESS struct {
DWORD OffsetToDirectory:31; //指出下一层子目录相对资源目录起始地址偏移
DWORD DataIsDirectory:1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;
在经过三层目录的索引后,最后会到达一个 IMAGE_RESOURCE_DATA_ENTRY结构中,这个结构指引到资源数据。
定义如下
{
DWORD OffsetToData; //资源数据RAV指针
DWORD Size; //资源数据大小
DWORD CodePage; //资源代码页信息
DWORD Reserved; //保留,恒为0
} IMAGE_RESOURCE_DATA_ENTRY,*PIMAGE_RESOURCE_DATA_ENTRY;
资源结构图