yaps 是一个 C++20 研究原型,用来实现 PUSHAN 风格的 VPC-sensitive
去虚拟化流程。它读取 x86-64 PE,使用 Ghidra SLEIGH p-code 解码原生指令,
恢复以 (native_addr, vpc) 为节点身份的 flat CFG,再把它简化成 p-code CFG,
最后输出可供人工检查的 pseudo-C。
本项目是 100% AI-generated under human direction,并且仍然处于 heavy
development。它是一个快速推进中的研究实现,并不是成熟产品,也不声称通用解决
所有 VM 混淆。
PUSHAN 是这个仓库最直接的参照对象:trace-free CFG 恢复、VPC-sensitive 节点、 轻约束符号模拟、语义保持简化,以及最终的 C-like 输出。这个仓库不是 PUSHAN 官方实现,而是在公开论文描述和本地实验基础上,对其中可验证部分进行独立重建。
原始论文地址:https://arxiv.org/html/2603.18355v1
当前目标比论文级叙事更窄,也更明确:
- 把 PUSHAN 风格恢复流程做成可以运行、可以检查的工程;
- 把手工 VPC 配置作为一等工作流;
- 从虚拟化样本中恢复有用的 flat CFG 和 p-code CFG;
- 明确暴露限制,同时继续改进实现。
对 PUSHAN 论文 claim 和评测叙事的技术性批评放在 docs/pushan-critique.md。README 只描述项目本身。
当前实现主要关注:
- Windows x86-64 PE 输入;
- 以 Tigress 风格虚拟化样本作为主要公开目标;
- 手工提供 VPC/VIP 事实,并在有帮助时提供 bytecode 区域事实;
- 基于 Ghidra SLEIGH 的指令提升;
- 寄存器和内存上的抽象状态传播;
- VPC-sensitive flat CFG 恢复;
- p-code CFG 简化;
- 从简化后的 p-code CFG 输出 pseudo-C。
手动定位 VPC 不是临时行为。自动 VPC 识别当然是有价值的未来工作,但手工事实是最可靠 的复现实验方式,并且会长期保留。
本仓库目前不声称已经通用支持 VMProtect 或 Themida 去虚拟化,也不声称已经能生成 高质量 C。现在的 pseudo-C 是真实的流水线输出,其中仍然有明显噪声,正好说明变量恢复、 结构化和简化仍然可以继续发展。
当前主打公开样本是 samples/tigress/fortigress.c。 它比单条 hash 链更复杂:
- 4 个输入 word;
- 双层循环;
- rolling state;
- 5 路分支选择;
- loop body 内还有额外条件分支;
- 最后有硬等式判断。
在当前 laptop 级 Windows 测试环境中,ifnest 恢复大致在 30 秒量级。这个数字仅仅只是 当前的工作点,不是正式 benchmark;当前环境也不是多核 benchmark 机器。
推荐环境是 Windows、Visual Studio generator 和 clang-cl。
需要准备:
- 初始化
third_party/sleigh、third_party/spdlog、third_party/json、third_party/LIEF、third_party/zlib子模块; - 通过
YAPS_GHIDRA_ROOT指向本地 Ghidra checkout; - 通过
YAPS_ABSEIL_ROOT指向本地 Abseil C++ checkout; - 通过
YAPS_Z3_ROOT指向本地 Z3 包,或将 Z3 解压到third_party/z3; - CMake 3.24 或更新版本。
初始化子模块:
git submodule update --init --recursive配置和构建 release preset:
cmake --preset windows-clangcl-release `
-DYAPS_GHIDRA_ROOT=F:/github/ghidra `
-DYAPS_ABSEIL_ROOT=F:/github/abseil-cpp `
-DYAPS_Z3_ROOT=F:/github/yaps/third_party/z3
cmake --build --preset windows-clangcl-releasepreset 里的路径只是本地开发默认值。你的 Ghidra、Abseil、Z3 位置不同就覆盖这些 CMake 变量。
运行 release 构建目录中的测试:
ctest --test-dir build/windows-clangcl-release -C RelWithDebInfo --output-on-failureDebug preset 也可用:
cmake --preset windows-clangcl-debug
cmake --build --preset windows-clangcl-debug
ctest --preset windows-clangcl-debug二进制分析模式:
build/windows-clangcl-release/RelWithDebInfo/yaps_cli.exe `
--binary samples/tigress_ifnest/fortigress_ifnest.exe `
--config samples/tigress_ifnest/fortigress_ifnest_config.json `
--function-rva 0x14bf `
--out-json out/ifnest_stage1.json `
--out-dot out/ifnest_stage1.dot `
--out-pcode-json out/ifnest_stage2.json `
--out-pcode-dot out/ifnest_stage2.dot `
--out-pseudo-c out/ifnest_stage3.c `
--timeout-ms 300000 `
--log-level info--function-rva 和 --function-va 必须二选一。
同一条命令也已保存到
samples/tigress_ifnest/command.ps1。
常用输出参数:
--out-json:flat CFG JSON;--out-dot:flat CFG DOT;--out-pcode-json:简化后的 p-code CFG JSON;--out-pcode-dot:简化后的 p-code CFG DOT;--out-pseudo-c:从 p-code CFG 生成 pseudo-C;--transform-dump-dir:输出 transform pass 中间结果;--transform-dump-before:包含 pass 前状态;--transform-dump-after-all:每个 pass 后都 dump,而不是只 dump 有变化的 pass。
也可以从已有 p-code CFG JSON 直接生成 pseudo-C:
build/windows-clangcl-release/RelWithDebInfo/yaps_cli.exe `
--in-pcode-json out/stage2.json `
--out-pseudo-c out/stage3.c最小 register VPC 配置:
{
"target_arch": "x86_64",
"vpc": {
"mode": "manual",
"storage": "register",
"register": "r15",
"width": 8
},
"vip": {
"mode": "same_as_vpc"
},
"entry_context": {
"initial_vpc": "0x0"
}
}bytecode 是可选字段。省略时 yaps 使用空 bytecode 区域,也就是 base = 0、
size = 0。当 VM bytecode 读取需要被当成已知常量传播时,提供该区域仍然有用。
类似当前 ifnest 运行的 memory-at-register VPC 配置:
{
"target_arch": "x86_64",
"vpc": {
"mode": "manual",
"storage": "memory_at_register",
"base_register": "rbp",
"displacement": "-0x58",
"width": 8
},
"vip": {
"mode": "same_as_vpc"
},
"bytecode": {
"base": "0x140019040",
"size": "0x5bf"
},
"entry_context": {
"initial_vpc": "0x140019040",
"registers": {
"rsp": "0x5ffed8",
"rcx": 5,
"rdx": "0x11000000"
}
}
}当前支持的 VPC storage:
register;memory;memory_at_register。
VIP 可以是 same_as_vpc,也可以是手工位置、从 VPC 派生出的简单值,或者手工表达式。
已测试的配置形态可参考 tests/config_test.cpp。
entry_context 可以设置初始 VPC、寄存器和部分内存。warmup 可用于在正式
VPC-sensitive 恢复前先执行一段 native prefix。
公开样本源码和部分选定产物在 samples/:
- samples/tigress_ifnest/:当前公开 ifnest 样例,包含生成 二进制、配置、命令行、原始 pseudo-C,以及一份手工去噪摘录;
- samples/tigress/fortigress.c:当前 ifnest 风格 Tigress 目标;
- samples/tigress/yaps_sample.c:更小的 Tigress 样本;
- samples/manual_vm/vm_run_sample.c:手工 VM 实验。
另有一个纯 C switch-VM fixture: tests/fixtures/switch_vm_fixture.c,说明见 tests/fixtures/switch_vm_fixture.md。
本仓库中非子模块代码当前临时使用 AGPL-3.0-only。这个选择主要是因为项目仍不完整, 并且还在快速变化中。等实现、测试和公开评测叙事更加成熟后,计划切换到更宽松的 许可证,例如 Apache-2.0。
Git 子模块和其他第三方依赖仍然遵循它们各自的许可证。
这是一个仍在活跃开发中的研究代码库。flat CFG 恢复已经足以支撑真实实验,但项目 会刻意避免过度 claim:
- 不声称通用去虚拟化;
- 不声称 pseudo-C 已达到最终质量;
- 不声称自动 VPC 识别已经解决;
- 不声称广泛支持商业虚拟化器。
近期重点是扩大被测试的 VM 形态,继续改进 p-code CFG 简化,并在不隐藏真实输出的 前提下让 pseudo-C 少一些噪声。