Skip to content

VMP 去虚拟化工具链(Devirtualization) ​

场景 ​

已按 02-vmp-virtualization-analysis 定位 VM entry 与 dispatcher,但 handler 数量多、mutation 重,纯手工还原不可行。需要用自动化工具把"handler 序列"还原为高层指令/IR。本文给出三级降级策略与配套外部开源工具链,并明确工具归属(ReverseLab 已集成 vs 需自行安装)。

输入信号 ​

  • 已确认 VMP 2.x/3.x 且拿到 VM entry 地址表
  • 需要还原语义的函数是纯函数(输入输出可枚举、无系统调用副作用)——这是 DSE 路线的必要条件
  • 被保护函数含调用外部 API(call handler)→ 静态 devirt 可能失败,需动态 trace 兜底

三级降级策略总览 ​

第 1 级: 静态 devirtualization (NoVmp/NoVmpy + VTIL)
         快, 适合 VMP 2.x/3.0-3.5 x64; 新版 VMP 3.8+ 大概率失败 → 降级
第 2 级: 符号执行 DSE (Triton / angr)
         以 VM entry 寄存器为符号输入跑解释器, 求解后化简为 LLVM IR/表达式
         适合纯函数; 性能开销大
第 3 级: trace 语义折叠 (bochscpu/PIN + 模式匹配, VMHunt 思路)
         记录 VM 循环指令 trace, 按 handler 指纹折叠为高层指令
         适合任何函数, 但人工校对量大

每一级失败立即降级到下一级,不要在一级上死磕。

第 1 级:静态 devirt(NoVmp / NoVmpy) ​

外部工具:以下工具不在 ReverseLab 工具箱内,需自行编译/安装。star 数据为 2026-08 GitHub 快照,适用性随 VMP 版本变化,落地前先在样本上验证。

