逆向工程核心原理-第十六章-基址重定位表
16.1 PE重定位
向进程的虚拟内存(记住是虚拟内存, 这里比较容易和CSAPP的内容搞混)时,不同文件有不同的加载方式(不同的ImageBase加载方式):
- DLL: 若加载的ImageBase处已有其他DLL, PE装载器就会让其换个位置(ImageBase)
- EXE: EXE无需考虑重定位的问题, 会有ASLR机制让每次虚拟基址都不同.
下面更详细介绍这个机制
16.2.1 DLL/SYS
下面是DLL加载的实例
A.DLL被加载到10000000地址, 此后B.DLL想加载到10000000时, 由于已经被A.DLL占位了, 所以被加载到了3C000000, 这个重定位跟链接时重定位不一样, 不要搞混了!!!
16.1.2 EXE
有ASLR机制, 使得每次加载的ImageBase不一样
我们可以使用OD验证, 使用Alt + E 查看可执行模块
16.2 PE重定位时执行的操作
我们先来陈述一下PE重定位的定义: 使硬编码在程序中的内存地址随当前加载地址变化而改变的处理过程就是PE重定位. 下面是一个实例:
- notepad的ImageBase为1000000虚拟地址处
- 在ASLR机制作用下, 程序被加载到了28000虚拟地址处
当无法加载到ImageBase(正如它的定义为优先加载地址, 只是优先, 不是绝对)处, 且未PE重定位, 该程序就会加载失败.
补充
硬编码(注意主语, 是硬编码, 如果是相对虚拟地址RVA就不需要了, 因为不论怎么变化加载地址, 相对的地址时不变的)的地址先(注意这个词)以ImageBase为基准, 在加载的瞬间, 会根据从定位后的位置重新定义这个硬编码.
16.3 PE重定位操作原理
原理步骤:
- 在应用程序中查找硬编码的地址
- 读取值后, 减去ImageBase(VA → RVA, 从绝对变成相对)
- 加上实际加载地址(RVA → VA, 从相对又变回绝对)
其中最关键的是查找硬编码的地址, 查找硬编码的过程就是通过Relocation Tabel(重定位表, 记录了硬编码地址偏移位置的列表)查找.
16.3.1 基址重定位表
基址重定位表的地址位于PE头 → NT头 → 可选头 → DataDirectory数组的元素[5].
16.3.2 IMAGE_BASE_RELOCATION结构体
基址重定位表就是IMAGE_BASE_RELOCATION结构体数组
1 | typedef struct _IMAGE_BASE_RELOCATION { |
IMAGE_BASE_RELOCATION:
- 第一个成员VirtualAddress为一个基准地址, 表示的是一个块, 后面的TypeOffset为这个块上的偏移
- 第二个成员SizeOfBlock是记录是指重定位块(下面的那个数组所占的大小)的大小
- 最后一线TypeOffset不是成员, 而是一个注释, 表示下面会有一个WORD的数组, 元素就是偏移
起始就是一个结构体跟一个WORD数组绑定起来, 然后二者一起表示重定位地址的信息
16.3.3 基址重定位表的分析方法
假设基址VirtualAddress值为1000, TypeOffset为3420
解析方法:
- TypeOffset的高四位为: 3, 表示的是类型, 32位PE中一般为3, 64位PE该值为A
- 低12位也就是: 420为真正的偏移, 可以知道重定位的RVA(1420) = VirtualAddress(1000) + Offset(420)
16.3.4 练习
本题假设ImageBase = 1000000, 但是实际加载虚拟地址 = AF0000
1. 查找程序中硬编码的位置
- 首先就是通过PE头中的可选头中的DataDirectory[5]来得到基址重定位表
- 查找到了内容, VirtualAddress = 1000, Offset的第十二位 = 420. 所以得到了RVA = 1420
- 这个1420就是我们的重定位的地址, 我们查找1420地址处得到了硬编码 = 10010C4
2. 读取值减去ImageBase值(VA → RVA, 绝对变相对地址)
10010C4 - 1000000 = 10C4
3. 加上实际加载地址(RVA → VA, 相对变绝对地址)
10C4 + AF0000 = AF10C4