0
点赞
收藏
分享

微信扫一扫

[系统安全] PE文件格式详解2


文章目录

  • ​​本篇内容介绍​​
  • ​​导出表 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查看区段信息。

[系统安全] PE文件格式详解2_安全


01edit中找到导入表的位置,0x00002570是导入表的RVA,0x00000089是其大小。

注意: 这里的0x00000089大小和导出表结构体40字节大小貌似没有关系,这个大小到底指的是什么意思以后再回来探索。如下图为DataDirectory数组内容

[系统安全] PE文件格式详解2_数据_02


导出表在文件中的偏移=0x00002570-0x00002000+0x00000E00=0x1370,

找到0x1370位置数据跟​​IMAGE_EXPORT_DIRECTORY​​对应

[系统安全] PE文件格式详解2_安全_03


结构体IMAGE_EXPORT_DIRECTORY中的变量Name

0x000025C0 --> 文件偏移位置0x000025C0-0x00002000+0x00000E00=0x13C0 --> PEDemo.dll

[系统安全] PE文件格式详解2_数组_04


结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfFunctions(函数入口地址),按照上面的信息断定有4个导出函数

0x00002598 --> 0x00002598-0x00002000+0x00000E00=0x1398

[系统安全] PE文件格式详解2_数据_05


结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfNames

0x000025a8 --> 0x000025A8-0x00002000+0x00000E00=0x13a8

[系统安全] PE文件格式详解2_数组_06


查看0x000025cb-0x00002000+0x00000e00=0x13cb,可以看到导出的函数名

[系统安全] PE文件格式详解2_系统安全_07

结构体IMAGE_EXPORT_DIRECTORY中的变量AddressOfNameOrdinals

0x000025b8 --> 0x000025b8-0x00002000+0x00000E00=0x13b8

[系统安全] PE文件格式详解2_导出表_08

导出表结构

[系统安全] PE文件格式详解2_安全_09

导入表 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;

导入表结构

[系统安全] PE文件格式详解2_数组_10

资源 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;

资源结构图

[系统安全] PE文件格式详解2_安全_11


举报

相关推荐

0 条评论