Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
tvm-recover — Devirtualizes Tencent VM .tvm0 sections: lifts VM handlers to guest-level listings and LLVM IR, recovers CFG and dispatch, and patches recovered code back into the PE. | Kitploit
工具/GitHubGitHub/neurosaki987/tvm-recover
Static AnalysisCode AnalysisDynamic Code Analysis (DAST)Reverse EngineeringDebuggersBinary AnalysisPapers & ResearchBinary Exploitation
GitHubneurosaki987/tvm-recover

tvm-recover

Devirtualizes Tencent VM .tvm0 sections: lifts VM handlers to guest-level listings and LLVM IR, recovers CFG and dispatch, and patches recovered code back into the PE.

1510小时6分前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
查看仓库

tvm-recover

DOI

关联 Zenodo 记录:10.5281/zenodo.23474767。

⚠️ Disclaimer: This project is intended strictly for authorized security research, interoperability analysis, and education. It does not include or provide any bypass functionality for commercial anti-cheat systems. Users are solely responsible for ensuring they have the legal right to analyze the target binaries.

面向 Tencent VM 的虚拟化还原工具。从 .tvm0 段恢复虚拟化代码,提取每个 VM handler 对 guest 寄存器和内存做了什么,并能把还原出的函数写回二进制,作为一个普通的只读可执行段。

它是工具:给它一个模块,拿回结构和代码。

.tvm0 段  ->  VM 语义  ->  guest 级 listing  ->  LLVM IR  ->  打过补丁的 PE

某个 VM root 的还原后 listing,guest 寄存器组在跨块绑定后的形式。

虚拟化还原效果图

构建

python -m venv .venv
source .venv/Scripts/activate      # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements.txt

Python 3.13、capstone。后端另需 llvmlite==0.50.0,Windows 上还需 MSVC link.exe/cl.exe。z3-solver 可选 —— 没有它时路径条件化仍在,只跳过有界 dispatch 证明。

配置

把 .env.example 复制为 .env,填上本机路径。.env 已 gitignore;每个配置项也都有命令行参数,文件只是提供默认值。

键含义
TVM_MODULE分析目标,供不带位置参数的 probe 使用
TVM_LISTINGS后端读取的 listing 目录
TVM_OUT所有输出的根目录(默认 out/)
TVM_STEPS每块步数上限,触顶即截断(默认 900000)
TVM_BLOCKS每 root 块预算(默认 512)
TVM_PAYLOAD_OPTpayload codegen 级别 0–3
TVM_PAYLOAD_MEMsealed | absolute guest 内存模式
TVM_MSVC_BIN存放 link.exe/cl.exe 的目录,免去依赖 PATH

优先级:命令行参数 → .env → 环境变量 → 内置默认值。

用法

引擎命令在 engine/ 下执行;后端命令在仓库根目录执行。

找虚拟化代码

cd engine
python -m probe.scan_tvm0 /path/to/dir        # 哪些文件带 .tvm0,以及它的 root
python -m probe.trampoline_forms input.exe    # 入口跳板长什么样
python -m probe.mnemonic_census input.exe     # VM 里包含哪些 x86

还原

python -m core.devirt input.exe --out ../out/listings

每个函数产出一个 0x<root>.txt listing 和一个 0x<root>.guest-state.json。选项:

选项默认含义
--out=DIRstdout写出 listing 与 guest state
--root=0x…全部发现的只处理一个 VM 入口
--show[=N]关打印前 N 行
--steps=N900000每块步数上限,触顶即截断
--blocks=N512每 root 块预算
--caller-entry=0x… --call-site=0x…关对入口做 caller 特化

每个 listing 的头部会写明这次运行是否被截断;截断的 listing 是下界,不是完整函数。

理解单个函数

python -m probe.cfg_run         input.exe 0x14015fc2e    # 走一遍,打印 CFG
python -m probe.guest_effect    input.exe 0x14015fc2e    # handler 对 guest 寄存器的效果
python -m probe.ctx_map         input.exe 0x14015fc2e    # 哪个 context slot 存哪个 guest 寄存器

python -m probe.why_unresolved  input.exe 0x14015fc2e    # 某个 dispatch 为什么没解出
python -m probe.block_exits     input.exe 0x14015fc2e    # 某个块为什么没有后继
python -m probe.stop_debug      input.exe 0x14015fc2e    # 求值为什么停下
python -m probe.unresolved_detail input.exe 0x14015fc2e  # 逐块细节

追到具体某个值或某次写入:

python -m probe.trace_window    input.exe 0x14015fc2e 0x14015fc40 0x14015fc90
python -m probe.reg_history     input.exe 0x14015fc2e rax
python -m probe.who_wrote       input.exe 0x14015fc2e 0x18
python -m probe.flags_at        input.exe 0x14015fc2e 0x14015fc60
python -m probe.stack_at        input.exe 0x14015fc2e

写回二进制

