【脱壳】手脱NsPack1.4壳
Magictong
2011-11-23
调试环境:xpsp3
调试工具:OD PEID WinHex
这是一个比较简单的壳,手脱步骤不难。通过堆栈平衡原理找OEP的方法就可以搞定了。
脱壳步骤
第一步:查壳
拿到样本之后,要使用PEID查看一下是什么壳,这个就跟医生治病救人一样,你得先知道病人是得什么病,然后才能对症下药。但是官网下载的PEID0.95是查不出这是什么壳的,因为带的查壳插件不够,可以去看雪下一个插件比较全的0.94版本,如果这个也查不出来,那恭喜你,你可能发现了一种新壳。言归正传,我这里的一个样本正好是加了NsPack壳,在PEID里面可以看到。

描述里面的Liuxingping难道是作者名字?Overlay的意思是有附加数据(注意,查壳工具还有其它的,自己觉得哪个好用就用哪个)。
原理简介:
刚才说过,这种NsPack1.4壳可以使用堆栈平衡的原理来脱,堆栈平衡的原理是什么?在编写加壳软件时,必须保证壳初始化的现场环境(主要是寄存器的值,尤其是ESP)与源程序的现场环境是相同的,那么加壳程序在初始化时就会首先把所有寄存器push到栈里面保存起来。壳执行完毕之后,立即恢复所有寄存器的值,然后跳转到原程序处执行。要保存所有寄存器的值,可以通过push指令将所有寄存器的值一个个的push到栈中,不过一般不会这么干,而是用pushad/popad、pushfd/popfd这两对指令来保存和恢复现场环境,我们先看看这几条特殊的指令(16位的指令一般就不用了):
pushad/popad |
将所有的32位通用寄存器压入/取出堆栈 |
pusha/ popa |
将所有的16位通用寄存器压入/取出堆栈 |
pushfd/ popfd |
将32位标志寄存器EFLAGS压入/取出堆栈 |
pushf/ popf |
将16位标志寄存器EFLAGS压入/取出堆栈 |
那么编写加壳软件时,为了遵守堆栈平衡一般就会有下面这样的指令:
PUSHAD ; 保存环境
PUSHFD ; 标保存志寄存器,可以不要,如果不要下面的POPFD也不要
……
POPFD ; 恢复标志寄存器
POPAD ; 恢复环境
JMP OEP ; 跳到原程序入口(这句也可以使用
PUSH OEP; RET 这两句代替)
OEP:…… ; 壳运行完毕后的原程序入口
注:PUSHAD和PUSHFD交换位置也是没关系的,当然下面的POPAD和POPFD也就要交换位置了。
那么在脱壳时,在执行了PUSHAD或者PUSHFD之后对ESP设置内存断点,很快就能找到OEP。
第二步:脱壳
既然有原理了,我们就来实战,上OD,把这个样本打开,入口如下:

这个样本简直就是原理的范本。
1、F7单步两次之后,走到那个CALL 108.0040A2BB指令处。
2、现在ESP的值是0012FFA0,对ESP的值设置一个硬件访问断点(可以通过命令Hr 0012FFA0来设置)。
3、F9跑起来直到中断下来,会发现走到了一个POPFD指令,嗯,可以和最开始的PUSHFD指令相呼应了,也许马上就水落石出了,注意到下一条的JMP 0040347F,这是一个E9长跳转,单步过去走到jmp的地址。

4、现在是到这里了,这个地方应该就是真正的OEP入口。看着怪怪的,用OD强制分析一下代码,如下图,就很清晰了。


5、DUMP文件,使用OD的插件OllyDump即可,注意选取OEP为0000347F。脱壳之后我给这个新PE起名为UnPack_108.exe,这个PE可能比较简单,不需要再额外重建输入表和清除自校验,但是原来的程序是有附加数据的(Overlay),需要把这块数据用16进制编辑工具附加到脱壳之后的PE文件UnPack_108.exe的尾部,然后就可以使用了。过程有点麻烦,而且一般都是需要修复导入表的,如果修复不成功是没法运行起来的。其实也可以不用dump文件而直接继续调试。只是每次都要手脱一下,可惜OD没有那种通过宏来保存操作的功能,否则可以把手脱的过程录制成宏。