首页
社区
课程
招聘
[原创]PE数据目录表解析
发表于: 2021-12-3 10:54 30110

[原创]PE数据目录表解析

2021-12-3 10:54
30110

接着学习PE结构解析。

写的过程中总结一下常用到的基础知识:

基地址(ImageBase):当PE文件通过Windows加载器载入内存后,内存中的版本称为模块,映射文件的起始地址称为模块句柄,可通过模块句柄访问内存中其他数据结构,这个内存起始地址就称为基地址。

虚拟地址(VA):在Windows系统中,PE文件被系统加载到内存后,每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址。

相对虚拟地址(RVA):可执行文件中,有许多地方需要指定内存中的地址。例如,应用全局变量时需要指定它的地址。为了避免在PE文件中出现绝对内存地址引入了相对虚拟地址,它就是在内存中相对于PE文件载入地址的偏移量。

它们之间的关系:虚拟地址(VA)  = 基地址(Image Base)+相对虚拟地址(RVA)

文件偏移地址(Offset):当PE文件存储在磁盘中时,某个数据的位置相对于文件头的偏移量称为文件偏移地址(File Offset)。文件偏移地址从PE文件的第一个字节开始计数,起始值为0。


数据目录表是PE中比较重要的一个部分,其也是一个结构。微软在Microsoft Virtual Studio在对其结构又定义。

image-20211201092655504


结构如下:

image-20211201093207857

用loadPE打开一个PE文件,点击目录,即可看到数据目录表,再点击每个表对应可以展开的按钮,即可看到相应参数对应的值,

比如点开输入表,可看到调用了哪些动态链接库,又在动态链接库里调用了哪些API:

image-20211201135159837

为了加深理解,下面对一些重要的表进行学习解析。

解析这些表之前,先写一个地址转换函数,就是将相对虚拟地址(RVA)转换为文件偏移地址(Offset)。

那么为什么要写这样一个函数呢?因为一些PE文件为了减小体积,磁盘对齐值不是一个内存页1000h,而是200h。当这类文件被映射到内存中后,同一数据相对于文件头的偏移量在内存中和磁盘文件是不同的,这样就出现了文件偏移地址和虚拟地址的转换问题。当然,那些磁盘对齐值与内存对齐值相同的区块,同一数据在磁盘文件中的偏移与在内存中的偏移相同,因此不需要转换。

如图,当文件被映射到内存中时,MS-DOS头,PE头和块表的偏移位置都没有改变,但是当区块被映射到内存中后,其偏移地址就发生了改变。

文件偏移地址(Offset)为add1 ,相对虚拟地址(RVA)为add2。它们直接相差了一个以0填充的空白区域,假设这个值为H,那么:

Offset =RVA -H

Offset =VA -ImageBase -H

image-20211202171918104

下面开始写代码

首先声明一个函数:

获取文件的缓存区:

原理比较简单:首先判断这个地址是否在PE头中,如果在,文件偏移和内存偏移相等,如果存在于文件的区段中,则利用以下公式: 内存偏移 - 该段起始的RVA(VirtualAddress) = 文件偏移 - 该段的PointerToRawData 内存偏移 = 该段起始的RVA(VirtualAddress) + (文件偏移 - 该段的PointerToRawData) 文件偏移 = 该段的PointerToRawData + (内存偏移 - 该段起始的RVA(VirtualAddress))

代码逻辑如下:

可执行文件使用来自其他DLL的代码或数据的动作称为输入。当PE文件在被载入时,Windows加载器的工作之一就是定位所有被数据的函数和数据,并让正在载入的文件可以使用那些地址。

导入函数就是被程序调用但其执行代码不在程序中的函数,这些函数在DLL文件中,当应用程序调用一个DLL的代码和数据时,它正被隐式地链接到DLL,这个过程由Windows加载器完成。另一种链接是显示链接,它是已经约定目标DLL已经被加载,然后寻找API的地址,一般是通过Loadlibrary 和GetprocAddress完成。

简而言之,导入表主要是PE文件从其他第三方库中导入API,以供本程序调用,结构如下:

image-20211201150034339

OriginalFirstThunkFirstThunk分别指向两个不同的IMAGE_THUNK_DATA结构的数组。这两个数组都以一个空的IMAGE_THUNK_DATA结构结尾。      一般情况下,导入表只需要关注OriginalFirstThunkFirstThunk这两个字段。

IMAGE_THUNK_DATA结构:

image-20211201150238009

那么要解析导入表,首先要定位到导入表:

通过PE扩展头里数据目录字段 + 导入表的宏定义,即可定位到导入表,

在Microsoft Virtual Studio中,在IMAGE_DIRECTORY_ENTRY_IMPORT处 ,ctrl +鼠标左键 即可跳转到该宏定义:

image-20211201150501425

然后就是填充结构,前面写了一个找数据表到文件头偏移的函数RvaToOffset,现在就用这个函数来找导入表到文件头的偏移:

然后就是遍历数据:

结果如下,可以看到是没有任何问题的:

image-20211201151544993

   导出表是PE文件为其他应用程序提供自身的一些变量、函数以及类,将其导出给第三方程序使用的一张清单,里面包含了可以导出的元素。

结构如下:

image-20211201164116518


从逻辑上来说,导出表由名称表、函数表与序号表组成。函数表和序号表必不可少,名称表则是可选的。序号表与名称表的作用是索引,找到真正需要的函数表,函数表中保存着被导出的函数的地址信息。

这里写一个有导出函数的测试dll:

image-20211202090309369

在loadPE中打开,打开导出表,可以看到有导出函数:

image-20211202090359466

现在就是和导入表一样,找导出表到文件头的偏移,然后手写代码获取相关信息:

逻辑和导入表类似,就不赘述了,直接给出代码:

结果如下,可以看到除了不美观以外,结果是没有任何问题的:

image-20211202090820059

当向程序的虚拟内存加载PE文件时,文件会被加载到ImageBase所指向的地址。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 9
支持
分享
最新回复 (2)
雪    币: 33
活跃值: (121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
受教。
2022-6-5 23:20
0
雪    币: 401
活跃值: (1186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
重定位表结束位置   这样判断似乎不妥  因为在win10  1809  上  的Ntosknrl.exe  上的结自己尾是 01 00 00 00 28 54 00 00
2023-5-21 09:47
0
游客
登录 | 注册 方可回帖
返回
//