[原创]自己动手修复ImpREC的Bug
--通过OD给ImpREC打补丁:
(在一次给inc2l.exe脱壳的作业中发现的问题)
用来测试BUG的程序:RadASM中Tools的inc2l.exe
首先用OD加载,发现他是一个加壳的程序,

入口有保存环境的代码(一般的加壳程序都会保存环境):
push eax 单步步过这条指令,
找到栈顶,数据窗口跟随

然后下硬件访问断点,因为它保存了环境,势必会在他恢复代码之后还原环境的,

然后就可以等着他来了,放开程序跑,F9
第一次来到是一个0x7C…开头的地址,可能是系统Dll的代码略过,继续F9

直到断在主程序的地址

经过两次F9后顺利来到了主程序的代码,然后把代码往下翻,看哪里有jmp,因为一般还原完代码都会有个jmp跳回OEP,不远处就发现了有个jmp ,选中该行F4程序跑到0x40AC1E处


发现代码已经被还原了,OD分析出是模块入口点。
F7单步过去,

然后右键脱壳,点脱壳,保存文件为dump.exe

一运行发现崩了,因为他这个程序的导入表在程序加载到内存的时候被抹掉了,这里用到impREC来修复导入表


因为这个程序是要通过手动定位IAT信息通过反推获取导入表信息(程序自带的自动查找IAT不准确),所以需要借助OD手动查看IAT表的起始位置和大小。在OD里面随便选择一个API点击一下,然后数据窗口跟随,选择数据窗口中跟随地址
再在数据窗口选择长型地址,就发现API名称跟地址都出来了

再通过肉眼过滤,一般API都在很大的地址,所以得到从:
开始到

大小0x1F0 
然后在ImpREC里填RVA:0x8050,Size:0x1F0,然后点获取导入表,发现导入表函数都是有效的,然后点修复。

选择刚保存的那个dump文件,确定

生成了一个
dump_.exe文件
然后打开发现这次不是访问内存错误了,直接是报应用程序初始化失败。到这里之前不会解析PE出错,只有用了ImpREC之后才出了错,然后我们来分析PE格式

用WinHex打开dump_exe,手工分析PE结构,由于刚才我们修复转存文件的时候ImpREC添加了一个新节,不知道是不是节区文件的问题,首先看是不是没有修改节的个数,发现是4个节,对照一下是对的。

经过仔细对比之后,发现最后添加了一个节区之后,PE头部的总大小变成了0x218,然后ImpREC并没有修改PE中的SizeOfHeaders的大小0x200。现在我们通过winHex手动修改他的值为0x218保存,然后程序正常运行了。
思路:
我们要修改PE中的SizeOfHeaders要在写文件之前,前面我们看到源程序有修改PE的SizeOfImage的操作,所以可以找到源程序修改SizeOfImage的代码区域,然后在那附近找到合适的代码地址覆盖jmp跳到源程序没有使用的内存空间添加我们修改SizeOfHeaders的代码,修改操作完成后添加jmp覆盖的指令再jmp回去。
如何定位该区域呢?首先我们知道程序要创建并写文件,所以会调用CreateFileA,WriteFile.所以我们可以通过API的参数知道写文件的Buffer在哪里,然后再通过给SizeOfImage的4字节设访问断点可以定位源程序修改PE的SizeOfImage代码的地址了。
LAB:
用OD打开ImpREC程序,F9让他跑,选择inc2l.exe的进程,填写RVA, Size,获取导入表,然后点击修复转存文件,先不要点保存,回到OD右键查找当前模块的名称,找到CreateFileA右键设置在每个参考上设置断点,然后回到ImpREC选择Dump文件点保存,

