Files

6.8 KiB
Raw Permalink Blame History

MANUAL — 通用脱壳前置分析 SOP (Unpacking & Deobfuscation)

本文件适用于 G0/G1 阶段识别出加壳保护后的标准化处置流程。 覆盖:MPRESS / UPX / Themida / VMP 3.x / .NET Reactor / 自定义混淆。 原则:能动态 dump 就不静态逆壳,优先内存 Dump 重建,效率最高。


一、 壳类型速查与工具路由

壳特征(triage 输出信号) 壳类型 首选脱壳路线
MPRESS1 / MPRESS2 节区节名 MPRESS 等待 OEP 后内存 dump(Scylla)
UPX0 / UPX1 节名,入口在末节 UPX upx -d target.exe 直接命令行解压
themida/.Boot 节名,大量无效导入 Themida/WinLicense OEP 断点 + Scylla IAT Rebuild
.vmp0 / .vmp1 节名,VM bytecode VMP 3.x 内存 dump + 动态 trace 提取算法(见第四节)
熵值 > 7.2(整个文件),无可读字符串 自定义壳 / 强加密 rabin2 entropy + x64dbg 手动定位 OEP
.text 区段全 0、代码在 .rsrc 内 资源型 Loader 提取资源区段 → 分析 Loader 落盘路径
_CorExeMain 导入(.NET) .NET 壳 de4dot + dnSpy 优先,再看 VMP 字节码

二、 通用 OEP 定位 + 内存 Dump 流程(适用所有用户态壳)

步骤一:定位 OEP(原始入口点)

策略 A(ESP 定律 — 通用于 MPRESS/UPX/简单 PE 壳):
1. 在 x64dbg 中运行程序,在首个用户态断点暂停
2. 在 ESP(栈顶指针)处设置内存写入断点(Hardware BP on Write)
3. 运行至断点触发,此时 EIP 即为解压后的 OEP
4. 验证:OEP 处通常为 "push ebp; mov ebp, esp" 或 .NET CRT init 代码

策略 B(节区执行监控 — 通用于 Themida/自定义壳):
1. 用 x64dbg 打开,在"内存布局"中对原始代码节区(.text)设置执行断点
2. 运行程序,壳在向 .text 写完解密代码后会 jmp 到 OEP,此时断点触发
3. 记录此时的 EIP 地址

策略 C(API 断点反向追踪 — 通用于所有壳):
1. 在 GetCommandLineA / GetModuleHandleA 等 CRT 初始化必调 API 上设断点
2. 程序调用此 API 时已完成壳初始化,此时在调用栈中回溯到 OEP

步骤二:内存 Dump 与 IAT 重建

使用 Scylla(x64dbg 内置插件):

1. 在 OEP 处暂停后打开 Scylla(Plugins → Scylla)
2. OEP 自动填入,点击 "IAT Autosearch" 扫描导入表
3. 点击 "Get Imports" 获取完整 IAT
4. 对所有标记为无效(红色)的条目,点击 "Fix Dump" 尝试自动修复
5. 点击 "Dump" 导出内存映像
6. 点击 "Fix Dump" 将 IAT 写入 Dump 文件
7. 用 CFF Explorer 或 PE-bear 验证修复后的 PE 头完整性

三、 UPX 脱壳(最简单,一行命令)

# 直接解压(破坏性操作,先备份!)
Copy-Item target.exe target_backup.exe
upx -d target.exe

# 验证解压成功
.\Tool\mcp\Tool\safe\radare2\bin\rabin2.exe -I target.exe | Select-String "class|bits|type"

若 UPX 头被抹除(修改魔数以对抗 upx -d):

# 修复被抹除的 UPX 魔数
with open("target.exe", "r+b") as f:
    data = f.read()
    # 定位 UPX 压缩数据特征并还原魔数
    idx = data.find(b"\x55\x58\x50\x00")  # UPX! 魔数附近
    if idx > 0:
        f.seek(idx - 4)
        f.write(b"UPX!")
print("Header patched, retry: upx -d target.exe")

