PE格式解析-重定位表分析

vs中是否开启随机基址选项
这里写图片描述

一、随机基址与非随机基址的区别

1、非随机基址(固定基址)
在非随机基址的条件下,如果我们需要调用一个函数(函数地址为00408843),采取的汇编命令一般为JMP 00408843或 CALL 00408843

2、随机基址
开启随机基址以后,基址00400000将在每次应用程序运行的时候产生基址的变化,可能变成 00430000、004d0000…,此时编译器在编译程序的时候,无法直接将基址写成00400000,但是程序必须运行,此时就需要重定位的实现,使得程序在运行的时候,程序在内存中的地址随着基址的变化而变化。
Windows根据重定位表来解决随机基址的问题。在重定位表中,会记录很多的项目,PE文件加载器在加载应用程序到内存的时候,就会去查找是否有重定位项,如果有,就重新计算对应项目的基址的值,更新内存数据。也就是说,当你的应用程序刚加载到应用程序的时候,Windows的PE加载器就负责帮你将基址都进行更新。
例如:在基址不变的情况下是 0x00400000,此时打开了随机基址,使得程序在被PE加载器加载的时候,就将基址更新为0x00450000,那么上面提到的调用函数地址就变成了JMP 00458843与CALL 00458843.

二、对重定位表的分析

PE平面结构图:
这里写图片描述
测试例子:test.exe及完整PE平面结构图
1、重定位表(basereloc)
这里写图片描述
前四位为重定位表的RVA值,后四位为重定位表的大小(SIZE)
重定位表RVA:0001D000->文件偏移(00019800)
重定位表SIZE:0000018C

LoadPE查看重定位:
这里写图片描述
从文件偏移0x19800处截取0x18C个字节(重定位表的二进制信息)
这里写图片描述
注:以下为区段表信息
这里写图片描述

所有重定位数据里面会分成几个大的部分,每个部分有一个头部,占用8个字节,分别代表本部分的项目所对应的程序区段的起始RVA以及本部分的大小,剩下的每两个字节表示一个重定位项目。
本示例中,一共有两大部分,即.text与.rdata部分
这里写图片描述

2、接下来我们就来以第一大部分分析,即.text部分
例如重定位表的前八个字节:00 10 00 00 68 01 00 00
前四个字节的值:00001000 代表.text区段的起始RVA
后四个字节的值:00000168 代表该重定位部分的总大小0x168

接下来的每两个字节:例如 07 30=3007(高四位始终是3,Windows固定为3)真正有效的数据为0007,则根据起始RVA,计算出第一个需要重定位的项目位置为: 1000+0007=1007
第二个需要重定位的项目:1013
第三个需要重定位的项目:101E
第四个需要重定位的项目:102A

该部分需要重定位的总个数为 (0x168-0x8(前八个字节))/2=0xB0=176d

同理.rdata部分
.rdata区段的起始RVA:00002000
.rdata区段的大小:00000024
项目个数:(0x24-0x8)/2=0xE=14d
第一个项目:2108
第二个项目:2110
第三个项目:211C

3、重定位的流程
使用bpx MessageBoxW对下MessageBoxW断点
这里写图片描述
跳到断点处,即对话框处,对话框中显示的是全局变量szTips
这里写图片描述
向MessageBoxW压入szTips字符串处的汇编指令的地址为 0x010E12D0,容易得出0x010E0000为程序的基址,所以szTips的RVA值为0x12D0(文件偏移量为0x6D0)
这里写图片描述
在文件偏移0xx6D0处,可以看到汇编指令为68 80 33 40 00(68 表示push指令),和使用OD查看到的指令(加载到内存后)68 80330E01前3个字节相同,但是后面两个字节,一个为 40 00 ,一个为 0E 01,两个并不相同。这两个的差别就是由于重定位的实现导致的。

Windows是如何进行重定位的?
1)读取文件中的值,例如 80 33 40 00->0x00403380
2)读取文件中的基址(从Image_Optional_Header中的ImageBase获取)0x00400000
3)两个相减 0x00403380-0x00400000=0x3380
4)读取文件实际加载的基址 例如本示例中的 0x010E0000
5)更新重定位项 0x010E0000+0x3380=0x010E3380->80 33 0E 01
注:如果在随机基址下,直接载OD中修改重定位项将会出错。
例如:直接修改的文件中的值为 0x00909090
文件的基址为0x00400000,相减得 0x00509090,此时文件实际加载的基址为 0x01000000,那么此时重定位项在内存中的位置为 0x01509090,下次文件实际加载的基址为 0x02000000时,此时重定位项在内存中的位置为 0x02509090。可以发现,前后两次加载程序后的重定位的位置0x01509090与0x02509090位置并不相同,如果盲目修改,在再次加载程序的时候将会出错。

4、手动去掉示例程序的对话框内容(修改szTips值)
注:直接在OD中使用nop填充,会出现错误
通过上面我们知道,MesageBoxW重定位的参数RVA为0x12D1,文件偏移为0x6D1(开始位置为0x6D0后的68 push指令之后,即多一个字节0x6D1)
1)0x12D0可以看出,该部分属于0x1000,即.text部分,所以我们要找的重定位项目为 0x12D1-0x1000=0x2D1=>D1 32
2)在.text部分查找 D1 32
这里写图片描述
3)将D1 32填充为0,将会失败,因为重定位的项目个数在前8个字节中已经固定了,填充0后将会使得提前截断,导致重定位的项目个数减少,产生未知的错误(本例中会产生乱码)
即:
这里写图片描述
删除重定位项的方法为:将要删除的重定位项移到最后,再将随后的二进制填充为0。这样就避免了重定位项的缺少以及其他重定位项的无法实现问题
这里写图片描述
4)我们还可以更加的改进完美,可以修改.text重定位部分的大小为0x166,同时将.rdata部分向前移动两个字节.
示例
这里写图片描述
对于重定位表的大小还要进行修改,从0x18C改为0x18A
这里写图片描述

总结以上步骤:找出需要重定位的项目在哪个部分->分析出重定位的项目是哪个->在该部分里找到目标项目->对目标项目进行nop(其余往前移动,该部分同时修改该部分的大小->另存.exe文件

本文难免有所错误,如有问题欢迎留言

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值