15、加壳与脱壳(上):什么是加壳、常见壳的分类

各位同学,今天我们来聊聊逆向工程里一个绕不开的话题——加壳与脱壳。说实话,我刚开始学逆向那会儿,最头疼的就是碰到加壳的程序。你辛辛苦苦分析半天,结果发现代码全是乱的,入口点也找不到,那种挫败感,嗯,我懂。

但别急,这恰恰是逆向的魅力所在。加壳就像给程序穿了一件“铠甲”,而我们要做的,就是把这件铠甲脱下来。今天这堂课,我们先从基础讲起,把“壳”这个东西彻底搞明白。

什么是加壳?

加壳,说白了就是给原始程序包上一层“外衣”。这层外衣负责在程序运行时,先把原始代码解压或解密到内存中,然后再跳转到真正的入口点去执行。

你想想看,一个正常的PE文件,它的入口点(OEP)是直接指向程序代码的。但加壳之后,入口点变成了壳代码的入口。壳代码先运行,完成解压或解密工作,最后才把控制权交给原始程序。

核心概念:加壳后的程序,入口点 ≠ 原始OEP。壳代码的入口叫“壳入口”,原始程序的入口叫“原始OEP”。脱壳的目标,就是找到这个原始OEP。

我在项目中遇到过不少恶意样本,它们特别喜欢用加壳来逃避杀软检测。有一次分析一个勒索软件,它用了三层壳,每一层都做了反调试。嗯,那确实是个难忘的夜晚。

常见壳的分类

壳的种类很多,但大致可以分为三类:压缩壳、加密壳、以及变态级的VMProtect。我们一个一个来看。

1. 压缩壳

压缩壳的目的很简单——减小程序体积。它就像给程序打了个zip包,运行时再解压。这类壳通常不加密,只是压缩,所以脱壳相对容易。

壳名称 特点 脱壳难度
UPX 最经典的压缩壳,开源,支持多种格式 ★☆☆☆☆
ASPack Windows平台常见,压缩率高 ★★☆☆☆
MPRESS 压缩比高,支持多平台 ★★☆☆☆

我个人习惯,拿到一个未知样本,先用UPX的-d参数试试能不能直接脱。如果能,那省大事了。如果不能,再考虑其他方法。

2. 加密壳

加密壳比压缩壳狠多了。它不仅压缩,还会对原始代码进行加密,甚至修改PE结构,让分析工具无法正常识别。这类壳的主要目的是防止逆向分析。

壳名称 特点 脱壳难度
ASProtect 早期很流行,有反调试、反dump机制 ★★★☆☆
Armadillo 支持多层保护,有CopyMem-II等高级特性 ★★★★☆
Enigma Protector 现代加密壳,支持虚拟化、反调试 ★★★★☆

为什么加密壳更难脱?因为它会在内存中动态解密代码,而且解密后的代码可能只存在一瞬间,你还没来得及dump,它又加密回去了。我曾经调试一个Armadillo加壳的程序,花了整整两天才找到正确的dump时机。

3. VMProtect

VMProtect是壳中的“天花板”。它不压缩也不加密,而是把原始代码翻译成一种虚拟机的字节码,然后用自己的虚拟机解释执行。你看到的代码全是VM指令,根本看不懂。

注意:VMProtect脱壳几乎不可能。通常的做法是分析VM虚拟机的行为,或者通过trace记录执行路径,还原出原始逻辑。这已经属于高级逆向的范畴了。

说实话,碰到VMProtect,我一般会先评估一下值不值得脱。如果只是分析一个功能点,可能用hook或者动态跟踪更划算。

查壳工具

在动手脱壳之前,第一步是查壳。你得知道对面穿的是什么铠甲,才能决定用什么武器。这里介绍两个我常用的工具。

PEiD

PEiD是老牌查壳工具,虽然已经很久不更新了,但对付经典壳依然好用。它通过特征码匹配来识别壳的类型。

使用方法:
1. 打开PEiD
2. 把目标程序拖进去
3. 看右下角显示的壳信息

