PE学习(10) 重定位表介绍与解析

一个EXE文件被拉伸加载至内存,分配了一定的内存空间,同时EXE需要加载DLL模块,也占用内存空间

一般情况下,EXE都是可以按照ImageBase的地址进行加载的。EXE拥有自己独立的 4GB 的虚拟内存空间,但DLL 并不是,当有EXE使用DLL时才加载到相关EXE的进程空间的

为了提高搜索的速度,模块间地址也是要对齐的,模块地址对齐为10000H也就是64K

观察反编译汇编时可以发现:编译时生成的地址 = ImageBase + RVA

全局变量,字符串等地址信息已经在编译完成后写入文件,程序运行时可以按照预定的ImageBase来加载,不需要重定位表。因此一般EXE很少有重定位表,但DLL不同,如果某个DLL模块没有按照ImageBase进行加载,那么所有类似上面中的地址就都需要修正。否则引用的地址就是无效的。需要修正的地方很多,那么如何记录哪些地方需要修正,这是重定位表需要做的事情

数据目录项的第6个结构,就是重定位表

根据数据目录RVA找到的重定位表如下

如何判断一共有几块数据:最后一个结构的VirtualAddressSizeOfBlock都为0

 

内存中的页大小是1000H也就是2的12次方,表示一个页内所有的偏移地址。具体项的宽度是16位,高四位代表类型:值为3代表的是需要修改的数据,值为0代表的是用于数据对齐的数据,可以不用修改。也就是说只关注高4位的值为3(0011)的就可以了

VirtualAddress:当前这一个块的数据,每一个低12位的值加VirtualAddress才是

真正的RVA = VirtualAddress + 具体项的低12位

当前块的总大小:具体项的数量 = (SizeOfBlock - 8)/2

 

打印

解析DOS头和PE头,通过可选PE头拿到数据目录

其中IMAGE_DIRECTORY_ENTRY_BASERELOC是宏定义5,数据目录第六项是重定位表

另外数据目录中保存的Size字段,在解析分析重定位表时不会参考

数据目录并没有保存真正的重定位表,只是一个地址。通过前文提到的RVA to FOA 文章,将

遍历所有重定位表项,下一个和上一个的距离是SizeOfBlock大小,通过当前重定位表起始地址加当前的SizeOfBlock即可得到下一个重定位表的起始位置,强转PIMAGE_BASE_RELOCATION得到下一个重定位表

将每个重定位表的VirtualAddress转为FOA地址,并计算出没块中的项目数量:由于VirtualAddressSizeOfBlock分别是4字节,真正大小应该是SizeOfBlock减八。参考上图每项2字节,除以二得到结果

确认当前表来自哪一个节:如果该节位于PointerToRawDataMisc之间,认为重定位表在当前节。使用memcpy将节名复制到数组中,节名是8字节,保留第九字节固定00表示字符串结束

遍历每个重定位表项需要先偏移8字节2字

0x0FFF进行与操作,对高4位清零,得到相对于RVA的偏移,相加得到该项真正的偏移

右移动12位得到高4位,验证是否为0,如果为0表示结束,为3表示有效

 

完整代码