脱壳:压缩壳(UPX/ASPack/FSG)

SkillDev tools

压缩壳脱壳:UPX/ASPack/FSG。触发词:脱壳、unpack、UPX、esp定律、OEP

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the 脱壳:压缩壳(UPX/ASPack/FSG) skill

What this skill tells your AI

The instructions your AI receives, as published by dslsdzc/rev-skills in .claude/skills/re-unpack-simple/SKILL.md and read by ahel’s review.

何时使用 / 何时不用

  • 用:[[re-packer-id]] 识别为简单压缩壳(UPX/ASPack/FSG 等);壳入口有 pushad/pusha 特征;需要干净样本继续静态/动态分析
  • 用:未知壳手动流程的第一步尝试(ESP 定律对多数压缩壳有效)
  • 不用:强壳 / 虚拟化壳(VMProtect/Themida,转 [[re-unpack-advanced]])
  • 不用:目标已确认无壳(直接 [[re-binary-core]])
  • 不用:需要观察带壳行为本身(行为分析有时刻意保留壳,与 [[re-behavior]] 配合时评估)
  • 注意:脱壳产物验证必须在沙箱([[re-sandbox]],[[re-analyze/platform-tips]] 最高原则)

工具准备

upx(官方解包主力)

  • Linux: apt install upx-ucl / dnf install upx / pacman -S upx
  • macOS: brew install upx
  • Windows: 官方 GitHub release upx/upx 下载 win64 zip(或 choco install upx
  • WSL: Linux 包
  • 验证: upx --version

调试器(按 OS)

  • Linux / Wine 下调试 PE: [[re-gdb]] —— apt install gdb / dnf install gdb / pacman -S gdb,验证 gdb --versionWine 直读wine sample.exe 运行后 gdb -p <pid> attach 直接操作 Wine 进程(见 [[re-analyze/platform-tips]] Linux 分支)
  • Windows: [[re-x64dbg]] —— 官方 release zip,验证:载入样本能单步
  • macOS: PE 脱壳场景走 Wine + [[re-gdb]] 或 Windows VM,不用本机调试器
  • WSL: 无法 attach Windows 进程,跨边界走 Windows 侧工具([[re-analyze/platform-tips]] WSL 分支)

Scylla(Windows IAT 修复)

  • x64dbg 新版官方 release 已内置(插件目录 plugins/ 下有 Scylla)
  • 独立版: GitHub NtQuery/Scylla release
  • Wine 环境:独立版 Scylla 可 attach 到 Wine 进程做 dump 修复(失败则在 Windows 环境完成 IAT 修复)
  • 验证: x64dbg 插件菜单出现 Scylla,能 Attach 到进程并列出模块

操作步骤

按顺序执行,每步记录结果(证据路径 + sha256,见 [[re-triage]]);动手前先按 [[re-triage]] 记录脱壳基线五件套(SHA256 / PE timestamp / EP RVA / section hash / import table),脱壳后逐项对照再判定成败。

  1. 优先官方 / 自动解包(upx -d

    upx -d sample.exe -o unpacked.exe
    file unpacked.exe     # 应显示正常 PE 结构;仍报 UPX / 报错 → 失败
    sha256sum unpacked.exe
    

    upx -d 失败常见原因:文件被修改过(自校验 / 二次加壳)、UPX 版本不兼容。失败则走步骤 2 手动流程。

  2. ESP 定律找 OEP(设断在 pushad 后):

    • 原理:压缩壳入口 pushad 保存寄存器 → 壳解密完毕后 popadjmp OEP对 pushad 之后的 ESP 所指地址下硬件访问断点,运行后在 popad 后的第一条指令附近找 OEP。
    • 本质(技巧,不是脱壳公式):入口 pushad 压栈是壳"保存现场"的必然行为,ESP 定律利用它定位"恢复现场"点——popad 恢复后即解密完成、控制流将跳真实代码。它只在"入口压栈保护现场、尾部恢复后跳转"这一实现下成立,并非对一切壳都有效:TLS 回调壳(回调先于 EP 执行,EP 处无压栈现场)、多线程壳(多线程各自压栈、断点命中的栈地址错位)、VMProtect/Themida 等虚拟化壳(入口直接进 VM dispatcher,无 pushad/popad 周期)都不适用。先确认 EP 入口确实是 pushad/popad 周期再用,否则直接走步骤 3 或转 [[re-unpack-advanced]]。
    • x64dbg(Windows):载入 → 停在 EP → 单步见 pushad → 再单步执行 pushad → 记录 pushad 执行后的 ESP 值 → 右键 ESP 寄存器 → Hardware Breakpoint(on access)→ 运行 → 断在 popad 后第一条指令 → 下面几行 jmp 的目标即 OEP。记下 OEP 地址。
    • gdb(Linux/Wine,见 [[re-analyze/platform-tips]] Wine 直读):
      (gdb) starti                    # 停在 EP
      (gdb) si                        # 单步执行 pushad(x86 pushad 压 8 个寄存器)
      (gdb) awatch *(int*)$esp        # 对 pushad 后的 ESP 所指地址下硬件观察点(访问即断)
      (gdb) continue                  # 运行,popad 访问该地址时触发
      
    • OEP 特征:一段函数序言(push ebp; mov ebp,esp),其后紧跟大量正常 API 引用。
  3. 内存断点法(VirtualAlloc 断点)

    • 适用:ESP 定律失效(无 pushad、多 stub、多阶段解密)。
    • 思路(对解密区域设执行监控):壳把解密完成的真实代码写入某区域(解压目标节 / VirtualAlloc 分配区),对该区域设执行/访问监控,壳控制流第一次落到解密代码时断下——即解密完成后的真实代码入口,不需要猜"OEP 在哪个地址"。
    • "第一次执行即 OEP"不成立:部分壳先跳 loader(壳自身运行库)、多阶段解密(第一次执行到的仍是中间层,后面还有解密循环)、或布置 fake OEP(伪装函数序言引诱过早转储)——断下后必须验证目标具备真实代码特征(函数序言 push ebp; mov ebp,esp + 后续密集正常 API 引用),多阶段壳重复下断直到控制流稳定离开壳代码,再进入步骤 4。
    • x64dbg:bp VirtualAlloc → 每次返回后看分配区域是否被写入可执行内容(壳的解压目标节)→ 对该区域下内存访问断点(View > Memory Map > 目标节 > Set breakpoint on access)→ 运行到壳完成解密处。也可直接对壳第二节(UPX1/.aspack)下内存断点。
    • gdb(Wine):break *VirtualAlloc,返回后查看分配区,再对分配地址下 watch。
  4. 转储 OEP(时机:解密完成后,见 [[re-analyze/platform-tips]] 关键经验;默认转储优先):

    • Linux/Wine:运行到 OEP(解密完成)后默认转储 gcore -o out <pid>(完整流程见 [[re-memdump]];转储前按 maps 过滤 vsyscall/vdso)。
    • Windows:x64dbg 运行到 OEP → Plugins > Scylla > Attach to process → 填 OEP 地址(步骤 2 记录值)→ Dump。先记镜像基址 + OEP 偏移。
    • 别在壳解密完成前 dump——拿到的是壳的初始状态(见坑 1)。
  5. IAT 修复(Scylla/ImpREC)

    • x64dbg: Plugins > Scylla > Attach to process → OEP 填步骤 2 的地址 → IAT AutosearchGet Imports(核对 API 解析正确)→ Fix Dump 输出 unpacked_fixed.exe
    • 替代:ImpREC(Windows,老工具)同样流程:Open Process → IAT AutoSearch → Fix Dump。
    • 修复后验证:file unpacked_fixed.exe → 导入 [[re-ghidra]] / [[re-ida]] 反编译确认导入表可解析 → 沙箱内复跑([[re-sandbox]])确认行为与脱壳前一致、无自校验触发。

跨域联合

  • [[re-anti-analysis]]:工作流第 3 步(简单壳分支)固定调用本技能
  • [[re-analyze]] 的 triage「样本带壳 / 脱壳」路径调用(re-anti-analysis → re-unpack-simple)
  • [[re-malware]]:恶意样本加壳(UPX 等)脱壳后回沙箱复跑再行为分析
  • [[re-mobile]] / [[re-apk]]:部分 Android 加固壳可类比本流程思路(具体加固壳按 [[re-frida]] / [[re-memdump]] 内存 DEX 提取)
  • [[re-crypto-decrypt]] / [[re-crypto-keys]]:解密 / 密钥在壳内时先脱壳(OEP 后再 dump,见 [[re-memdump]] 转储时机)
  • 配套:[[re-memdump]](默认转储)、[[re-gdb]] / [[re-x64dbg]](调试器)、[[re-sandbox]](脱壳产物复跑验证)

常见坑与陷阱

  • 转储过早 = 壳初始状态:现象——dump 后 file 仍报 UPX、反编译只见压缩数据;原因——壳未运行到 OEP 解密完成就转储;对策——解密完成后再 dump([[re-analyze/platform-tips]] 关键经验),默认转储优先
  • IAT 不修 → 导入表乱:现象——脱壳样本反编译里全是 GetProcAddress 动态调用、导入表解析失败;原因——导入表由壳在运行时重建,转储未修复;对策——步骤 5 Scylla/ImpREC 修复,修复后重新反编译验证
  • 自校验(CRC)→ 脱壳后需补丁:现象——脱壳样本沙箱运行即退出 / 弹校验失败;原因——程序比对自身字节(原壳时是压缩数据,脱壳后对不上);对策——定位校验代码(xref 自身映像基址 / CRC 计算函数)patch 跳过或 hook,验证在沙箱内做
  • Wine 环境用错调试器:现象——Windows 调试器在 Wine 下 attach 失败 / 断点不生效;原因——Wine 是 Linux 进程,需 Linux 调试路径;对策——按 [[re-analyze/platform-tips]] Wine 直读:gdb -p attach Wine 进程读内存、下断点、gcore 转储
  • 双层压缩壳 → ESP 定律要用两次:现象——ESP 定律断下后进入的仍是解包层,dump 出来还是壳代码,upx -d 解一层后 file 仍报壳特征;原因——壳套壳(如 ASPack v2.12 第一层解完还有一层在解密 IAT);对策——第一层落地后继续观察:出现又一次 pushad/popad 周期、或 VirtualFree 释放 IAT 解密堆时才是最后一步,跟到最终 jmp 并验证目标是真实代码序言(push ebp; mov ebp,esp + 密集正常 API 引用)再 dump,别在过渡 jmp 前早一跳
  • 按 FF 25 搜 IAT 不可靠:现象——按教程二进制搜索 FF 25(间接 jmp)找 IAT 起址,找不到或找到错位置;原因——不是所有程序都经间接跳转调 API(直接 CALL [addr] 很常见);对策——用调试器 Find > All intermodular calls 定位一个真实程序调用点,顺 CALL/JMP [addr] 跟到跳转表顶端计算 IAT 起址与块大小,再填 ImportREC/Scylla
  • dump 后 PE 头字段未修 → 加载失败:现象——IAT 修复完样本仍打不开 / "invalid Win32 application" / 加载即崩;原因——转储工具没修 SizeOfImage/NumberOfSections/CheckSum,或 Win7+ 上 LordPE/ImportREC 因 ASLR 失败;对策——Fix Dump 后仍异常就用 pefile 手工核对修正头三字段,必要时关闭随机基址后重新转储
  • 转储后崩溃 ≠ 脱壳失败:现象——IAT 修复完成、反编译也正常,运行仍崩溃/闪退,误判脱壳失败而放弃;原因——除 IAT 外还依赖其他机制:TLS callback(壳在回调里重设参数/解密数据)、重定位表(ASLR 下基址重定位未处理)、异常表(SEH 链被壳重排)、Load Config(安全 cookie / GS 检查指向壳数据);对策——按 [[re-format-pe]] 逐项核对 TLS 回调表、重定位目录、异常目录、Load Config 目录,缺哪项修哪项;脱壳成功判据=代码/导入/重定位/异常机制完整(见 [[re-unpack-advanced]]),不是"不崩溃"或"修完 IAT"
  • ESP 定律当公式硬套:现象——对 TLS 回调壳 / 多线程壳 / VMProtect 套 ESP 定律,硬件断点不触发或停在错位栈地址,反复试数小时无果;原因——把"入口压栈找恢复点"当通用公式,实际只适用于入口 pushad 的单线程压缩壳;对策——动手前确认适用前提(EP 入口确为 pushad/popad 周期),否则直接走步骤 3 内存断点法 / 转 [[re-unpack-advanced]],别在公式上耗时间
  • 把"第一次执行解密区域"当 OEP 直接转储:现象——内存断点第一次触发就 dump,拿到的是壳 loader / fake OEP,反编译仍是壳代码;原因——壳先跳 loader、多阶段解密或布置 fake OEP,第一次执行≠解密完成;对策——断下后先验证真实代码特征(函数序言 + 密集 API 引用),多阶段壳重复下断直到控制流稳定
  • Wine 脱壳失败未必是流程错:现象——Wine 下按完整流程操作,断点不触发 / 壳运行异常 / 解密结果错乱;原因——Wine 环境问题而非流程问题:缺失壳依赖的 API、壳驱动组件无法加载、反虚拟化检测(Wine 特征 API/注册表/进程名)或环境检测(系统版本/硬件特征)使壳拒绝正常解密;对策——先定位失败点:WINEDEBUG=+loaddll 看加载失败日志、断 GetVersionEx/注册表读取看是否命中环境检测;确认是 Wine 环境限制后果断转 Windows VM([[re-sandbox]])完成脱壳,不在 Wine 里反复折腾

Signals

GitHub stars
57
Forks
8
Last commit
Sep 2026

ahel review

  • K1binfo
    installs-packages

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
re-unpack-simple
Source
github.com/dslsdzc/rev-skills