PE文件基址重定位

来源:互联网 发布:外汇交易大师软件 编辑:程序博客网 时间:2024/04/28 13:17

PE文件基址重定位(Base Relocation),程序编译时每个模块有一个优先加载地址ImageBase,这个值是连接器给出的,因此连接器生成的指令中的地址是在假设模块被加载到ImageBase前提之下生成的,链接器把code段和data段的相关地址都写入到PE文件中,如果程序没有被加载到imageBase段,链接器所登记的地址就是错误的会影响code和data段数据的定位。 此时就需要用到重定位表来调整, PE文件往往单独分为一块,用'reloc'表示。

假设一个可执行文件,基址是0x400000,在这个image偏移0x1234处是一个指针,指向一个字符串,字符串始于实际地址0x404002处,所以指针应该是0x404002(RVA应该是4002) 。加载器决定把他加载到0x600000处,连接器假设的地址和实际的地址之差成为delta(差值),上例delta为0x200000 , 那么字符串未知就应该为0x604002.为了让windows有能力这样调整,可执行文件中有许多'基址重定位数据”本例中的装载器应把0x200000加给0x404002,并将0x604002 写回原处。

 

下面我们来介绍一下【基址重定位项】,加载器就是利用它来知道模块是否按预期的位置加载,哪些指令是需要修改的。基址重定位表位于一个.reloc的区块内,但找到他们的正确方式是通过数据目录表的IMAGE_DIRECTORY_ENTRY_BASERELOC条目

因此我们研究的重点将是【基址重定位项】,首先加载器也是通过数据目录来定位【基址重定位项】,【基址重定位项】被包装为一系列连续区段,每一个区段来描述一个4K PAGE(也就是一页)的重定位信息,长短不一(每页中需要重定位的指令数目也不一样),

它们以一个IMAGE_BASE_RELOCATION结构作为开始,格式如下:

DWORD VirtualAddress重定位内存页的起始RVA,每一个【基址重定位项】的偏移位置(即下面的TypeOffset的低12位,它是指令相对于它所在页的第一条指令的偏移),必须加上重定位页的RVA才是一个真正的RVA,指向【基址重定位项】

DWORD SizeOfBlock:结构大小,在加上跟着后面的所有【基址重定位项】(都是WORDS),为了决定【基址重定位项】的个数,=(SizeOfBlock-Sizeof(IMAGE_BASE_RELOCATION)(8个字节))/2(WORD占2个字节);例如此值为44,则个数为44-8/2=18.

总结:IMAGE_BASE_RELOCATION包含两个成员,一个是VirtualAddress,包含自身内存页起始位置RVA,另一个SizeOfBlock,表明这个结构有多大。

WORD TypeOfOffset

这并不是单独一个WORD,而是一个WORDS组,数组元素个数可以有上一个式子计算得到,每一个WORD的最底部12位代表【基址重定位项】的位置偏移,但必须在加上IMAGE_BASE_RELOCATION表头中VirtualAddress,最高4位是【基址重定位项】的型态。对于在Intel CPU中的PE文件,你将看到两种状态,0(IMAGE_REL_BASED_ABSOLUTE)此一【基址重定位数据项】无意义,只是用来充数而已,使所有【基址重定位项】总数为DWORD倍数。3(IMAGE_REL_BASED_HIGNLOW):把delta值加到欲计算的RVA值,另外还有其他状态在WINNT.h中,它们大部分是给i386以外的的CPU使用。

下面给出一些【基址重定位项】,请注意其中的RVA已经被IMAGE_BASE_RELOCATION中的VirtualAddress校正过。

VirtualAddress:00001000  Size0000012C

000001032 HIGHLOW

00000106D HIGHLOW

0000010AF  HIGNLOW

......

VirtualAddress:00002000 Size0000009C

000020A6  HIGHLOW

00002110  HIGHLOW

00002136  HIGHLOW

00002156  HIGHLOW

........

VirtualAddress:000003000 Size00000114

0000300A  HIGHLOW

0000301E  HIGHLOW

0000303B  HIGHLOW

0000306A  HIGHLOW

通过上面的例子我们可以看到相邻区段正好相差0X1000,也就是4K,证实了上面所说的“每一个区段描述image一个4K Page(也就是一页)的重定位信息”同时我们可以看到第一个区段的VirtualAddress是00001000,正好是.text起始RVA。编译器就会根据【基址重定位项】去修正哪些需要修正的指令,读取对应地址的值例如00204000的数据将其加上delta值作为新的地址值,这样程序就可以正常执行了

0 0
原创粉丝点击