OD断了下来,查看堆栈窗口,发现不是我们想要的dump_.exe
放开跑,第二次是创建我们想要的dump_.exe
跑到这里肯定会有写文件的操作,再下一个WriteFile断点。
然后F9,写文件的时候触发断点,查看堆栈窗口的buffer,
数据窗口跟随,他写入的大小是整个文件的大小,
从这里可以印证添加了一个节,源程序修改了SizeOfImage,找到该程序操作这块内存的位置下访问断点,从而定位代码
通过对PE格式的分析,定位到0XC5CDD0,右键下断点,一个DWORD长度,然后重新跑程序,操作从LAB开始。
之后势必会再访问这个地址的时候断下来, F9的同时观察数据窗口PE数据是不是被读到内存中来了,如果没有,继续F9。之后再0x42CA12的地址又触发了断点,发现他的上一条指令是就修改SizeOfImage
这一切都是发生在写文件之前的,所以我们可以在这附近找一个合适的地方插jmp跳到别的地址添加代码,然后修复覆盖的指令再jmp回来。一个jmp指令对应的机器码是5个字节,我们只要找到一条指令大于等于5字节就可以了。刚好在0x42CA0F的下一条指令是6个字节,可以在这里插jmp,首先记下这条指令MOV EAX, DWORD PTR SS:[ebp+420],因为插入自己的代码之后还是要还原原指令的(我们只能添加代码,不能修改原有的代码),到代码的最后面找一块空白的地方,
0x448930 修改这里为jmp过去的地址,于是选中0x42CA12修改汇编代码:
jmp 0x448930

TAB:-----从这里跳过,请看分割线以下-------2015.9.16
然后:
1.再0x448930的地址添加汇编代码修改SizeOfHeaders:
ADD DWORD PTR DS:[EAX+3C],28
每次都加一个节的大小0x28.
2.保存jmp覆盖的原指令:
MOV EAX,DWORD PTR SS:[EBP+420]
3.修改完之后要跳回去继续执行原来的代码,添加:
jmp 0x42CA18

然后全选复制到可执行文件,再保存为ImpREC_fix.exe.到这里已经修复完了。
-------------------------------分割线---------------------------------
2015_9_16修改:
由于上次是用了OllyDump的时候没有取消他的默认选项重建导入表,但是OD并没有修复成功,又多出了一个节表。
然后我用ImpREC再次修复IAT的时候又多添加了一个节表,然后就出现了我遇到的问题......
我取消了重建输入表选项,之后用ImpREC修复就没有问题了。
这里多谢“Kisesy”的提醒。
然后我又想给打个补丁,让其在Dump出.exe后无论添加多少个节表用ImpREC修复都不会出错。
分析:
可能是ImpREC修复的时候没有检查Dump出来的文件有多少个节,直接在后面添加一个节,然后写导入表信息。我们可以在其写dump_.exe文件之前,算一下内存中dump文件的节表个数,写入SizeOfHeads就行了。
修复:
从上次最后修改补丁的地方开始.(TAB)
覆盖原来的修改代码加入如下汇编代码(经过分析PE格式写出的,望大家指教):
pushad ;保存环境
lea eax, dword ptr ss:[eax+0x98] ;获取MZ头地址
mov ecx, dword ptr ds:[eax+0x3C]
lea edx, dword ptr ds:[eax+ecx] ;获得PE标志地址 IMAGE_NT_HEADERS
lea esi, dword ptr ds:[edx+0x18]
xor ecx, ecx
mov cx, word ptr ds:[edx+0x14]
add esi, ecx ;求得节表的首地址
mov cx, word ptr ds:[edx+6] ;节表个数:edx+8
imul ecx, 0x28 ;求出当前的节表总大小
add ecx, 0x28 ;求出修复后的节表总大小
add esi, ecx ;节表尾地址
sub esi, eax ;节表尾减MZ头求出SizeOfHeaders
mov dword ptr ds:[edx+0x54], esi ;复制SizeOfHeaders到Buffer
popad ;还原环境
MOV EAX,DWORD PTR SS:[EBP+420] ;修复jmp覆盖的代码
jmp 0042CA18

修复的ImpREC上传了
然后,我也是菜鸟一枚(还在科锐学习...),有什么地方写的不对可以指出来,大家可以互相交流~
一直都是潜水党.第一次发帖,求通过
