devirt-dev/devirt-core

GitHub: devirt-dev/devirt-core

一款以编译器方式构建的通用、与样本无关的 JavaScript 反混淆器,通过不动点算法进行语义保持的 AST 重写来恢复代码可读性。

Stars: 1 | Forks: 0

# devirt-core [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/29b0ff9727015316.svg)](https://github.com/devirt-dev/devirt-core/actions/workflows/ci.yml) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license) [![Release](https://img.shields.io/github/v/release/devirt-dev/devirt-core)](https://github.com/devirt-dev/devirt-core/releases) **一个通用的、与样本无关的 JavaScript 反混淆器,以编译器的形式构建。** 它使用 [oxc](https://github.com/oxc-project/oxc) 解析输入,运行保持语义的 AST 重写的 不动点算法,并打印出可读的 JavaScript。**没有针对特定样本的 规则** —— 每一趟处理都是一个通用转换,并且输出经验证 保持了可观察的行为。 ``` deob obfuscated.js > readable.js ``` ## 安装 针对 Linux、macOS 和 Windows(x86_64 + arm64)的预编译 `deob` 二进制文件已附在 每个[发布版本](https://github.com/devirt-dev/devirt-core/releases)中。 ``` # 无需 Rust toolchain — 下载预编译的 binary: cargo binstall --git https://github.com/devirt-dev/devirt-core devirt-cli # …或者从 source 构建并安装(需要 Rust toolchain): cargo install --git https://github.com/devirt-dev/devirt-core devirt-cli ``` 这两种方式都会将 `deob` 二进制文件放到你的 `PATH` 中。你也可以从发布页面 下载归档文件,并手动将 `deob` 放到你的 `PATH` 中的某个位置。 ## 用法 ``` deob # deobfuscate, print readable JS to stdout deob --format # reprint with NO transforms ("before" side of a diff) ``` 统计信息和任何错误都会输出到 **stderr**,因此 stdout 保持纯净,便于管道传输: ``` deob in.js > out.js # just the code diff <(deob --format in.js) <(deob in.js) # see only real changes ``` `--format` 共享反混淆器完全相同的格式化工具,因此与正常输出进行 diff 对比时,只会显示真正的反混淆变化,而没有格式化带来的干扰。 ## 库 该反混淆器也是一个普通的 Rust crate,你可以将其嵌入: ``` use devirt_core::{deobfuscate, format_only}; let report = deobfuscate(source, "input.js"); // filename only steers source-type inference println!("{}", report.code); ``` `deobfuscate` 返回一个 `Report`,其中包含转换后的 `code`、解析错误、 不动点统计数据,以及一个 `error` 字段(如果流水线失败,会设置该字段,而不是直接 panic)。 `format_only` 仅进行重新打印而不做任何转换,用于 diff 对比中的“修改前”部分。 ## 工作原理 流水线按顺序运行每一趟处理,并不断重复,直到一整轮下来没有任何变化(不动点)。这些处理趟位于 `crates/core/src/passes/` 目录中,并在 `default_pipeline()`(`crates/core/src/passes/mod.rs`)中注册。它们分为以下几个组: - **语法标准化器**(成员访问、序列拆分),用于暴露结构。 - **数据流核心**(常量折叠、内联、死存储和死代码消除),在各轮处理中不断累积效果。 - **控制流恢复**(switch 分支反平坦化、CFG 重建)。 - **解码器处理** —— 一个 [Boa](https://github.com/boa-dev/boa) 沙盒用于 执行字符串解码器函数,以便将其结果提升到源码中。 - **重命名**,通过推断出的角色分配有意义的名称。 控制流恢复使用基于 SSA 的 IR(`crates/core/src/ir/`),结合支配树分析和 relooper,从扁平化的调度器中重建结构化代码。 ### 健壮性 反混淆器在对抗性输入上运行深层机制(oxc 遍历、SSA IR、Boa 沙盒),因此我们遏制了两种导致宿主崩溃的途径: - 流水线在一个具有大型显式栈(`crates/core/src/util.rs`)的专用工作线程上运行。Boa 的递归解析器和 VM 极度依赖本地栈,而栈溢出会导致直接中止(abort)而不是展开(unwind),因此深度上限被设置得很大,并且独立于调用方。 - Panic 会被捕获:内部 panic(oxc 的 bug,或者 Boa 的 `i32::MIN % -1` 溢出)会降级为工作线程的 `join()` 返回 `Err`,而 `deobfuscate` 会回退到返回经过干净重新格式化的输入,同时设置 `Report::error`。调用方永远不会看到崩溃。 ## 仓库布局 这是一个 Cargo workspace,在 `crates/` 目录下包含三个 crate: | Crate | 路径 | 提供 | |---|---|---| | `devirt-core` | `crates/core` | 库(`devirt_core`):处理趟、SSA IR、沙盒 | | `devirt-cli` | `crates/cli` | `deob` 命令行前端 | | `devirt-report` | `crates/report` | `report` 语料库评分 / 等价性检查工具(维护者工具) | ## 开发 ``` cargo build --release # build the whole workspace cargo nextest run --release # full test suite (what CI runs) cargo test --release # same, without nextest cargo clippy --release --all-targets -- -D warnings # lint gate ``` 测试套件是自包含的:每一个测试夹具(fixture)都是嵌入在 Rust 测试中的内联 JS 代码片段,因此不需要拉取任何语料库,并且它仅关注正确性。请参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 了解如何添加新的处理趟,以及 [SECURITY.md](SECURITY.md) 了解威胁模型和如何报告漏洞。 ## 语料库 混淆 JS 的语料库位于**此仓库之外**,存放在一个 Hugging Face 数据集中 ([`devirt-dev/devirt-corpus`](https://huggingface.co/datasets/devirt-dev/devirt-corpus))。 HF 数据集是普通的 git 仓库,因此不需要额外的工具;`./samples` 目录被 gitignore 忽略。实时的可读性评分以及可浏览的输入→输出对照表已发布在[数据集页面](https://huggingface.co/datasets/devirt-dev/devirt-corpus)上。 ``` scripts/pull-corpus.sh # clone/update ./samples from the dataset CORPUS_REV= scripts/pull-corpus.sh # pin a revision for reproducible scores cargo run --release --bin report # per-source readability scoreboard cargo run --release --bin report -- --equiv # differential behavioral-equivalence gate cargo run --release --bin report -- --json samples/metrics.jsonl --markdown # JSONL + card ``` `--equiv` 是针对可读性评分的健全性检查对应项:它在反混淆前后通过全程序测试工具(`sandbox::behavior_signature`)运行每个样本,并要求签名完全一致(终端结果 + console 输出序列),一旦发现不匹配就会以非零状态码退出。对于那些原本就无法进行可运行性对比的样本(超时,或者测试工具无法构建宿主环境),将被直接跳过。 该报告按来源对样本进行分组(`generated`、`real/npm`、`real/tranco`、`real/httparchive`),并显示 `kept%` 以及**opaque% in→out** —— 即处理前后具有机器生成特征标识符的比例。在处理真实的*压缩*(minified)输入时,`kept%` 会超过 100%,因为引擎会对其进行重新格式化/展开;体现可读性提升的是 opaque-% 的下降,而不是字节数。 ### 扩充语料库 ``` node scripts/make-seeds.mjs --count 200 # varied plain seeds node scripts/gen-corpus.mjs # obfuscate them under ~21 profiles node scripts/gen-corpus.mjs --profiles strong,controlflow # …or a subset ``` 真实世界的样本来自三个来源(npm / Tranco 抓取 / HTTP Archive), 每一个都使用相同的方式进行过滤和去重 —— 参见 [`scripts/real/README.md`](scripts/real/README.md)。生成样本需要在本地安装 `javascript-obfuscator`(`npm i javascript-obfuscator`);它仅作为本地工具使用, 绝不是 crate 的一部分。 ## 许可证 根据以下任一许可证进行授权: - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)) - MIT license ([LICENSE-MIT](LICENSE-MIT)) 由你选择。 除非你明确声明,否则任何由你提交以包含在本作品中的有意贡献,根据 Apache-2.0 许可证的定义,均应按上述方式获得双重许可,无需任何额外的条款或条件。
标签:CMS安全, JavaScript, Rust, 代码反混淆, 可视化界面, 编译器, 网络流量审计, 通知系统