Skip to content

壳

1. 什么是壳 (Packer)?

壳是在原始的可执行文件(如 EXE, ELF)上加的一层特殊代码。

  • 物理层面: 它通过压缩、加密等手段改变原程序的代码段和数据段。

  • 逻辑层面: 程序运行时,操作系统首先加载的是“壳”,由壳负责在内存中将真正的程序还原(解压/解密),然后再跳转到原程序的入口点(OEP)执行。

核心作用:

  1. 压缩: 减小文件体积,方便传输。

  2. 保护 (防逆向): 隐藏程序真实的逻辑,防止被静态分析工具(如 IDA)直接读取,对抗调试器。


2. 壳的分类

根据加壳的目的,通常分为两大类:

A. 压缩壳 (Packer)

主要目的是减小体积,通常不具备很强的对抗性。

  • UPX: 最经典的开源壳,支持多种平台(Windows, Linux 等)。

  • ASPack: Windows 平台上经典的压缩壳。

B. 加密壳/保护壳 (Protector)

主要目的是对抗反汇编、对抗调试和虚拟机检测。

  • VMProtect (VMP): 目前顶级的壳,将代码转换为自定义的虚拟机指令,极难逆向。

  • Themida: 著名的强保护壳,拥有极强的反调试和内核级保护。

  • Enigma Protector: 常用于商业软件保护。


3. 重要概念:OEP 与 Dump

  • OEP (Original Entry Point): 原程序入口点。这是脱壳的目标——找到程序在没加壳时的第一个执行地址。

  • Dump (内存转储): 当壳把程序在内存中还原后,我们将这段内存数据抓取并保存为文件的过程。


4. 如何脱壳 (Unpacking)

脱壳通常分为 自动脱壳 和 手动脱壳 (ESP 定律)。

步骤一:查壳

在开始之前,先确定是什么壳。

  • 工具: Die (Detect It Easy), PEID, 或 Linux 下的 file 命令。

步骤二:脱壳操作

1. 自动脱壳 (针对简单壳)

如果是 UPX 等常见压缩壳,直接使用自带工具即可:

upx -d filename  # -d 代表 decompress

2. 手动脱壳 (针对复杂或魔改壳)

这是 CTF 逆向的基本功,通常遵循以下流程:

  1. 寻找 OEP: 使用调试器(x64dbg 或 GDB)跟踪,直到找到壳跳转回原程序的那个“大跳转”。

  2. Dump 内存: 在 OEP 处停下,将内存镜像导出。

  3. 修复 IAT (导入地址表): 加壳程序往往会破坏导入表,需要使用工具(如 Scylla)重新关联 DLL 函数。

3. ESP 定律

这是手动脱壳最常用的技巧,适用于大多数压缩壳。

  • 原理: 壳在运行开始时通常会执行 PUSHAD(将所有寄存器压栈)以保存环境,运行结束准备跳回原程序时会执行 POPAD(出栈)。

  • 操作:

    1. 在程序刚启动后的第一个 PUSH 动作后,观察 ESP 寄存器。

    2. 对该 ESP 指向的地址下 硬件访问断点 (Hardware Breakpoint)。

    3. 当程序运行触发该断点时,通常就在壳代码的末尾,附近必然有一个跳转通往 OEP。

强壳(如 ASProtect, Armadillo)会利用 SEH 来对抗单步追踪。

  • 核心逻辑: 壳作者故意在代码里写一些会引发崩溃的指令(比如访问非法内存、抛出特定中断),以此来阻断调试器的单步跟踪。同时,壳会自己偷偷修改 SEH 链,把异常处理函数(Handler)指向它自己写的下一段解密代码。

  • 运行轨迹: 触发异常 -> 调试器或系统接管 -> 移交给壳注册的 SEH Handler -> 壳在 Handler 中继续解密 -> 再次触发异常...

  • 如何利用:

    1. 在调试器中,取消对所有异常的拦截(Shift+F9,让异常直接抛给程序自己处理)。

    2. 观察程序一共会抛出多少次异常才跑飞(或运行起来)。假设是 5 次。

    3. 重新加载程序,按 Shift+F9 忽略前 4 次异常。

    4. 在第 5 次(最后一次)异常发生时停下。此时壳已经接近完成解压。

    5. 查看当前的 SEH 链(在栈中或者调试器的 SEH 窗口),找到当前接管异常的 Handler 地址。

    6. 在这个 Handler 地址处下断点,跟进去单步走。因为这是最后一次异常,处理完之后,程序通常就会恢复寄存器并 JMP 到 OEP。


5. 练习

  1. 从 UPX 开始: 自己写个 C 语言的 Hello World,用 upx 加壳,然后尝试用 x64dbg 手动找到 OEP,不要只依赖命令脱壳。

  2. 掌握工具链:

    • Windows: x64dbg, Scylla, Die.

    • Linux: GDB (配合插件 pwndbg 或 gef).

  3. 识别特征码: 多看各种壳的开头汇编代码,久了一眼就能看出这是什么壳。