通过ReverseMe学习PE文件结构-重定位表
任务:
给程序Reversed_.exe添加一个重定位表,使ReverseMe_.exe能正常工作。
工具:U-Edit、OllyDBG
需要知识:PE头结构、少许汇编知识
为什么会产生这个错误?跟踪一下ReverseMe_.exe的运行情况就会发现:对MyMessageBox的调用没错,程序正确的找到了这个函数的入口地址,该函数在我这里表现是这个样子的:
00374085 > 55 push ebp
00374086 8BEC mov ebp, esp
00374088 6A 00 push 0
0037408A 6A 00 push 0
0037408C 68 A0404000 push 4040A0《一
00374091 6A 00 push 0
00374093 FF15 44404000 call dword ptr [404044] ; ReverseM.00400000《二
为了便于理解,这里使Reversed_.exe中的同一段代码:
00404085 > 55 push ebp
00404086 8BEC mov ebp, esp
00404088 6A 00 push 0
0040408A 6A 00 push 0
0040408C 68 A0404000 push 004040A0 ; ASCII "I am a LaMe rEvErSeR!:p"《一
00404091 6A 00 push 0
00404093 FF15 44404000 call dword ptr [<&USER32.MessageBoxA>>; USER32.MessageBoxA《二
应该注意的到各个命令除了地址不同,其他都是一样的(真是废话,他们就是同一段代码)。
对照一下就能发现造成错误的原因是地址:代码的地址被移动到了另一个位置,而代码中使用的数据地址、函数地址却没有做调整。
怎么能让程序自己对地址做调整?这就是重定位表的作用了。
观察一下出问题的两条语句(标记一、二)会发现命令中用到的地址与正确的地址相差的距离是一样的,如果能把这个距离求出来,让程序自己把地址调整一下,命令不就都是对的了么?这个距离是模块实际装入地址(00370000 )与模块建议装入地址(00400000)之差。可见,重定位需要3个数据:模块实际装入地址、模块建议装入地址、需要修正的机器码地址。其中,前两个数据分别是由PE头和WINDOWS装载器确定的,所以需要我们提供的数据仅仅是需要修改的机器码的地址一个。
每个重定位块以一个IMAGE_BASE_RELOCATION结构开头,后面跟着本页面使用的所有重定位项,每个重定位项占用16位的地址(WORD):
IMAGE_BASE_RELOCATION STRUCT
VirtualAddress dd ?;重定位内存页的起始RVA
SizeOfBlock dd ?;重定位块的长度
IMAGE_BASE_RELOCATION ENDS
一般来说,重定位项里放的应该是地址,而存放一个地址需要32位(DWORD),为何重定位表中的重定位项是16位的?
每个需要重定位的块都需要单独的重定位块(我猜是因为不同的块被装入的地址不一样,重定位信息不一样,自然不同的块的重定位信息要分开放)。一个块中的地址的高位总是相同的而且在一个页面中寻址需要的指针位数是12,如果把高位地址统一表示,就可以省略一部分空间。
每个重定位项的低12位就是要重定位的数据在页面中的地址,高4位用来描述当前重定位项的类型,能见到的只有两种:
0 这个重定位项无意义,仅仅用来作为对齐用
3 重定位地址指向的双字的32位都需要被修正
最后所有的重定位块以一个VirtualAddress字段为0的IMAGE_BASE_RELOCATION结构作为结束。
另外,重定位块中的重定位项数=(SizeOfBlock-8)/2
============
一.给程序Reversed_.exe建立重定位表
建立重定位表
新建一个块:
Name: .reloc
VirtualSize: 0x0100
VirtualAddress: 0x6000
SizeOfRawData: 0x0200
PointerToRawData: 0x0E00
PointerToRelocations: 0
PointerToLinenumbers: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
Characteristics: 0x42000040
我们需要重定位的有两处:
0040408C 68 A0404000 push 004040A0 ; ASCII "I am a LaMe rEvErSeR!:p"《一
00404093 FF15 44404000 call dword ptr [<&USER32.MessageBoxA>>; USER32.MessageBoxA《二
所以需要修改的机器码的地址为408D和4094(记得吧?命令中用到的地址),建立这样一个重定位块:
VirtualAddress dd 0x4000
SizeOfBlock dd 0xC(重定位块的长度,而不是需要修改的机器码的地址的个数)
0x308D(加上类型说明)
0x3095
VirtualAddress dd 0x0000(表示重定位块结束)
SizeOfBlock dd 0x0
============
2.将Reversed_.exe的重定位表指向新块。
============
3.运行一下,发现还是不行。跟踪程序的运行情况会发现重定位的两处地址并没有改变。
重新看一遍PE结构,发现IMAGE_FILE_HEADER结构里有个Characteristics项,这一项决定了文件的装入方式。
我把第0位置0(说明文件中存在重定位信息)时情况依然。
我更干脆的把文件类型改成DLL文件(第13位置1)时两个程序都运行不了了。
看来我选错对手了,这条路我走不下去了 =.=
============
4.不用重定位的解决方法。
之所以会有重定位问题,是因为文件被装载到的位置不是我们预期的位置,会造成这种情况的原因是建议装入地址已经有别的文件占用了,所以我的解决方法就是改变Reversed_.exe的基址和一些代码的地址参数:
因为这个方法并不好,没有通用性,就不细说了,有兴趣的自己看看程序
============
问题:
1.重定位表是DLL文件专用的?就没办法将EXE当DLL一样用么?
2.我把Reversed_.exe的第13位置1的时候,运行ReverseMe_.exe会遇到一个初始化错误。这是怎么回事?