什么是重定位表?为什么要有重定位表?
- 如上图 A.DLL 和 B.DLL 中某个或多个的全局变量编译时可能是相同的内存地址。
- 如果 A.DLL 先行加载,其中的某个全局变量抢占到了 0x00410000 这个内存地址。
- B.DLL 因为后加载的原因,是从 0x00420000 处开始展开。此时,B.DLL 中如果有全局变量的地址也是 0x00410000 这个内存地址,就会出问题。
- 因为在调用 B.DLL 中某个全局变量的时候,如 0x00410000 地址的变量,但因为 B.DLL 是从 0x00420000处开始展开的,所以就无法调用。
- 那么这个时候就需要有一张表,记录并修改冲突的项目,例如把 B.DLL 中的 0x00410000 改为 0x00420000,那么就能正常使用了。
重定位表的位置
PE结构图:IMAGE_OPTIONAL_HEADER->DataDirectory 中的 IMAGE_DIRECTORY_ENTRY_BASERELOC 成员,就是重定位表。
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock; //以字节为单位,它有多大代表一个重定位块有多大
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
什么是重定位块?
一个PE文件中需要修正的地方是有很多的,需要分成很多块来记录。
根据什么来设计为一块一块的?
- 内存以 4KB 为单位,也就是在物理内存上,每个内存的大小划分是 4KB,4KB为一个物理页,重定位表也是如此设计。
- 一个物理页创建一张重定位表,每张重定位表只负责修复当前物理页上的相关内容,与其他物理页不影响。
如何确定重定位表结束?
直到出现连续 8 个为 00 的字节,代表当前进程重定位表结束。
- 每个物理页(4KB)重定位表中的地址,在4KB中的偏移需要多少个二进制位才能找到偏移?2的12次方 = 4KB。
- 物理页只有 4KB,如果想找到物理页中的任何一个地址,只需要 12 个二进制位就够了,但是占用了 16 个位。
- 也就是说,虽然重定位表中真正要修正的数据每个是 2 字节(16 个二进制位),但是真正使用到的只有 16 个二进制位中的 12 个二进制位。
- 16 个二进制位的高 4 位挪作他用(图中标红的部分),当操作系统真正要修复某个位置的时候,先判断高 4 位。
- 只有当高 4 位的值等于 3 的时候,才意味着 VirtualAddress + 低12位 才是真正要修复的位置。