示例输出:
"UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo"
"ASPack 2.12 -> Alexey Solodovnikov"

PEiD的缺点也很明显——对新壳基本无能为力。而且它容易被伪造的特征码欺骗。我记得有一次,一个样本故意在PE文件里插入了UPX的签名,PEiD直接报UPX,结果我脱了半天才发现根本不是。

Detect It Easy (DIE)

DIE是PEiD的现代替代品,目前还在活跃更新。它支持多种检测方式,包括特征码、熵值分析、结构分析等。

DIE的优势:
- 支持更多壳类型
- 可以显示熵值(高熵值通常表示加密或压缩)
- 支持插件扩展
- 界面更友好

使用技巧:
- 查看"Entropy"标签,如果熵值接近8.0,说明代码被加密或压缩过
- 查看"Versions"标签,可以看到可能的壳版本

我个人现在主要用DIE,偶尔用PEiD做交叉验证。两个工具配合使用,基本能覆盖90%以上的壳。

手动寻找OEP:ESP定律法

查完壳,接下来就是脱壳了。手动脱壳的第一步,就是找到原始OEP。这里介绍一个经典方法——ESP定律法。

ESP定律的原理很简单:壳代码在跳转到原始OEP之前,一定会把原始程序的上下文环境(寄存器状态)恢复好。而ESP寄存器(栈指针)在壳代码执行过程中,通常会被用来保存原始程序的栈环境。

具体操作步骤:

  1. 用调试器(如x64dbg)加载加壳程序
  2. 程序会在壳入口处断下(通常是pushad指令)
  3. 记录当前ESP的值
  4. 在内存窗口中,对ESP地址下硬件访问断点(Hardware Breakpoint)
  5. 运行程序,断点会在壳代码访问ESP时触发
  6. 继续运行,直到断点停在一条跳转指令附近
  7. 这条跳转指令的目标地址,就是原始OEP

小技巧:很多壳在跳转到OEP之前,会执行一条"popad"指令恢复寄存器。如果你看到popad,紧跟其后的跳转大概率就是去OEP的。

为什么ESP定律好用?因为壳代码无论如何伪装,它总得在某个时刻把栈环境恢复成原始程序的样子。这个“恢复”的动作,必然涉及对ESP的操作。我们只要抓住这个操作,就能找到OEP。

我曾经用ESP定律脱过一个加了三层壳的样本。第一层是UPX,第二层是ASPack,第三层是自定义的简单加密。每一层都用ESP定律找到了OEP,然后dump出来,再修复IAT。嗯,虽然过程繁琐,但思路清晰,最后成功还原了原始程序。

知识体系图

下面这张图总结了本章的核心知识结构,你可以对照着梳理一下思路。

加壳与脱壳(上)知识体系 加壳与脱壳 什么是加壳? 常见壳分类 查壳与脱壳 壳代码先执行,再跳转OEP 压缩壳(UPX等) 加密壳(ASProtect等) VMProtect(虚拟机壳) PEiD(老牌查壳) Detect It Easy(现代) ESP定律法找OEP 核心思路:查壳 → 找OEP → Dump → 修复IAT ESP定律是手动找OEP最经典的方法,适用于大多数压缩壳和部分加密壳 脱壳是逆向的基本功,多练才能熟能生巧

小结

好了,今天的内容就到这里。我们讲了加壳的本质、常见壳的分类、查壳工具的使用,以及ESP定律法找OEP。这些都是脱壳的基础,也是你后续学习更高级脱壳技术的基石。

说实话,脱壳这件事,理论讲再多都不如动手练一次。我建议你找个UPX加壳的小程序,用ESP定律法亲手脱一次。第一次可能会有点懵,但多试几次,你就会发现其中的规律。

嗯,记住一点:脱壳不是目的,分析原始代码才是。别为了脱壳而脱壳,搞清楚程序到底在干什么,才是我们逆向工程师的本职工作。


公众号:蓝海资料掘金营,微信 deep3321