首先我们要知道PE文件在映射进内存的时候有一个默认基址保存在可选头的ImageBase中
所以每次在映射进内存的时候都会在这个地址进行映射,都是所有的dll的默认基址都是一样的,如果一个dll的默认基址已经被其他dll占据了怎么办。只能映射到别的地方。从而ImageBase中的值就没有了参考价值。
程序中有许多常量值是按照基址为ImageBase编写的,
Offset HEX Assembly
10001000 55 push ebp
10001001 8BEC mov ebp,esp
10001003 6AFE push 0xFFFFFFFF
10001005 68 10220010 push 0x10002210
我们可以看到10001005处push的值是一个VA常量,都是如果映射到内存的基址变了,这个VA常量也必须改变才可以。这时候就需要用我们的重定位表。
我们先来看重定位表的结构
typedef struct _IMAGE_BASE_RELOCATION
{
DWORD VirtualAddress;
DWORD SizeOfBlock;
WORD TypeOffset[1];
}IMAGE_BASE_RELOCATION;
重定位表由IMAGE_BASE_RELOCATION的数组构成。
每个数组记录了当前4KB页面中需要重定向的地址。如何定位到这些值呢?
VirtualAddress:需要重定位的数据的RVA,这个值需要加上后面的TypeOffset的低12位才是需要重定位数据的RVA
需要重定位的数据位置 = 真实加载基址 + VirtualAddress + TypeOffset低12位
重定位后地址 = (真实加载基址-默认加载基址) + 需要进行重定位的地址
原本的常量是根据默认加载基址计算的,现在基址换成了真实加载基址,我们只需要将原本的数据加上基址的偏移就可以替换成真实有效的地址了。
注意:
1.应用场合:
凡是涉及到直接寻址的指令都需要进行基址重定位处理;
2.EXE文件不存在重定位表:对于EXE文件来说,每个文件总是使用独立的虚拟地址空间,所以EXE总是能够按照这个地址装入,这意味着EXE 文件不再需要重定位信息。
DLL文件需要重定位表:对于DLL文件来说,由于多个DLL文件全部使用宿主EXE文件的地址空间,不能保证装入地址没有被其他DLL使用, 所以DLL文件必须包含重定位表。
3.同一系统的kernel32.dll、user32.dll等会被加载到自身固有的IB,所以系统的DLL实际上并不会发生重定位问题。