重定位定义:
重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程,也就是说在装入时对目标程序中指令和数据的修改过程。他是实现多道程序在内存中同时运行的基础.
Windows使用重定位机制保证代码无论模块加载到哪个基址都能正确被调用的实现步骤:
1.编译的时候由编译器识别出哪些项使用了模块内的直接VA,比如push一个全局变量、函数地址,这些指令的操作数在模块加载的时候就需要被重定位。
2.链接器生成PE文件的时候将编译器识别的重定位的项纪录在一张表里,这张表就是重定位表,保存在DataDirectory中,序号是 IMAGE_DIRECTORY_ENTRY_BASERELOC。
3.PE文件加载时,PE 加载器分析重定位表,将其中每一项按照现在的模块基址进行重定位。
那些项目需要被重定位的:
1.代码中使用全局变量的指令,因为全局变量一定是模块内的地址,而且使用全局变量的语句在编译后会产生一条引用全局变量基地址的指令。
2.将模块函数指针赋值给变量或作为参数传递,因为赋值或者传递参数是会产生mov和push指令,这些指令需要直接地址。
3.C++中的构造函数和虚构函数赋值虚函数表指针,虚函数表中的每一项本身就是重定位项。
例子:
以下指令都需要进行重定位
mov eax,dword ptr[00100ffc]
push 00402000
jmp dword ptr [00403030]
分析重定位表需要了解两个问题:对一条指令进行重定位需要哪些信息;这些信息中哪些应该被保存在重定位表中。
重定位结构:
struct _IMAGE_BASE_RELOCATION
{
DWORD VirtualAddress; //重定位数据开始的RVA地址
DWORD SizeOfBlock; //重定位块的长度
WORD TypeOffset; //重定位项位数组
} IMAGE_BASE_RELOCATION;
VirtualAddress:
是这一组重定位数据的开始RVA地址.各重定位项的地址加上这个值才是该重定位项完整的RVA地址.
SizeOfBlock:
是重定位结构的大小
TypeOffset:
是一个数组.数组每项大小为两个字节,共16位.它又分为高4位和低12位,高4位代表重定位类型;低12位是重定位地址,它与VirtualAddress相加即是指向PE映像中需要修改的地址数据的指针.