基址重定位表

定义

PE重定位指如果PE文件加载时(一般是DLL,SYS动态加载)已经有别的DLL或SYS文件加载到它的ImageBase处,PE装载器会将其加载到其它未被占用的空间。PE装载器把它装载到其它位置的一系列行为就叫做PE重定位。(如果不进行这个操作,程序无法运行,内存地址引用错误)

要解决什么问题呢?
–即让DLL文件能加载到其它地方并可以被正确调用.

对于exe文件来说,exe每次都会加载到随机地址(ASLR机制)

操作原理

  • 在程序中查找硬编码的地址位置(如指令指向的绝对地址)
  • 读取值后,减去imagebase(VA->RVA)
  • 加上实际加载的地址(RVA->VA)
  • 注:这个是在加载时直接写入修改,而不是每次调用函数或指令时再计算

那么,该如何查找硬编码地址呢?

基址重定位表

基址重定位表记录了程序加载时需要修正的值的相关信息,包括修正地址的位置,需要修正的字节数,需要修正的地址类型等.在.reloc节中.位于PE头的DataDirectory数组的索引为5的元素指向了这个表.该表中每个记录都被称为一项(entry),每个项包含了需要修正的地址的详细信息.项以可变长度数组的形式存在重定位块中,每个重定位块是如下的IMAGE_BASE_RELOCATION结构体

1
2
3
4
5
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; // 需要重定位的数据的RVA
DWORD SizeOfBlock; // 重定位块的长度(包括TypeOffset)
//WORD TypeOffset[1]; // 重定位的偏移
} IMAGE_BASE_RELOCATION;
1
2
3
4
5
6
struct
{
WORD Offset:12; // 大小为12Bit的重定位偏移
WORD Type :4; // 大小为4Bit的重定位信息类型值
}TypeOffset; // 这个结构体是A1Pass总结的

每个IMAGE_BASE_RELOCATION结构体都表示一个重定位块,每块记录了0x1000页,记录方法是VirtualAddress表明基地址,每个TypeOffset的低12位表示偏移,基地址加偏移即是那个需要修改的地址.

其中SizeOfBlock的大小(块的长度)指明该重定位块中重定位项的个数

TypeOffset原则上不属于结构中,且元素个数 = (SizeOfBlock - 8 )/ 2,其中前4位的部分含义:

  • [IMAGE_REL_BASED_ABSOLUTE (0):使块按照32位对齐,位置为0]
  • [IMAGE_REL_BASED_HIGH (1):高16位必须应用于偏移量所指高字16位]
  • [IMAGE_REL_BASED_LOW (2):低16位必须应用于偏移量所指低字16位]
  • [IMAGE_REL_BASED_HIGHLOW (3):全部32位应用于所有32位]
  • [IMAGE_REL_BASED_HIGHADJ (4):需要32位,高16位位于偏移量,低16位位于下一个偏移量数组元素,组合为一个带符号数,加上32位的一个数,然后加上8000然后把高16位保存在偏移量的16位域内]。

数组的最后一项以NULL填充结尾.