工具URL语言功能维护状态
NoVmpgithub.com/can1357/NoVmpC++VTIL 驱动 VMP 3.x x64 静态 devirt2021 后停更;对 VMP 3.8+ 大概率失效
NoVmpygithub.com/wallds/NoVmpyPythonNoVmp 的 Python 移植(含 VTIL 绑定)2025 仍有提交
VTIL-Coregithub.com/vtil-project/VTIL-CoreC++虚拟化翻译 IR + 优化/反混淆后端(NoVmp 依赖)活跃(2026-07)
Tritongithub.com/JonathanSalwan/TritonC++DBA 符号执行库(第 2 级核心)活跃
VMProtect-devirtualizationgithub.com/JonathanSalwan/VMProtect-devirtualizationC++/LLVMTriton DSE + LLVM 自动去虚拟化纯函数2022 停更,参考实现
VMHuntgithub.com/s3team/VMHuntC++PIN trace + 符号执行 + 语义验证(CCS'18)2018 停更,思路参考
ScyllaHide-IDA7.5github.com/notify-bibi/ScyllaHide-IDA7.5C++反反调试插件(前置步骤)2021 停更

NoVmp 运行流水线(x64 样本、Visual Studio 构建):

1. git clone --recursive https://github.com/can1357/NoVmp
2. VS 打开 NoVmp.sln 编译 (Release x64)
3. NoVmp.exe <sample.exe> <function_rva>
   → 输出 <sample>.devirt.exe + <sample>.devirt.ll (LLVM IR)
4. 用 Ghidra/IDA 加载 devirt 产物, 或直接读 .ll 验证语义
5. 失败特征: 输出与原始行为不符 / 崩溃 / 空 IR → 降级到第 2 级

第 2 级:符号执行 DSE(Triton) ​

原理:以 VM entry 处寄存器为符号值,让 VMP 解释器在符号引擎中执行,handler 展开的表达式被求解并化简,输出"输入寄存器 → 输出寄存器"的语义公式。参考 JonathanSalwan/VMProtect-devirtualization 的流程(Triton 脚本 → LLVM IR → 验证)。

Python 骨架(Triton 有 Python 绑定,但未列入 MCP 安装 allowlist:PyPI 的 triton 包名与 OpenAI GPU 编译器冲突,需要时从源码构建,或用 angr 作为 DSE 替代):

python
# 思路骨架: 用 Triton 仿真 VM entry 到 VM exit, 提取符号表达式
from triton import TritonContext, ARCH, Instruction, MODE
ctx = TritonContext()
ctx.setArchitecture(ARCH.X86_64)
ctx.setMode(MODE.ONLY_SYMBOLIZE_INDEXED_MEMORY, True)

# 1. 把样本加载到模拟内存(可用 unicorn 先 dump 运行态内存)
# 2. 在 VM entry 处开始, 通用寄存器符号化:
#    for reg in ctx.getParentRegisters(): ctx.symbolizeRegister(reg)
# 3. 单步执行到 VM exit (结束地址 = entry 的 ret 目标):
#    while ctx.getConcreteRegisterValue(ctx.registers.rip) != exit_addr:
#        insn = Instruction(bytes=ctx.getConcreteMemoryAreaValue(pc, 15), address=pc)
#        ctx.processing(insn)
# 4. 对目标寄存器取表达式并化简:
#    expr = ctx.getSymbolicExpression(ctx.getSymbolicRegister(ctx.registers.rax)).getAst()
#    print(ctx.getAstContext().unroll(expr))   # 化简后的语义公式

关键约束:只对纯函数做 DSE(无系统调用、无内存副作用依赖);VM context 在 3.x 中进出被变换,符号化起点选在 dispatcher 入口而非函数入口更稳。执行前先过反调试(ScyllaHide / patch 自检)。

第 3 级:trace 语义折叠(VMHunt 思路) ​

1. bochscpu (x64dbg 插件, 外部工具) 或 PIN 记录 VM 循环内全部指令 trace
   → 得到 (内存地址, 指令字节) 序列
2. 按 handler 边界切分 trace: dispatcher 跳转目标即 handler 起点
3. 对每个 handler 片段做符号执行/模式匹配, 得到该 handler 的语义
   (mov x, imm / add r, c / jmp label / call api ...)
4. 折叠: 把连续 handler 序列翻译为高层指令流, 人工校对
5. 验证: 同一输入分别跑原始样本与还原代码, 比较输出 (见下)

VMHunt 的语义验证思路可直接复用:还原代码与原始样本在全部测试向量上输出一致才算还原成功。

结果验证(输入输出等价性) ​

无论哪一级还原,都必须做等价性验证:

python
# 伪代码: 对比原始与还原结果的输入输出对
test_vectors = [v1, v2, v3]          # 覆盖边界值/随机值
for v in test_vectors:
    orig_out = run_original(v)       # x64dbg/独立进程调用被保护函数
    dev_out   = run_devirt(v)        # 运行还原后的函数/IR
    assert orig_out == dev_out, f"mismatch on {v}"
print("devirtualization verified")

验证失败 = 还原不完整(漏 handler 语义、VM key 解错、字节码错位),回到对应级别修正。

攻击链 ​

样本 + VM entry 地址表 (来自 02 篇)
→ 前置: ScyllaHide/bochscpu 过自检
→ 第 1 级 NoVmp: 静态 devirt → 成功? → 等价性验证 → 完成
→ 失败 → 第 2 级 Triton DSE (纯函数) → LLVM IR → 验证
→ 失败 → 第 3 级 trace 折叠 → 人工校对 → 验证
→ 还原结果回填 Ghidra: 给被保护函数重命名/重定义, 继续常规静态分析

MCP 工具映射 ​

AI Agent 可调用以下 MCP 工具自动完成或加速上述攻击链步骤:

攻击链步骤MCP 工具说明
安装 angr/unicorn Python 库python_re_tool_installallowlist 安装逆向库(angr/unicorn/capstone 等),供 DSE/仿真脚本使用;Triton 未纳入 allowlist(PyPI 包名冲突),需源码构建
分析 devirt 产物ghidra_headless_analyze对 .devirt.exe/还原后样本做无头分析导出 summary
读取还原目标函数反编译ghidra_summary_function_detail按地址/函数名读取 callers/callees/decompile 核对还原结果
从运行态 dump 中 carve 还原 payloadcarve_payloads_from_dump从 dump 自动提取 PE 供后续分析

说明:NoVmp/VTIL/Triton C++/bochscpu 等外部工具未集成进 ReverseLab MCP;如需自动化,先人工安装验证工具可用性,再考虑封装为 MCP 工具(工具测试闭环见 scripts/misc/lab_healthcheck.py)。

证据与验证闭环 ​

  • 记录样本 SHA256、VMP 版本、被还原函数 RVA、所用工具与版本(NoVmp/VTIL/Triton commit hash)。
  • 每一级的结果绑定:输入 RVA、输出文件路径(.devirt.exe/.ll/trace 文件)、失败模式与降级原因。
  • 等价性验证必须记录测试向量集合、原始输出、还原输出、断言结果。
  • 工具安装/测试记录:命令、版本输出、样本上是否生效;未验证的工具明确标注"未测试"。
  • 全部产物(devirt 输出、trace、验证脚本)保存到 exports/windows/。

GPL-3.0 · 仅供授权环境下的学习与防御性研究使用