基址重定位表

PE重定位

向进程的虚拟内存加载PE文件(EXE/DLL/SYS)时,文件会被加载到PE头的ImageBase所指的地址处。若加载的是DLL ( SYS)文件,且在ImageBase位置处已经加载了其他DLL (SYS)文件,那么PE装载器就会将其加载到其他未被占用的空间。这就涉及PE文件重定位的问题,PE重定位是指PE文件无法加载到ImageBase所指位置,而是被加载到其他地址时发生的一系列的处理行为。

DLL/SYS

A.DLL被加载到TEST.EXE进程的10000000地址处。此后,B.DLL试图加载到相同地址(( 10000000 )时,PE装载器将B.DLL加载到另一个尚未被占用的地址(3CO00000)处。

1

EXE

创建好进程后,EXE文件会首先加载到内存,所以在EXE中无须考虑重定位的问题。但是Windows Vista之后的版本引入了ASLR安全机制,每次运行EXE文件都会被加载到随机地址,这样大大增强了系统安全性。

PE重定位的基本原理:

1、在应用程序中查找硬编码的地址位置;

2、读取值后,减去ImageBase(VA转换为RVA);

3、加上实际加载地址(RVA转换为VA)。

基址重定位表:

基址重定位表位于PE头的DataDirectory数组的第六个元素(索引为5),如图:

2

其地址为0002F000,查看一下:

3

如图罗列了硬编码地址的偏移。

基址重定位表是IMAGE_BASE_RELOCATION结构体数组,其中第一个成员为VirtualAddress如地址2F000的值,第二个成员为SizeOfBlock如地址2F004的值,第三个成员为TypeOffset数组如2F008地址以下的值,以注释的形式存在,表示该结构体之下会出现WORD类型的数组,并且该数组元素的值就是硬编码在程序中的地址偏移。

基址重定位表的分析方法:

如上图,VirtualAddress的值为1000,SizeOfBlock的值为150,即TypeOffset数组的基准地址为RVA 1000,块的总大小为150。其中,TypeOffset值为2字节(16位)大小,由4位的Type和12位的Offset组成,比如TypeOffset数组的第一个元素值为3420,其中高4位的值“3”(IMAGE_REL_BASED_HIGHLOW)为Type,一般为PE文件,而64位的PE文件常见值为“A”,低12位是真正的位移、其值为420,下面公式计算程序中硬编码地址的偏移:

VirtualAddress + Offset = 1000 + 420 = 1420(RVA)

查看1420偏移处(程序被加载到00330000地址处、故RVA 1420即VA 00331420)是否存在要执行PE重定位操作的硬编码地址:

4

运行notepad.exe程序,假设其被加载到00330000。

1、查找程序中硬编码地址的位置:

使用PEView查看RVAV 1420处的内容:

可以看到,RVAV 1420处保存着程序硬编码地址值010010C4。

2、读取值后,减去ImageBase值(VA转换为RVA):

010010C4 - 01000000 = 10C4

3、加上实际加载地址(RVA转换为VA):

10C4 + 00330000 = 003310C4

可以看到,和上图硬编码地址一致。

对于程序内硬编码的地址,PE装载器都会做如上处理,然后将值覆盖到同一位置。若TypeOffset值为0,则表明一个IMAGE_BASE_RELOCATION结构体结束。