四、 VMP 3.x 脱壳与算法提取(最复杂,动态 trace 优先)

VMP 将原始 x86/x64 指令编译为自定义 VM 字节码,不可能静态完整还原。 正确策略:不试图还原 VM 本身,而是在 VM 执行过程中 trace 算法结果。

4.1 G0 阶段止损判定

seep_auto_triage 检测到 .vmp0/.vmp1 节名后,Agent 应立即输出:
  "⚠️ 目标存在 VMP 3.x 级虚拟机保护,超出全自动符号反编译范畴。
   将切换为动态 trace 策略提取关键算法输出,而非还原完整伪代码。"

4.2 动态 Trace 策略(提取算法的输入/输出)

// Frida 方案:监控 VMP 保护函数的入口参数与出口返回值
// 需先通过 auto_triage 确定被 VMP 保护的关键导出函数 RVA
const baseAddr = Process.enumerateModules()[0].base;
const vmProtectedFunc = baseAddr.add(0xDEAD00);  // 替换为实际 RVA

Interceptor.attach(vmProtectedFunc, {
  onEnter(args) {
    console.log("[VMP] Func entered, args:");
    for (let i = 0; i < 4; i++) {
      try { console.log(`  arg[${i}] = ${args[i]} = "${args[i].readUtf8String()}"`); }
      catch(e) { console.log(`  arg[${i}] = ${args[i]}`); }
    }
  },
  onLeave(retval) {
    console.log(`[VMP] Func returned: ${retval} (0x${retval.toString(16)})`);
  }
});

4.3 Unidbg 模拟执行(无需真机,黑盒调用)

# 适用于 Android SO 中的 VMP 保护(Java_* 导出函数)
# 在 Unidbg 中直接调用目标 JNI 函数,忽略内部 VM 实现,只捕获结果
java -jar unidbg-android.jar \
  --lib target.so \
  --func Java_com_example_MainActivity_verifyLicense \
  --args "test_user_id,TEST-SERIAL-1234"

五、 .NET 壳脱壳(.NET Reactor / ConfuserEx / Eazfuscator)

5.1 通用流程

# 第一步:用 de4dot 自动识别并去混淆
.\de4dot.exe target.exe -o target_clean.exe

# 第二步:用 dnSpy 打开清理后的程序集检查
# 若 de4dot 无法识别,尝试:
.\de4dot.exe --dont-rename target.exe -o target_clean.exe

5.2 运行时 dump(对抗字符串/资源加密)

// Frida 方案:在 .NET CLR System.Reflection.Assembly.Load 时捕获解密后的程序集
const Load = Module.getExportByName('clr.dll', '_CorExeMain');
// 等待 CLR 完成解密,dump AppDomain 中加载的所有程序集
Process.enumerateModules().forEach(m => {
  if (m.name.endsWith('.dll') || m.name.endsWith('.exe')) {
    console.log(`[CLR Module] ${m.name} @ ${m.base} size=${m.size}`);
    // 用 Memory.readByteArray 导出并 PE-fix
  }
});

六、 脱壳后完整性验证

# 快速验证脱壳后 PE 是否可正常分析
$r2 = ".\Tool\mcp\Tool\safe\radare2\bin\rabin2.exe"

# 检查 PE 头完整性
& $r2 -I target_unpacked.exe | Select-String "class|type|bits|os|subsys"

# 检查导入表是否正常恢复
& $r2 -i target_unpacked.exe | head -20

# 检查节区属性(脱壳后 .text 应为 RX)
& $r2 -S target_unpacked.exe

七、 两击熔断纪律 —— 脱壳的止损规则

情形 触发 处置
ESP 定律 2 次均找不到 OEP 壳可能有 TLS 回调或多层包装 改用节区执行监控策略
节区执行监控 2 次失败 壳对内存断点有检测 改用 API 断点反向追踪
所有 Dump 策略失败 可能有内核级保护 声明"当前工具链无法完全脱壳",切换 Unidbg Emulation 提取算法关键结果