深入理解PE结构中的重定位表.reloc
第一个问题:一个EXE程序如何运行?
exe程序加载到内存是由一个exe和一堆dll组成的。
任何一个程序一般都是先加载exe,因此exe加载到内存的时候先加载到ImageBase地方,这个加载过程,就是磁盘文件拉伸到内存的过程。
exe加载到内存运行的过程和一个PE拉伸到内存的过程类似,PE拉伸到内存。
exe运行也是,先要把exe从ImageBase拉伸到内存中之后,然后开始加载dll。
加载第一个dll的时候,就会从0x10000000开始的地址分配内存空间,然后加载第二个dll,就这样一块一块的加载,加载完成之后,一个程序就可以跑起来了,这个过程类似于PE拉伸到内存的过程。
但是加载第二个dll的时候,ImageBase也是0x10000000,但是第一个dll已经占用了,那么就会往后边的位置加载,那么这个dll的ImageBase就会变。
而程序中有的位置的地址使用的是有的使用的是RVA,有的使用的是计算好的RVA+ImageBase地址。
因此当ImageBase发生变化了,那么程序里面使用原ImageBase + RVA的地址访问就会出现问题。
因此这些地方就需要重定位,而重定位表就是记录程序中有哪些位置需要重新计算地址。
一般exe里面是没有重定位表的,因为exe加载到虚拟空间的内存中的时候会抢先加载到ImageBase的位置,但是dll不一样,因为一个exe会加载多个dll,这样后边的dll一般都要进行重定位(因为抢不到0x10000000这个ImageBase位置)
为了提高搜索的速度,而2个dll模块之间一般都会有个空隙,这个空隙一般都是0x10000000h,与内存对齐相比,这相当于模块对齐。
第二个问题:重定位表长什么样?
先在可选头中找到数据目录表,然后数据目录表第6个(下标为5)存储的是重定位表第一块的RVA,转换成FOA,在文件中查找就是第一块重定位表的位置。

重定位表和之前学的导出表结构不太一样,重定位表也是连续放在一起的,但重定位表是一块一块存储的。

分析重定位表块每个字段的意思:
VirtualAddress 代表的是一个基址(4个字节)
SizeofBlock (4个字节) 表示第一块重定位表的尺寸(这个单位是字节),注意这个块的尺寸的起始位置是从VirtualAddress这个位置开始算的,共计多少个字节。
后边跟着数据,每2个字节是一部分。
由于重定位表记录的是哪些地方需要重新计算地址,那么应该如何设计呢?
如果用普通的思维逻辑,假如程序中有100个地址需要修改,每个地址4个字节表示(0x12345678),那么100*4就需要400个字节空间记录这写地方需要改。
为了节省空间,重定位表设计利用偏移的概念,就像x86段寄存器寻址那样。用一个基址(4字节),其他需要改的地址,为相对于这个基址的偏移(2字节)。
例如:有这几个地址0x102000、0x103000、0x104000、0x105000、0x106000需要重定位
普通思维0x102000、0x103000、0x104000、0x105000、0x106000把这几个地址记录下来需要重新定位,那么需要4*5=20个字节空间
重定位表的设计思维,把0x100000作为基址
0x2000 、0x3000、0x4000、0x5000、0x6000(都是2个字节)偏移,那么这总共记录4+5*2=14个字节的空间。
那么VirtualAddress 记录的就相当于这个基址,VirtualAddress(相当于Base)+后边2个字节的地址(相当于偏移)=一个RVA(这个值就是最终的要重定位的地方,这个和是RVA)
后边跟着的2个字节的数据,就相当于要改的地址的偏移。
由于要修改的地址太多,可能有的需要重定位的地方是0x601000、0x602000、0x604000、0x605000、0x606000,那么还需要按照上边的思路存储,
因此就会又有一块。
所以这就造成了重定位表是一块一块的原因。
第三个问题:那么这个重定位表中的块会有多少块呢?
重定位表一块一块的结构有多少块这个是不确定的,但是可以根据结尾标志判断有多少块要修改的地方。
第四个问题 :后边跟的偏移为什么是2个字节呢?
首先要了解内存分页的概念,内存为了加速查找速度,一般都是按照页查找,每一页大小是4kb(0x1000h=4*1024=2的12次方)
而重定位表中的每一块会记录内存中每一页(内存页4096=0x1000h=4kb)所有需要修改的位置。
如果能记录这个内存页中所有要修改的地方,那么就需要12位来表示。1个字节是8位,1个字节不够,那么2个字节是16位,2个字节就够了,还多4位
因此这后边的偏移是2个字节,并且这2字节并不全是偏移,前边的前4位也有用,后边的12位才是组成真正的偏移值。
前4位(范围0x0-0xF),如果是0011(0x3),那么就表示后边12位的这个偏移位置需要修改。在PE64+中 前4位一般为0xA
前4位,如果是0000,那么不用管,表示的是内存对齐的概念。
学PE中导入表、导出表、重定位表的原因:

过游戏驱动的一种办法:游戏会把内核函数加上钩子,当我们调试的时候,使用这些内核函数的时候,被游戏驱动发现,就会返回一个0,这样我们就找不到游戏程序。
这个时候就需要内核重载,把内核当做一个exe,只是一个在0环跑的exe,然后把这个内核拉伸,重新复制一份复制到内存中。
但是复制之后,本来要加载到内核原来的位置,这个位置被原内核占住了,就需要把复制过来的内核放在原内核位置的下边,那么使用这个内核的时候,里面的地址也需要进行重定位。
程序中调用内核函数的时候,不用系统的内核函数,而使用我们复制的这个内核,这就叫做内核重载。

浙公网安备 33010602011771号