软件脱壳技术

简介

一般的压缩壳,都有专门的脱壳机可以使用,或者使用万能脱壳机,比如:procdump。而加密壳一般很少有脱壳机,必须手动脱壳。

手动脱壳步骤

  1. 查找程序的真正入口点(OEP)(OEP:Original Entry Point原始入口点)
    查找OEP有如下几种方法:根据跨段指令寻找OEP;根据堆栈平衡原理找OEP;根据变异语言特点找OEP;用内存断点找OEP;
  2. 抓取内存映像文件
    外科程序解压还原后就会跳转到OEP处执行,此时内存映像文件是已解压的程序。这时就可以抓取内存映像文件了(该过程成为Dump)。可以使用LoadPE工具。
  3. 输入表重建
    程序总是需要与系统打交道,每次与系统打交道的途径是API,而API的地址已经替换成了壳的HOOK-API的地址,那程序每一次与系统打交道都会让壳的代码获取一次控制权。重建输入表的关键是获得没有加密的IAT(即导入地址表,Import Address Table),一般的做法是跟踪加壳程序对IAT处理过程,修改相关指令,不让外壳加密IAT。可以使用ImportREC工具。

脱壳方法

ESP定律

脱壳之前,可以先使用工具PEiD查看下软件使用的是什么加壳技术,使用工具DIE(Detect it easy)查询软件是什么语言开发的:


可以看到软件的加壳技术为:ASPack(2.12-2.XX),使用的编译器为Borland Delphi(-)[-]

查找OEP

使用OD载入程序:

可以看到载入程序的开头显示的pushad,按下F8执行第一条指令,同时注意观察CPU寄存器,只有ESP寄存器是红色:

这种情况一般都可以使用ESP定律。
紧接着上一步,右键点击ESP寄存器的数值,选择HW break,会设置一个硬件断点:

在菜单栏中选择“调试”->“硬件断点”,可以查看到这个断点:

然后F9全速运行程序,程序听到断点处:

然后F8执行出函数返回,如下图所示:

到上面的地方,程序就基本上完成了解压缩,也就是脱壳,右键->分析->从模块中删除分析:


程序完成解压缩,这个地方就是程序的OEP。

抓取内存映像文件

上一步完成之后,下一步就是dump映像文件了,先把硬件断点删掉,然后反汇编窗口,右键,使用OllyDump脱壳调试进程:


点击保存,注意另外保存一份没有重建输入表的原始内容,因为有的程序重建输入表之后才能打开,有的不重建才能打开。本软件重置输入表之后可以打开,不重置不能打开。

重建输入表

上一步已经完成。

最后使用PEiD,再次打开脱壳成功之后的文件,可以看到识别出程序的编译器,而原始程序是识别不到的:

使用OD载入,也不会有最开始的提示加壳信息。

单步跟踪法

查找OEP

使用OD载入程序,一路F8,如果程序跑飞了,需要在跑飞的地方设置断点,然后F7跟进去,再按F8单步向下,直到找到“popad”指令(如果有向上的跳转,在跳转下面一行,按F4直接运行过循环),到了popad之后,离OEP就不远了:

抓取内存映像文件

同ESP定律。

重建输入表

同ESP定律。

两次断点

查找OEP

使用OD载入程序,在使用两次断点法之前,需要先设置一下,菜单栏->选项->调试设置:

在异常标签页中,所有项目全部打上勾,全部忽略。确定之后,点击工具条的M,来到内存页面,找到以程序文件名命名的属主段,在“.rsrc”段下断点:

然后shift+F9,运行到断点处,继续切换到内存页面,这个时候内存页面下的第一个断点会消失,接着下第二个断点,在0X00401000处,F2,接着shift+F9,运行到断点处,然后在F8继续运行,找到popad,就离OEP不远了。

抓取内存映像文件

同ESP定律。

重建输入表

同ESP定律。

最后一次异常法

查找OEP

使用OD载入程序,在调试设置窗口中,找到异常标签,取消所有的忽略,保存。

然后shift+F9,运行程序,记录下需要按几次shift+F9,程序才能运行起来,加入按了三次shift+F9程序才能运行,证明程序有2个异常,重新载入程序,按两次shift+F9,来到断点处,在堆栈窗口中找到“SE处理程序”,右键->反汇编窗口中跟随:

然后设置F2断点,shift+F9运行到断点处,继续单步F8,直到找到popad,离OEP不远了。

抓取内存映像文件

同ESP定律。

重建输入表

同ESP定律。

SFX自动脱壳法

查找OEP

使用OD载入程序,菜单栏->选项->调试设置->SFX标签->字节方式跟踪真正入口处:

然后直接重载程序,可以看到左下角的提示窗口显示“跟踪SFX”,然后一直等待程序暂停就行,时间可能会很长,找到入口点之后,程序会自动停下来:

抓取内存映像文件

同ESP定律。

重建输入表

同ESP定律。

模拟跟踪法

主要使用在两次断点法的基础上,如果两次断点,执行到第二个断点之后,在单步F8程序经常跑飞,或者不知道入口点长什么样的时候,可以继续使用模拟跟踪法,使用前提条件,程序停在二次断点法的第二个断点处 ,这个位置离真正的OEP已经很近了,切换到内存窗口,找到“SFX,输入表”:

记录下前面的地址,然后在下面的命令窗口输入:“tc eip<00567000”,按下回车即可,跟踪到入口点,程序会自动暂停。

出口标志法

pushad:寄存器数据全部入栈,压入32位寄存器,其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI。
popad:对应pushad,寄存器数据出栈。
导入程序,在反汇编窗口右键->查找->命令,或者直接快捷键Ctrl+F,搜索“popad”,注意不要选中整个块,然后单击F4,运行到当前位置,接着F8单步,直到到达OEP:

如果找到popad之后,按下F4运行到此处的时候,程序跑飞了,需要继续查找下一个popad,一次类推,直到程序不跑飞为止,然后在F8单步。

秒到OEP

OD载入程序,直接F9运行,然后来到堆栈窗口,拉到最底下,从最底下向上翻,找到以程序名命名的第一个字符串:

然后选中该行,右键,在反汇编窗口中跟随:


然后删除分析,这个时候,堆栈窗口中会显示这个框:

沿着框向上找,会找到返回点:

然后右键->反汇编窗口中跟随,转入反汇编窗口,在反汇编窗口中,向上翻,对照常见OEP(先使用工具查下软件使用什么语言编译的,然后找对应语言的OEP对照),找到程序真正的OEP:

然后想办法让程序停在此处,选中该行,右键->数据窗口中跟随->选择:

右键->断点->硬件执行

在菜单栏->调试->硬件断点中,能够看到我们设置的这个断点:

然后重新载入程序,运行F9,程序会直接停在OEP处,然后DUMP,就和上面的过程一样了。

如果是强壳,使用上面的办法拖下来之后,可能程序还不能正常运行,还需要修正镜像大小和导入表才可以。