coleleavitt/js-beautify-rs

GitHub: coleleavitt/js-beautify-rs

这是一个基于 Rust 和 oxc 构建的高性能 JavaScript 美化与去混淆工具,专门用于处理大型生产环境代码包及复杂的混淆攻击载荷。

Stars: 3 | Forks: 1

# js-beautify-rs [![Crates.io](https://img.shields.io/crates/v/js-beautify-rs.svg)](https://crates.io/crates/js-beautify-rs) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.md) 一个使用 Rust 编写的快速 JavaScript 美化器和去混淆器,由 [oxc](https://github.com/oxc-project/oxc) 提供支持。 接收经过压缩、混淆的 webpack/esbuild/Bun bundle 并输出可读的 JavaScript。 能够处理真实的生产环境 bundle —— 已针对 11MB+ 的构建文件进行测试。 ## 安装 ``` cargo install js-beautify-rs ``` 或者从源代码构建: ``` git clone https://github.com/coleleavitt/js-beautify-rs cd js-beautify-rs cargo build --release # binary 位于 ./target/release/jsbeautify ``` ## 快速开始 ``` # 美经混淆的 JavaScript jsbeautify input.js -o output.js # 美经 + 去混淆 (20 阶段 AST 管道) jsbeautify input.js --deobfuscate -o output.js # 从 stdin 管道输入 cat bundle.js | jsbeautify - > output.js # 提取 webpack 模块到单独文件 jsbeautify bundle.js --extract-modules --module-dir ./modules # 生成依赖图 (DOT 格式) jsbeautify bundle.js --extract-modules --dependency-graph deps.dot # 跨版本对齐,附带 sourcemap 名称恢复 jsbeautify v1.js --sourcemap v1.js.map --align-with v2.js --align-output v2.aligned.js -o v1.aligned.js # 从 Bun bundles 中提取名称 (MR exports, this.name, displayName) jsbeautify bundle.js --bun-extract --sourcemap bundle.js.map -o output.js ``` ## CLI 参考 ``` Usage: jsbeautify [OPTIONS] Arguments: Input JavaScript file (use "-" for stdin) Options: -o, --output Write output to a file instead of stdout -d, --deobfuscate Enable AST-based deobfuscation (20-phase pipeline) --split-chunks Split webpack chunks into separate files --chunk-dir Directory for chunk output [default: ./chunks] --chunk-map Write chunk metadata to a JSON file --extract-modules Extract webpack modules to separate files --module-dir Directory for module output [default: ./modules] --dependency-graph Generate a dependency graph in DOT format --source-maps Generate source maps --sourcemap Sourcemap for extracting original variable names --names-json Name mappings from extract-names.ts --align-with Second bundle to align with (produces stable diffs) --align-output Output path for the aligned second bundle --raw Skip beautification, output raw aligned code --bun-extract Extract names from Bun bundle patterns --indent-size Indentation size in spaces [default: 4] --indent-with-tabs Use tabs for indentation instead of spaces -h, --help Print help -V, --version Print version ``` ## 库用法 js-beautify-rs 也可以作为 Rust 库使用: ``` use js_beautify_rs::{beautify, Options}; let code = "function test(){console.log('hello');}"; let options = Options::default(); let beautified = beautify(code, &options).expect("beautification failed"); ``` 用于去混淆: ``` use js_beautify_rs::AstDeobfuscator; let obfuscated = std::fs::read_to_string("bundle.js").unwrap(); let mut deobfuscator = AstDeobfuscator::new(); let clean = deobfuscator.deobfuscate(&obfuscated).unwrap(); ``` ## 去混淆流水线 `--deobfuscate` 标志会运行一个 **阶段 0 预处理器**,随后是一个 **20 阶段的 AST 转换流水线**。每个阶段的结果会输入到下一个阶段 —— 顺序很重要。 ### 阶段 0:加密 Eval 解密(Pre-AST) 在 AST 解析之前,jsbeautify 会检测并解密钓鱼工具包(Tycoon2FA 及类似工具)使用的一种特定混淆模式: ``` // Input: Encrypted eval pattern var data = "NjFiMjZkZDA6MTcwNDcy:SGVsbG8gV29ybGQh..."; // Base64 with PRNG seed var chars = ['\x23e64','\x23v05','\x23a0B','\x23l2C']; // Steganographic "eval" // ... PRNG XOR + Caesar cipher decryption logic ... window[chars.map(c => c[1]).join('')](decrypted); // eval(decrypted) // Output: Decrypted code directly in source console.log("Hello World!"); ``` 移除的加密层: 1. Base64 解码,带有冒号分隔的 PRNG 种子和计数器 2. 通过自定义 PRNG(基于种子)进行 XOR 密钥流处理 3. 变量移位凯撒密码(每个字符移位值为 1-25) 4. 用于 `eval` 调用的颜色十六进制隐写术 该模式是从一次实时的钓鱼攻击活动中逆向工程得出的。解密后的载荷会原位替换整个加密块,然后继续进行 AST 处理。 ### AST 转换阶段(1-20) | 阶段 | 传递 | 作用 | |------:|------|-------------| | 1 | 控制流平坦化还原 | 从基于 switch 的状态机中重构原始控制流 | | 2 | 字符串数组旋转 | 检测并应用数组旋转(shift/push IIFE 模式) | | 3 | 解码器 / 字符串数组 / 分发器内联 | 解析解码器函数(base64, RC4, XOR, offset)并内联字符串值 | | 4 | 调用代理内联 | 检测单次使用的包装函数并内联其目标 | | 5 | 运算符代理内联 | 解析包装二元运算符的代理函数 | | 6 | 表达式简化 | 括号转点符号、`!0`->`true`、`void 0`->`undefined`、常量折叠、代数简化、强度削减 | | 7 | 死代码消除 | 移除 `if(false)`、`while(false)`、return/throw 之后不可达的代码 | | 8 | 死变量消除 | 移除从未被读取的变量 | | 9 | 函数内联 | 内联具有简单主体的单次使用函数 | | 10 | 数组解包 / 动态属性 / 三元 / try-catch | 解析常量数组索引,简化常量三元表达式,移除空的 catch 块 | | 11 | Unicode / 布尔值 / void / 对象稀疏化 | 标准化 unicode 标识符、布尔字面量、void 表达式、稀疏对象模式 | | 12 | 变量重命名 | 将十六进制编码的标识符(`_0x1a2b`)重命名为可读名称 | | 13 | 空语句清理 | 移除先前传递中残留的空语句 | | 14 | 序列表达式拆分 | 将逗号表达式(`a(), b(), c()`)拆分为单独的语句 | | 15 | 多变量拆分 | 将 `let a=1, b=2, c=3` 拆分为单独的声明 | | 16 | 三元转 if/else | 将独立的三元表达式语句转换为 if/else 块 | | 17 | 短路转 if | 将独立的 `a && b()` / `a || b()` 转换为 if 语句 | | 18 | IIFE 展包 | 将零参数箭头 IIFE 展包为内联语句 | | 19 | esbuild 辅助函数检测 | 识别 `__commonJS`、`__esm`、`__export`、`__toESM`、`__toCommonJS` 运行时辅助函数 | | 20 | 模块标注 | 使用注释分隔符标记 webpack、esbuild 和 Bun 模块边界 | ## 跨版本对齐 通过恢复原始名称并在版本之间匹配语句,在压缩 bundle 的不同版本之间生成稳定的 diff: ``` # 使用 sourcemap 名称恢复对齐两个 bundle 版本 jsbeautify v2.1.88.js \ --sourcemap v2.1.88.js.map \ --align-with v2.1.96.js \ --align-output v2.1.96.aligned.js \ -o v2.1.88.aligned.js ``` 三层规范命名: 1. **Sourcemap 名称** —— 从 `.map` 文件中恢复的原始标识符(36,000+ 个名称) 2. **基于槽的名称** —— 基于 Bun 字母表派生的 `sN` 名称,用于未映射的标识符 3. **基于哈希的名称** —— 用于不匹配代码的 AST 结构哈希后备 `_rN` 结果:94.7% 的语句匹配率,bundle 版本之间 diff 减少 76%。 ## Bun Bundle 支持 从 Bun 特定的 bundle 模式中提取原始名称: ``` jsbeautify bundle.js --bun-extract --sourcemap bundle.js.map -o output.js ``` 检测到的模式: - `MR(target, { exportName: () => minifiedVar })` 导出映射 - 类构造函数中的 `this.name = "ClassName"` - `Component.displayName = "ComponentName"` 赋值 ## 测试 ``` cargo test --lib ``` ## 许可证 [MIT](LICENSE.md)
标签:Bun, CMS安全, ESBuild, JavaScript, oxc, Rust, SourceMap, URL提取, Webpack, WebSocket, 云安全监控, 代码格式化, 代码美化, 代码还原, 依赖分析, 反混淆, 反编译, 可视化界面, 网络流量审计, 自动化payload嵌入, 通知系统, 静态分析