PE文件结构2

本文详细介绍了PE文件中的输入表、输出表以及资源结构,包括它们的组成、作用和数据目录表中的指向。同时涵盖了重定位表和重定位类型的解释,帮助理解PE文件加载过程中的地址调整机制。
摘要由CSDN通过智能技术生成

1.输入表

PE文件中存在一个叫做输入表的结构,这个表保存了PE文件的所有输入信息,包括输入了哪个DLL文件的哪个函数、函数是以名称还是以序号的方式输入、被输入函数在内存中的地址等。数据目录表的第二个成员指向输入表,输入表是一个类型为IMAGE_IMPORT_DESCRIPTOR(简称IID)的数组,每一个DLL都对应有一个IID结构,由于没有字段指明这个IID数组的长度,所以通过在末尾添加一个空的IID结构来表示数组的结束,IID结构体在WinNT.h头文件中的定义如下所示:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;

关于IID结构体中各个成员的含义介绍如下:
1. OriginalFirstThunk,RVA,指向输入名称表(简称INT),INT是一个类型为IMAGE_THUNK_DATA结构的数组,同样的,通过在数组末尾附加一个空的IMAGE_THUNK_DATA结构来表示数组的结束,每一个输入的函数都有一个对应的IMAGE_THUNK_DATA结构;
2. TimeDateStamp,DLL的时间日期标志,通常忽略;
3. ForwarderChain,通常忽略,不作讲解;
4. Name,RVA,指向DLL的名字,如“User32.dll”;
5. FirstThunk,RVA,指向输入地址表(简称IAT),IAT也是一个类型为IMAGE_THUNK_DATA结构的数组,同样的,通过在数组末尾附加一个空的IMAGE_THUNK_DATA结构来表示数组的结束,每一个输入的函数都有一个对应的IMAGE_THUNK_DATA结构;
需要注意的是,OriginalFirstThunk与FirstThunk极其相似,前者指向INT,后者指向IAT,其中INT和IAT都是类型为IMAGE_THUNK_DATA的数组。
输入表的结构如下图所示:

IAT被PE装载器填充后的输入表结构如下图所示。

2.输出表

DLL文件在将函数暴露给其他模块调用时,需要将相关的信息存放到输出表中。例如User32.dll的输出表中存在MessageBoxA,表明其他程序可以调用User32.dll的MessageBoxA函数。在INT和IAT的区别中,提到IAT会被PE装载器填充为函数的实际地址,这个操作就是根据对应DLL的输出表的解析来实现的。
EXE文件一般不存在输出表,而大部分的DLL文件中都存在输出表,当然这也不是绝对的。
输出表中的信息包含了函数的输出名称、输出序号等相关信息。数据目录表的第一个成员指向输出表,输出表是一个类型为IMAGE_EXPORT_DIRECTORY(简称IED)的结构
输出表的结构如下图所示:

3.资源结构

资源是PE文件中非常重要的组成部分,几乎所有的PE文件都包含有资源数据,不过与输入表和输出表相比,资源的组织方式就复杂多了。资源数据通常存放在PE文件的.rsrc节区中,不过正确找到资源段的方式是通过数据目录表的第三项来获取。

常用工具:eXeScope就是一款PE文件资源编辑工具,它可以提取、删除、修改、替换PE文件的资源数据。类似eXeScope的资源编辑工具还有ResScope、Resource Hacker等。

资源结构采用类似文件系统的分层目录结构,通常包含三层目录。数据目录表的第三个成员指向资源表,其类型为IMAGE_RESOURCE_DIRECTORY,该结构占用16字节。

主要关注最后两个字段:NumberOfNamedEntries与NumberOfIdEntries(都是WORD类型),两者加起来就是资源表根目录下总的资源条目的数量。
在IMAGE_RESOURCE_DIRECTORY之后,紧跟着NumberOfNamedEntries + NumberOfIdEntries个IMAGE_RESOURCE_DIRECTORY_ENTRY(简称IRDE)结构类型的数据。

IMAGE_RESOURCE_DIRECTORY_ENTRY结构体在WinNT.h头文件中的定义如下所示:
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
DWORD Name;
DWORD OffsetToData;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

该结构体占用8个字节,结构体的成员的介绍如下:
1. Name,这个字段拥有多个不同的含义:当IRDE位于第一层目录时,Name表示资源的类型;当IRDE位于第二层目录时,Name表示资源的名称;当IRDE位于第三层目录时,Name表示代码页的编号。此外,还需要先判断Name的最高位是0还是1,如果是0则表示当做一个值来使用,如果是1,则表示低31位当做指针来使用;
2. OffsetToData,指针;当最高位为1时,表示低位数据指向下一层目录;当最高位为0时,表示没有下一层目录,低位数据指向一个IMAGE_RESOURCE_DATA_ENTRY结构。
注意,当Name和OffsetToData当做指针使用时,其值并不是RVA,而是表示相对于资源区块起始位置的偏移值。

4.重定位表结构

在介绍PE文件的OptionalHeader的时候,其中有一个成员ImageBase,它指定了程序的首选装载基地址,也就是说,如果条件允许的话,PE文件被加载到内存时将会被加载到这个基地址。而如果这个地址已经被其他模块所占用了的话,那么这个PE文件就不能再放到这个位置了,它需要在进程空间的其他地方找到一个合适的位置来装载,因为加载的基地址发生了变化,该PE文件引用的一些全局变量或者函数都需要进行重定位处理。

需要进行重定位的数据通常都会放在PE文件名为.reloc的节区中,数据目录表的第六个成员指向重定位表。重定位表存放着许多类型为IMAGE_BASE_RELOCATION的结构,该结构体在WinNT.h头文件中的定义如下:
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;

该结构各个成员的介绍如下:
1. VirtualAddress,RVA,指向当前这一组重定位数据开始的地址,各个重定项的值加上VirtualAddress计算出实际需要重定位操作的RVA值;
2. SizeOfBlock,整个IMAGE_BASE_RELOCATION结构的大小,注意因为后面的TypeOffset数组的大小不固定,所以需要通过SizeOfBlock来指定整个结构的大小;
3. TypeOffset,需要重定位的项,数组的项数可以通过(SizeOfBlock - 8)/2计算出来,数组每一项的大小占用2个字节,一共16位,其中高4位表示重定位的类型,低12位表示重定位的地址,低12位加上VirtualAddress得到实际上需要进行重定位操作的数据的RVA值;

常见的重定位类型有:
1. IMAGE_REL_BASED_ABSOLUTE,值为0,没有具体意义,用于对齐使用;
2. IMAGE_REL_BASED_HIGHLOW,值为3,表示指向的地址需要进行重定位修正;
3. IMAGE_REL_BASED_DIR64,值为10,出现在64位的PE文件中,对指向的整个地址修正;

重定位表的结构如下图所示:

实际装载地址 + RVA =新地址,
RVA = 原始地址 - 首选装载地址,
新地址 = 实际装载地址 - 首选装载地址 + 原始地址


 


 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值