说在前面
这两天刚接触壳看了L4Nce前辈的脱壳篇上后,自己迫不及待的就想自己脱一下那个大表哥的例子程序。L4Nce前辈在视频里说准备要用OD脚本讲解如何脱这个壳,无奈自己不会OD脚本就想先试试看能不能脱,然后发现此例子不用OD脚本更简单,就想着把自己的思路和方法发出来和大家一起学习。
工具
- OD
- LordPE
- ImportRE
- 环境虚拟机win7
脱壳详细过程
寻找OEP
这里我用的是简单的ESP定律,即堆栈平衡原理。
先把例子程序拖到OD中,然后程序来到壳代码的程序入口点。第一条指令是pushad是壳代码为了保存环境,所以在离开壳代码进入OEP的时候其一定会popad恢复环境。我们F7运行后观察到ESP为0x18FF6c,其在popad的时候一定会在访问此栈地址,所以我们在此栈地址处下硬件读断点。(在命令行键入 hr 0x18FF6c)
然后我们直接F9运行程序,程序会在执行完popad指令后断下。然年我们就可以很容易发现跳转到OEP的代码指令,因为程序的加载基地址为0x400000,所以获得OEP的RVA为0x1DDAC。
dump程序文件
接着我们就可以在程序OEP处下断点然后运行程序断在此处。
然后我们利用工具LordPE进行dump程序,在对应进程处右击-》完整转存。(可以不必一定运行到此处在dump只要所有区块都被解压缩后就可以dump程序)
重建输入表
我们Alt+M查看内存,一般程序的IAT都会存放在非外壳代码的区块中。我们只需要一个区块区块的找就可以了。
但是这里我们并没有发现IAT的踪迹,然后就很懵。为什么没有IAT呢?这时我想到其程序如果没有IAT是怎么调用的API呢,接着就想着查看程序的函数调用指令。来到程序的函数调用处发现OD中并没有显示API调用的注释。
难道其没有调用API吗?显然是不可能的,那麽为什么没有API的调用提示呢?这时我看到一些调用指令和正常的API调用指令很相似。call [ 地址 ]的形式
跟进后我们发现其地址处是指令 push 0x769C4D40,retn。就相当于是jmp 0x769C4D40,接着发现 0x769C4D40处实际就是一个API的首地址。
然后我们发现这种调用形式还有很多,然后总结出应该是壳代码将IAT处API函数真正的地址修改为调用自己的代码然后再由此代码去调用对应的API。这样做就可以让程序不需要IAT而运行,从而达到防止IAT被找到构建输入表。如果我们直接用输入表重构工具ImportRE来导入此程序就会发现因无法找到IAT而无法找到有效的函数调用地址。
ruci
我们需要自己找到其修改IAT地址为自己的地址的代码,防止其修改或将真正的API调用地址写回到IAT中,使ImportRE可以找到IAT从而获得对应的有效函数调用地址。因为其壳代码会在将各个区块解压缩后修改对应的IAT的数据。我们需要对原IAT地址处下内存写入断点从而找到这段修改IAT的代码。
接下来我们以前面我们分析的那条API调用指令为例,其地址调用0x432174地址的代码。
然后用OD重新加载程序,通过对代码段下内存访问断点多次中断后知道代码段被完全解压缩后,我们在0x432174地址处下内存写入断点。
然后运行程序后会中断,我们发现此时壳代码会先对此地址进行初始化为0。
然后我们继续运行程序,程序中断在API调用地址获取处。我们发现其先把API真正的地址写入0x432174中,然后又把自己的代码地址替换掉0x432174中的真正的API调用地址,所以我们可以直接将替换地址的代码直接nop掉。这样其就不会修改真正的API调用地址,达到防止其修改IAT的目的。
我们对此处代码下断点,然后重新加载程序。运行程序来到此断点处,我们将修改IAT的指令直接nop去。
接着我们运行程序,让程序停到OEP处。这是我们在查看IAT表发现其为对应的API真正的地址。此IAT表的RVA为0x32000,大小为0x554
然后我们利用工具ImportRE重建输入表,填写OEP为 0x1DDAC ,在IAT的RVA处填写0x32000,大小为0x554。然后点击获取输入表可以获得输入表函数信息。
接着我们点击修复转存文件并选择我们一开始dump的文件即可完成脱壳操作。
总结
L4Nce前辈用的是OD脚本,也可以直接用我的这种直接nop的方法。当然OD脚本是真的强大,回头我也要学学。此壳主要难点就是防止壳代码修改IAT,从而重建输入表。新手脱壳,如有建议请指点。