PE文件结构(五)基址重定位


 

参考

书:《加密与解密》

视频:小甲鱼 解密系列 视频

 

基址重定位

 

        链接器生成一个PE文件时,它会假设程序被装入时使用的默认ImageBase基地址(VC默认exe基地址00400000h,dll基地址10000000h),并且会把代码中所有指令中用到的地址都使用默认的基地址(例如 程序代码中 push 10001000,就是把10000000h当做了基地址,把push 10001000写入到文件中)。如果一个exe程序中一个dll装载时的地址与其它dll地址发生冲突(因为windows程序是虚拟地址空间,exe一般不会有地址冲突,加载dll时可能会有地址冲突),就需要修改代码中的地址,如push 10001000,call 10002000等。这时就需要用进行基址重定位。而基址重定位表中存放了,如果默认地址被改,需要修改的代码的地址。在PE文件中,基址重定位表一般放在一个单独的 ".reloc" 区,可以通过IMAGE_OPTIONAL_HEADER 中 的DataDirectory[5] 查看 基址重定位表 的RVA。

 

例如:

 

        用W32Dasm 查看 Demo.dll  (下载地址:http://pan.baidu.com/s/1qWDepo4)

 

图片1

 

        可以发现MyMessageBox 这个函数,看看它的代码中的push 10006040 , push 10006030 中的地址是指向字符串的。如果一个程序在加载Demo.dll时因为Demo.dll 默认的地址被占用了,而使用其它的基地址,例如使用20000000h作为基地址,Demo.dll就从20000000h开始装载。这样字符串“Demo”和“Hello World!” 就不是在10006040h跟10006030h中了,这时就需要把push 10006040 , push 10006030改成push 20006040 , push 20006030 。

 

 

        基址重定位表是由一个一个IMAGE_BASE_RELOCATION结构  构成的。


图片2

 

IMAGE_BASE_RELOCATION 结构:

 

 

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
//  WORD    TypeOffset[];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

 

 

        其中 VirtualAddress  表示 这一组地址的起始RVA。SizeOfBlock表示当前这个IMAGE_BASE_RELOCATION 结构的大小。TypeOffset是一个数组,它的元素个数就是( SizeOfBlock - 8 ) / 2 ,TypeOffset 每一个元素占用两个字节即16位,其中高4位表示重定位类型(一般都为3),低12位表示重定位地址。

 


实例分析:

 

        查看Demo.dll的第一个 IMAGE_BASE_RELOCATION 结构

 

图片3

 

可以发现:

VirtualAddress  为1000h

SizeOfBlock      为 0164h

TypeOffset[0]   0333h  即 3是重定位类型  33h为重定位地址
TypeOffset[1]   0338h

TypeOffset[2]   0340h

     ........

 

通过Type低12位+VirtualAddress 可以知道前三个的地址为1033h,1038h,1040h 。

再来看看图片1中Demo.dll的代码,可以发现1033h就是图片1中的push 10006040中的10006040,1038h就是push 10006030中的10006030 。

 

    如果加载dll,发现不是使用默认的基地址,PE加载器就会把基址重定位表中所写的地址的值改掉。改掉方法是把原先的值加上 实际基地址 - 默认基地址 的值。