基址重定位
使用工具
- OllyDbg 1.10原版,简称
OD
; OD
汉化
和插件
均来自互联网;- PE 查看工具为LoadPE,来自互联网;
WinHex
来自互联网;- 文中特殊数字均是
HEX
,为了书写方便采用DEC
;
基址重定位的概念
基址重定位是指 PE 文件无法加载到 ImageBase 所指的位置,而是被加载到其它地址时所发生的一系列的处理行为;
当链接器生成 PE 文件时,会假设这个文件在执行时会被装载到默认的基地址处,即 ImageBase 所指的地址处;
如果载入时将 ImageBase 默认的值作为基地址写入,则不需要重定位;
如果载入时 PE 文件被装载到虚拟内存的另一个地址中,此时,链接器登记的地址就是错误的,这时就需要用重定位表来调整;
在 PE 文件中,重地位表往往单独作为一块,用.reloc
表示;
对 EXE 文件来说,每个文件总是使用独立的虚拟地址空间,且创建好线程后,EXE 文件会首先加载到内存,所以 EXE 总是能够按照基址载入,这意味着 EXE 文件无需考虑重定位的问题(不需要重定位信息);
对 DLL 文件来说,因为多个 DLL 文件使用宿主 EXE 文件的地址空间,不能保证载入地址没有被其它 DLL 使用,所以 DLL 文件中必须包含重定位信息;
但是,Windows Vista 之后的版本引入了 ASLR 安全机制,每次运行 EXE 文件都会被加载到随机地址,这样大大增强了系统安全性;
ASLR 机制也适用于 DLL/SYS 文件,对于各 OS 的主要系统 DLL,根据不同版本会分别赋予不同的 ImageBase 地址;
ASLR
ASLR(Address Space Layout Randomization,地址空间布局随机化)是一种针对缓冲区溢出的安全保护技术,微软从 Windows Vista 开始采用该技术;
在以前的 OS 中,根据 OS 版本的不同,特定 DLL 总是会加载到固定地址,所以大部分安全漏洞(一般为内存缓冲区溢出)只出现在特定 OS、特定模块、特定版本中,漏洞利用代码(exploit code)会在特定内存地址中以硬编码形式编入,因此,微软采用 ASLR 技术来增加恶意用户编写漏洞利用代码的难度,从而降低利用 OS 安全漏洞破坏系统的风险;
借助 ASLR 技术,PE 文件每次加载到内存中的起始地址都会随机变化,并且每次运行程序时相应进程的栈以及堆的起始地址也会随机变化,也就是说,每次 EXE 文件运行时加载到进程内存的实际地址都不同,最初加载 DLL 文件时装载到内存中的实际地址也是不同的;
但是,并不是所有可执行文件都会自动应用 ASLR 技术,要应用 ASLR 技术,必须满足两个条件:
- OS 的内核版本必须为 6 以上,即系统版本不低于 Windows Vista(Kernel Version 6);
- 使用的编程工具要支持 /DYNAMICBASE 选项;
应用 ASLR 技术的 PE 文件会增加 1 个名为.reloc
的节区,它是编译时由编译器生成并保留在可执行文件中的,PE 文件被加载到内存时,该节区被用作重定位的参考,它不是 EXE 文件运行时的必须部分,可以从 PE 文件中删除,但是,由于 DLL 文件总是需要重定位,所以在 DLL 文件中不可将其删除;
应用 ASLR 技术的 PE 文件,其 IMAGE_FILE_HEADER(映像文件头)结构中的 Characteristics 字段将缺少 IMAGE_FILE_RELOCS_STRIPPED 标志(特征值为:0001),但 NumberOfSections 字段会增加 1;
应用 ASLR 技术的 PE 文件,其 IMAGE_OPTIONAL_HEADER(可选映像头)结构中的 DllCharacteristics 字段会设置 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE(特征值为:0040)标志;
若要删除 ASLR 功能,只需将 DllCharacteristics 字段设置的 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE(特征值为:0040)标志置 0 即可;
基址重定位的处理
PE 格式不参考外部 DLL 或模块中的其它区块,而是把文件中所有可能需要修正的地址放在一个数组中;
如果 PE 文件不在首选的地址载入,那么文件中的每个定位都需要被修正;
对加载器来说,它不需要知道关于地址使用的任何细节,只要知道有一系列的数据需要以某种一致的方式来修正就可以了;
基址重定位表
基址重定位表(Base Relocation Table)位于一个.reloc
区块内,基址重定位表地址位于 PE 头的 DataDirectory 数组的第 6 个元素(索引为 5),即 IMAGE_DIRECTORY_ENTRY_BASERELOC(IMAGE_NT_HEADERS/IMAGE_OPTIONAL_HEADER/IMAGE_DATA_DIRECTORY[5])条目;
基址重定位数据采用类似按页分割的方法组织,是由许多重定位块串接而成的;
每个块存放 4KB(1000)的重定位信息,每个重定位数据块的大小必须以 DWORD(4 字节)对齐;
基址重定位表是 IMAGE_BASE_RELOCATION 结构体数组;
IMAGE_BASE_RELOCATION 结构如下:
1 | IMAGE_BASE_RELOCATION STRUCT |
- VirtualAddress:双字,这组重定位数据的起始 RVA 地址,各重定位项的地址加上这个值才是该重定位项的完整 RVA 地址;
- SizeOfBlock:双字,当前重定位结构的大小,因为 VirtualAddress 和 SizeOfBlock 都是固定的 4 字节,所以这个值减去 8(字节)就是 TypeOffset 数组的大小;
- TypeOffset:重定位项数组,数组每项大小为 2 字节,共 16 位,分为高 4 位和低 12 位;
高 4 位代表重定位类型,低 12 位是重定位地址,它与 VirtualAddress 相加就是指向 PE 映像中需要修改的地址数据的指针;
常见的重定位类型:
类型 | winnt.h 中的预定义值 | 含义 |
---|---|---|
0 | IMAGE_REL_BASED_ABSOLUTE | 没有具体含义,只是为了让每个段 4 字节对齐 |
3 | IMAGE_REL_BASED_HIGHLOW | 重定位指向的整个地址都需要修正,实际上大部分情况下都是这样的 |
10(A) | IMAGE_REL_BASED_DIR64 | 出现在 64 位 PE 文件中,对指向的整个地址进行修正 |
在一组重定位结束的地方会出现类型是 IMAGE_REL_BASED_ABSOLUTE 的重定位,这些重定位什么都不做,只是用于填充,以便下一个 IMAGE_BASE_RELOCATION 结构按 4 字节分界线对齐;
对于 PE 文件来说,所有的基址重定位类型都是 IMAGE_REL_BASED_HIGHLOW;
对于 64 位的 PE+ 文件来说,重定位类型似乎总是 IMAGE_REL_BASED_DIR64;
尽管 64 位的 PE+ 文件页大小是 8KB,但基址重定位仍是 4KB 的块;
删除.reloc
节区
使用 WinHex 打开程序,定位到节区头:
节区头从文件偏移 270 处开始,到 297 处结束,节区大小为 1000,磁盘偏移为 C000;
选中节区头的全部数据,用 00 覆盖填充;
在 WinHex 中定位到节区起始偏移 C000,然后选中节区所有数据:
删除
.reloc
节区:这样
.reloc
节区就被物理删除了,同时 PE 文件的体积也会变小;由于删除了一个节区, IMAGE_FILE_HEADER 结构中表示节区数目的 NumberOfSections 字段就需要修正:
删除
.reloc
节区后,整个映像体积随之减少了相应的大小,IMAGE_OPTIONAL_HEADER 结构中的 SizeOfImage 字段同样需要修正:保存所有修改后,将程序导入 OD,观察内存映射窗口:
同时,使用 LoadPE 查看程序的区段信息:
.reloc
区段已经被剔除了;