Skip to content

Latest commit

 

History

History
256 lines (195 loc) · 8.26 KB

File metadata and controls

256 lines (195 loc) · 8.26 KB

yaps:重建 PUSHAN 风格的 VPC-sensitive 去虚拟化

English README

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/sleighthird_party/spdlogthird_party/jsonthird_party/LIEFthird_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-release

preset 里的路径只是本地开发默认值。你的 Ghidra、Abseil、Z3 位置不同就覆盖这些 CMake 变量。

运行 release 构建目录中的测试:

ctest --test-dir build/windows-clangcl-release -C RelWithDebInfo --output-on-failure

Debug preset 也可用:

cmake --preset windows-clangcl-debug
cmake --build --preset windows-clangcl-debug
ctest --preset windows-clangcl-debug

CLI

二进制分析模式:

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 = 0size = 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/

另有一个纯 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 少一些噪声。