什么是重定位:
重定位就是你本来这个程序理论上要占据这个地址,但是由于某种原因,这个地址现在不能让你占用,你必须转移到别的地址,这就需要基址重定位。你可能会问,不是说过每个进程都有自己独立的虚拟地址空间吗?既然都是自己的,怎么会被占据呢?对于EXE应用程序来说,是这样的。但是动态链接库就不一样了,我们说过动态链接库都是寄居在别的应用程序的空间的,所以出现要载入的基地址被应用程序占据了或者被其它的DLL占据了,也是很正常的,这时它就不得不进行重定位了。
哪些数据需要重定位:
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
:0040100383C4FC add esp, FFFFFFFC
:00401006 A1FC0F4000 mov eax, dword ptr [00400FFC] ;mov eax,dwVar
:0040100B 8B45FC mov eax, dword ptr [ebp-04] ;mov eax,@dwLocal
:0040100E 8B4508 mov eax, dword ptr [ebp+08] ;mov eax,_dwParam
:00401011 C9 leave
:00401012 C20400 ret 0004
:00401015 68D2040000 push 000004D2
:0040101A E8E1FFFFFF call 00401000 ;invoke Proc1,1234
其中地址为00401006h处的mov eax,dword ptr [00400ffc]就是一句需要重定位的指令,当整个程序的起始地址位于00400000h处的时候,这句代码是正确的,但00400000h只是它自己期望的起始地址,也许exe希望把这个DLL加载到00500000h处,那么句指令必须变成mov eax,dword ptr [00500ffc]才是正确的。这就意味着它需要重定位。即地址变成了00400ffch+(00500000h-00400000h)。这些数据被表示成[00400FFC],其实FFC只是数据相对于程序自身首地址的一个偏移量,程序假设自己就是从400000处开始加载然后从偏移FFC处可以取到数据,但是,实际加载之后,有时候并不是从400000开始。
所以,重定位的算法可以描述为:将直接寻址指令中的双字地址加上模块实际装入地址与模块建议装入地址之差。为了进行这个运算,需要有3个数据,首先是需要修正的机器码地址;其次是模块的建议装入地址;最后是模块的实际装入地址。在这3个数据中,模块的建议装入地址已经在PE文件头中定义了,即OptionalHeader中的ImageBase,而模块的实际装入地址是Windows装载器确定的,到装载文件的时候自然会知道。
事实上,PE文件的重定位表中保存的就是一大堆需要修正的代码的地址。
为什么需要重定位可查看这篇文章:https://cloud.tencent.com/developer/article/1432449
重定位表的位置:
重定位表一般会被单独存放在一个可丢弃的以“.reloc”命名的节中,但是和资源一样,这并不是必然的,因为重定位表放在其他节中也是合法的,惟一可以肯定的是,如果重定位表存在的话,它的地址肯定可以在PE文件头中的数据目录中找到。(不清楚的需要翻我之前的PE头文件介绍)
即利用OptionalHeader中的DataDirectory。
重定位表的结构:
- typedef struct _IMAGE_BASE_RELOCATION {
- DWORD VirtualAddress;
- DWORD SizeOfBlock;
- // WORD TypeOffset[1];
- } IMAGE_BASE_RELOCATION;
- typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
VirtualAddress:页起始地址RVA。
SizeOfBlock:表示该分组保存了几项重定位项。其实这个是快的长度
IMAGE_BASE_RELOCATION结构后面跟着的n个字就是重定位项,每个重定位项的16位数据位中的低12位就是需要重定位的数据在页面中的地址,剩下的高4位也没有被浪费,它们被用来描述当前重定位项的种类。在PE中一般只有0和3
每个IMAGE_BASE_RELOCATION元素包含了VirtualAddress、SizeOfBlock,后边跟着数目不定的重定位项。
实例分析
从PE文件头数据目录里面可得到重定位表文件偏移:0X00000C00
字节码可以得知
VirtualAddress=0x00001000
SizeOfBlock=0x000000B4
结束地位为C00+B4=CB4,以00 00 00 00为结束所有块标志,之类说明只有一个重定位表
第一个重定位数据3036
高四位为3,说明需要重定位。036为需要修正地址
- 实际VA地址=基地址 + 代码起始页面 +需修正地址
- =00100000 + 001000 +036
- =00101036
我们从OD里面查看内存中的代码,Ctrl+G跟随到10001036
因为例子是DLL,基地址是从10000000.EXE加载是00100000
可以看出10001036处有绝对地址10003008,就是需要对其进行修正的。
所有需要进行修正的地址都在重定位表重得出与对应