PE文件格式学习(八):基址重定位表(BaseRelocationTable)

1.简介

基址重定位表位于数据目录表中的第六个,它位于安全表的后面。

这个表的作用用来索引那些需要重定位的数据。程序的加载基址一般是0x400000,但是程序导入了众多的DLL,这些DLL的默认加载基址一般都是0x10000000,这就出现了一个问题,当第一个DLL加载到0x10000000处,第二个DLL就无法再加载到0x10000000处了,这时系统会自动分配一个加载基址给DLL文件。这就是需要重定位的根本原因,因为DLL的载入基址变了,有些绝对地址上的变量又不再正确了,需要进行重新定位。

重定位表中的项就是用来索引到需要重定位的数据处,通过一个公式计算出修复后的地址,数据就可以正确被访问了。

2.重定位表的解析

通过数据目录表查看到重定位表的RVA是5000h,转换成offset为0x1a00,我们找到0x1a00处:

我们对比重定位表的结构体来分析:

typedef struct _IMAGE_BASE_RELOCATION
{
    DWORD VirtualAddress;
    DWORD SizeOfBlock;
    WORD TypeOffset[1];
}IMAGE_BASE_RELOCATION;

需要首先进行说明的是,重定位表的RVA指向的位置是一个数组,里面的元素都是IMAGE_BASE_RELOCATION,每个IMAGE_BASE_RELOCATION只负责4KB大小分页内的重定位信息。因此结构中的VirtualAddress总是0x1000的倍数。

VirtualAddress:需要重定位的数据的RVA,这个值需要加上后面的TypeOffset的低12位才是需要重定位数据的RVA,这么说可能不好理解,我们用本文使用到的程序作为例子,这是它的一部分汇编代码:

Offset        HEX          Assembly
10001000      55           push ebp
10001001      8BEC         mov ebp,esp
10001003      6AFE         push 0xFFFFFFFF
10001005      68 10220010  push 0x10002210

程序的ImageBase是0x10000000,VirtualAddress是0x1000,后面要提到的TypeOffset的低12位是0x006,有如下公式成立:

需要重定位的数据位置 = ImageBase + VirtualAddress + TypeOffset低12= 0x10000000 + 0x1000 + 0x006

这里计算得到的最终需要重定位的数据地址是0x10001006,我们注意看上面的代码,0x10001005处是push 0x10002210,也就是说push后面的地址0x10002210需要进行重定位。

假设有一个dll实际加载到了0x72ab0000,它的ImageBase却是0x10000000,系统会通过下面的公式进行重定位

重定位后地址 = (真实加载基址-默认加载基址) + 需要进行重定位的地址
					  = (0x72ab0000 - 0x10000000) + 0x10002210

SizeOfBlock:整个重定位表的大小,IMAGE_BASE_RELOCATION结构的总大小,对应上图中的0x118,实际上本文用到的程序有两个IMAGE_BASE_RELOCATION结构,它们的大小分别是0x118和0x24,合起来是0x13c,正好是数据目录表中标记的值。

TypeOffset:重定位的偏移,这个值加上IMAGE_BASE_RELOCATION中的VirtualAddress就是完整的RVA了,这个字段实际上并不属于IMAGE_BASE_RELOCATION结构体,但SizeOfBlock字段记录的大小包含了这个结构体,这个结构体通常有多个,按顺序进行排列,最后以0x0000作为结尾,结构体本身占2个字节,结构体如下:

struct
{
    WORD Offset:12; 
    WORD Type:4; 
}TypeOffset;

Offset:低12位,上面介绍过,它跟VirtualAddress相加就是完整的重定位RVA


Type:重定位信息的类型,有如下类型:
#define IMAGE_REL_BASED_ABSOLUTE              0
#define IMAGE_REL_BASED_HIGH                  1
#define IMAGE_REL_BASED_LOW                   2
#define IMAGE_REL_BASED_HIGHLOW               3
#define IMAGE_REL_BASED_HIGHADJ               4
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5    5
#define IMAGE_REL_BASED_RESERVED              6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7    7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8    8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9    9
#define IMAGE_REL_BASED_DIR64                 10
信息宏定义
0无重定位操作,用于4字节对齐IMAGE_REL_BASED_ABSOLUTE
1重定位指向位置的高2个字节需要被修正IMAGE_REL_BASED_HIGH
2重定位指向位置的低2个字节需要被修正IMAGE_REL_BASED_LOW
3重定位指向位置的全部4个字节需要被修正(绝大多数都是这种情况)IMAGE_REL_BASED_HIGHLOW
4需要两个TypeOffset配合完成索引IMAGE_REL_BASED_HIGHADJ
5IMAGE_REL_BASED_MACHINE_SPECIFIC_5
6保留IMAGE_REL_BASED_RESERVED
7IMAGE_REL_BASED_MACHINE_SPECIFIC_7
8IMAGE_REL_BASED_MACHINE_SPECIFIC_8
9IMAGE_REL_BASED_MACHINE_SPECIFIC_9
10重定位指向位置的8个字节需要被修正IMAGE_REL_BASED_DIR64

重定位表中记录的重定位信息的个数可以通过下面的公式进行计算:

重定位个数 = (SizeOfBlock - 8(IMAGE_BASE_RELOCATION的大小)) / 2(每个TypeOffset是2个字节)
总重定位个数 = 所有IMAGE_BASE_RELOCATION结构体相加后的重定位个数

因为数据目录表中记录的重定位表的大小是0x13c,而第一个IMAGE_BASE_RELOCATION只占用了0x118,所以应该还有其他的IMAGE_BASE_RELOCATION,通过0x1a00+0x118得到0x1b18,我们可以看到这个位置是另一个IMAGE_BASE_RELOCATION的起始,它的VirtualAddress是0x2000,它的SizeOfBlock是0x0024,通过将这两个IMAGE_BASE_RELOCATION的SizeOfBlock相加正好等于0x13c,通过计算第二个IMAGE_BASE_RELOCATION内的重定位个数是(0x24-8)/2=0xe,所以最终分析出来这个重定位表包括了两个IMAGE_BASE_RELOCATION,此程序一共有0x88+0xe个地方需要进行重定位修复。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值