第24章 加壳与脱壳(二):ASPack、FSG、压缩壳通用脱壳方法

上一章我们聊了UPK和北斗壳,算是给压缩壳开了个头。这一章,咱们继续深入,把ASPack和FSG这两个老牌压缩壳给收拾了。说实话,这两个壳在现在的恶意软件里已经不常见了,但在一些老旧的商业软件、游戏修改版里,你还能经常碰到它们。我个人的经验是,掌握了这两个壳的脱法,你基本就摸清了压缩壳的通用套路。

24.1 压缩壳的“三板斧”

压缩壳,说白了就是给PE文件“瘦身”。它把原始代码和数据压缩起来,运行时再解压到内存里执行。你想想看,它总得有个解压的入口点吧?总得把原始入口点(OEP)给恢复吧?这就是我们的突破口。

我总结了一下,压缩壳的脱壳流程,其实就是三板斧:

  1. 找OEP:找到程序真正的入口点。这是最关键的一步。
  2. Dump内存:把解压后的完整程序从内存里“倒”出来。
  3. 修复IAT:重建导入地址表,让Dump出来的程序能正常运行。

嗯,这里要注意,不同壳的OEP特征不一样,但思路是相通的。我们一个一个来看。

24.2 ASPack:经典的“Pushad/Popad”壳

ASPack是我早期逆向时经常遇到的壳。它的特征非常明显:入口点通常是一连串的pushad,然后是一堆花指令,最后用popad配合retn跳转到OEP。

为什么会这样?pushad保存了所有寄存器的状态,这是为了在解压过程中不破坏原始程序的寄存器环境。解压完成后,popad恢复现场,然后跳转。

脱ASPack,我习惯用ESP定律。这个方法简单粗暴,成功率极高。

ESP定律脱ASPack步骤:

  1. 用OD(OllyDbg)加载程序,停在入口点。你会看到类似 pushad 的指令。
  2. 在命令行输入 hr esp,对ESP寄存器下硬件访问断点。
  3. 按F9运行,程序会断在某个popadretn指令附近。
  4. 单步跟踪几步,就能看到一个大跳转,跳到一个看起来像正常代码的地方。那就是OEP。

我曾经遇到过一个ASPack加壳的程序,它加了反调试,ESP定律失效了。后来我用了内存断点法,在代码段下断点,才找到OEP。所以,ESP定律不是万能的,但它是我们最趁手的武器。

24.3 FSG:更小巧,更隐蔽

FSG(Fast Small Good)是另一个经典的压缩壳。它比ASPack更小,代码更精简。它的入口点特征不像ASPack那么明显,但通常也是以pushad开头,不过后面的代码更短,跳转更直接。

FSG的脱壳方法,和ASPack大同小异。我个人更推荐用单步跟踪法。因为FSG的代码量少,单步跟踪很快就能找到OEP。

单步跟踪法技巧:

  • 遇到call指令,如果call后面跟着popadd esp,这通常是花指令,直接F8跳过。
  • 遇到jmpretn,如果跳转目标看起来像代码(有连续的pushmov等),那可能就是OEP。
  • 注意观察堆栈窗口。当堆栈中出现返回地址,且该地址指向一个看起来像函数入口的地方,那很可能就是OEP。

我记得有一次,我脱一个FSG壳的程序,单步跟踪了半天,发现它在一个循环里打转。后来我仔细一看,原来它用了SEH(结构化异常处理)来混淆。我直接忽略了异常,继续跟踪,才找到真正的OEP。所以,遇到异常不要慌,先看看是不是壳在搞鬼。

24.4 通用脱壳方法:ESP定律与内存断点

其实,不管是ASPack还是FSG,甚至是其他压缩壳,它们的核心逻辑都是一样的。所以,我们可以总结出一套通用的脱壳方法。

我把它们整理成了下面的流程图,方便你理解:

压缩壳通用脱壳流程 加载程序到OD 入口是pushad? (ESP定律适用?) 使用ESP定律 单步跟踪法 找到OEP Dump内存 修复IAT

你看,流程很清晰。先判断入口特征,然后选择合适的方法找到OEP,最后Dump和修复。这套流程,我用了十年,依然有效。

24.5 实战:脱一个ASPack壳的程序

光说不练假把式。我们拿一个实际例子来走一遍流程。

假设我们有一个名为 target.exe 的程序,被ASPack加壳了。

第一步:加载程序

用OD加载 target.exe。停在入口点:

00401001 >  60              pushad
00401002    0BC0            or eax,eax
00401004    74 68            je short 0040106E
00401006    E8 00000000      call 0040100B
...

嗯,看到了吧,经典的 pushad 开头。

第二步:使用ESP定律

在命令行输入 hr esp,然后按F9运行。程序会断在:

0040106E    5D              pop ebp
0040106F    55              push ebp
00401070    C3              retn

第三步:找到OEP

单步跟踪,执行到 retn,程序会跳转到:

00401234    55              push ebp
00401235    8BEC            mov ebp,esp
00401237    83C4 F0         add esp,-0x10
...

这个 push ebp; mov ebp,esp 就是典型的VC++入口特征。OEP找到了!

第四步:Dump内存

用OD的插件(如OllyDump)或LordPE,将当前进程的内存Dump出来。

第五步:修复IAT

用ImportREC工具,选择刚才Dump出来的文件,填入OEP地址(00001234),然后修复IAT。搞定!

注意事项:

  • Dump时,要确保程序已经解压完成,且停在OEP处。
  • 修复IAT时,如果ImportREC无法自动识别IAT,可以尝试手动搜索或使用其他工具(如Scylla)。
  • 有些壳会修改PE头,导致Dump出来的文件无法运行。这时需要手动修复PE头。

24.6 避坑指南

最后,分享几个我踩过的坑:

  • 反调试:有些壳会检测OD,导致断点失效。可以尝试用硬件断点、内存断点,或者使用隐藏OD插件。
  • 多线程:有些壳会创建多个线程,干扰脱壳。可以在OD中暂停所有线程,只保留主线程。
  • IAT加密:有些壳会对IAT进行加密,导致ImportREC无法修复。这时需要手动分析IAT的结构,或者使用更高级的修复工具。

我曾经遇到一个壳,它把IAT的地址给加密了,ImportREC怎么都修复不了。后来我手动跟踪了壳的解密代码,找到了真正的IAT地址,才修复成功。所以,遇到问题不要怕,多动动手,总能找到解决办法。

好了,这一章的内容就到这里。压缩壳的通用脱壳方法,你掌握了吗?