.\write-devirt.ps1 -Module 'C:\path\to\SGuardSvc64.exe'

依次跑 payload → 注入 → 加载 → 差分,遇到第一个失败就停下。选项:-Listings、-Out、-Opt 0..3、-Mem sealed|absolute、-SkipDifferential、-Force。

也可以逐个阶段单独跑:

cd llvm
python payload.py  --listings=../out/hard-cases-listings-final --out=../out/write-devirt --opt=0 --mem=sealed
python peinject.py --host=input.exe --payload=../out/write-devirt --out=../out/write-devirt/input.neko.exe
python miniload.py ../out/write-devirt/input.neko.exe
python diff_payload.py --payload=../out/write-devirt --patched=../out/write-devirt/input.neko.exe

真正有意义的是 diff_payload.py:它运行打过补丁的镜像里的代码,与解释器在同一 listing 上逐字节比对。miniload.py 只证明镜像能加载、入口能跑。

VM 是怎么工作的

ACE 用一个跳板替换原生函数,跳入 VM dispatcher。dispatcher 执行的是原生 x86 handler,但被保护函数的体系结构状态存放在 VM context 中。

flowchart TB
    subgraph TOP[ ]
        direction LR

        subgraph TEXT[.text]
            ENTRY["<b>函数入口</b><br/>跳转到 VM entry"]
        end

        subgraph VM[.tvm0 段]
            direction LR
            CORE["<b>共享 dispatcher 与 handlers</b><br/>执行 x86 - 解析下一个 handler"]
            CTX["<b>Guest context</b><br/>GPRs - RFLAGS - VIP"]
            CORE <--> CTX
        end
    end

    subgraph NATIVE[VM 之外的原生执行]
        direction LR
        BOXED["<b>Boxed 指令</b><br/>执行原始 x86"]
        CALL["<b>Guest 调用</b><br/>调用原生目标"]
        EXIT["<b>Guest 返回或尾调用</b><br/>恢复原生执行"]
        BOXED ~~~ CALL ~~~ EXIT
    end

    TEXT --> VM
    VM <--> NATIVE

    style TOP fill:transparent,stroke:transparent

部分操作在 VM 之外执行。Boxed 指令在 VM 恢复它所需的寄存器后执行原始 x86 编码,再把效果写回 guest context。Guest 调用调用一个原生目标,返回时把返回值与 Windows x64 ABI 的 clobber 体现在 guest 状态里。Guest 返回与尾调用则完全离开 dispatcher。

context 包含全部十六个 guest 通用寄存器(含 guest RSP),以及打包的 guest 标志位。它的布局随保护版本变化,因此是通过观察 VM 的保存/恢复行为定位的,而不是假设固定地址 —— probe.ctx_map 报告在 store 处实测的映射,且每个输出的 listing 都会给 context 访问标注 slot 偏移。

条件控制流通过 dispatch 算术实现:handler 取出一个 guest 标志或谓词,用它选出下一个虚拟程序计数器,再算出下一个 handler 的地址。还原器符号化地求值这一过程,并在所选路径依赖未解析 guest 值时分裂求值状态。一个块的标识是 (handler, vip) 这一对,而不只是 handler。

整个还原建立在一条规则上:按机器实际做了什么来识别值,而不是按分析给它的名字。 跨块寄存器传播在 listing 中以注解形式携带,绝不作为证据 —— 把 listing 里每个寄存器版本号任意置换后,解析出的结构必须不变。

结构

write-devirt.ps1   一条命令:payload -> 打过补丁的 PE -> 差分
.env.example       配置模板;复制为 .env
envfile.py         .env 解析;bootenv.py 供 llvm/ 加载,engine/envboot.py 供 probe 加载
engine/core/       PE 解析、表达式 arena、x86-64 lifting、机器模型、
                   guest context、CFG 恢复、guest IR、guest-state 程序
engine/probe/      28 个 probe —— 每个回答一个关于目标的问题
engine/tests/      30 个套件,全部基于合成 fixture 或 Python 独立算术对照
llvm/              listing 文法、IR -> LLVM IR、解释器、差分、
                   payload 构建、PE/PDB 编辑、注入、加载
llvm/runtime/      payload 调用的代码:密封内存、入口桩、__chkstk
out/               工具输出(已 gitignore)

范围

guest context 的布局是通过测量得出的,不是假设的:probe.ctx_map 报告在 store 处实测的 slot 与寄存器对应关系。一个块的标识是 (handler, vip) 这一对,而不只是 handler。

关于输出不声称什么:listing 是 VM 做过什么的记录,不是反编译结果。未解析的 dispatch 会被如实报告为未解析,而不是猜一个。截断的运行会在文件头标记。--caller-entry 产出的是以那个调用者为前提的结果,payload 拒绝把它提升为通用还原。

Thanks

基于 capstone 做反汇编,基于 llvmlite 做 LLVM 后端。

下载工具