Verbatama/js-deobfuscator

GitHub: Verbatama/js-deobfuscator

基于 Babel AST 的 Node.js CLI 工具,用于静态还原并简化由 javascript-obfuscator 生成的混淆 JavaScript 代码。

Stars: 0 | Forks: 0

# Patch 1.1.1 — SequenceExpression 轮转修复 此版本修复了对 `javascript-obfuscator` IIFE 轮转器的检测,此前该轮转器位于 top-level 的 `SequenceExpression`(逗号操作符)中,并与 `require("dotenv").config()` 等应用代码并存。现在移除轮转器时,只会删除轮转器节点,并保留其旁边的应用表达式。 新增内容: - 针对 `SequenceExpression` 内部轮转器的回归测试; - `audit-output.js`,用于检测字符串语义被破坏的反混淆结果。 审计示例: ``` node audit-output.js /path/to/deobfuscated-project ``` 一次性反混淆整个项目: ``` node deobfuscate-tree.js --aggressive --audit /path/to/original-project /path/to/recovered-project ``` # JavaScript 混淆器 AST 反混淆器 这是一个基于 Babel AST 的 Node.js 20+ CLI 工具,用于恢复字符串并简化由 `javascript-obfuscator` 生成的源代码。1.1 版本增加了事务性清理和保守模式,以防止输出的结果中包含已被删除但仍被引用的 decoder 或 alias。 本项目不使用正则表达式来替换源代码。所有的搜索、静态评估、节点替换、runtime 移除、constant folding、表达式简化以及 binding 重命名,都是通过 Babel AST 以及 scope 分析来完成的。 ## 功能 - 字符串数组(string array)和字符串数组访问器(accessor)。 - 在 runtime 运行的 Base64/URI 字符串 decoder。 - 执行自我重定义并使用缓存的 decoder。 - 嵌套 alias decoder,例如 `const x = decoder; const y = x;`。 - 采用 `push(shift())` 或 `unshift(pop())` 模式的数组轮转 IIFE。 - 十六进制索引和常量索引表达式。 - 针对一元、二元、逻辑、条件、模板以及索引字面量表达式的 constant folding。 - 简化 `!![]`、静态 computed property、安全的逻辑/条件表达式,并将十六进制数字转换为普通数字。 - 在清理 decoder 后验证悬空的 runtime 引用。 - 如果 runtime 清理不安全,则自动回滚到仅恢复字符串(strings-only)的结果。 - 通过递归检查副作用来清理未使用的 binding。 - 简化逻辑/条件表达式时会保留 `this` 的语义。 - 在 `--aggressive` 模式下使用 Babel 的 scope binding 对混淆的 identifier 进行重命名。 - 使用 `@babel/generator` 进行美化输出。 ## 安装 ``` npm install ``` 环境要求: - Node.js 20 或更高版本。 - 输入必须为可被 Babel 解析的 JavaScript。 ## 用法 默认模式为 `--safe`。指定输入和输出: ``` node deobfuscate.js input.js output.js ``` 显式调用安全模式: ``` node deobfuscate.js --safe input.js output.js ``` 不显式指定输出: ``` node deobfuscate.js file.js ``` 自动生成结果: ``` file_deobfuscated.js ``` ### 转换模式 #### `--safe` — 默认 - 将所有可被静态评估的 decoder 调用替换为字符串字面量。 - 尝试删除 alias、decoder、字符串数组、缓存以及轮转 IIFE。 - 验证是否存在变成悬空引用的 runtime 名称。 - 如果清理不安全,则自动回滚到仅恢复字符串(strings-only)的检查点。 - 不进行变量重命名。 - 不删除未使用的应用 binding。 ``` node deobfuscate.js --safe input.js output.js ``` #### `--aggressive` 执行安全模式的所有步骤,然后重命名 binding 并清理未使用的混淆 identifier。只有在递归检查证明其没有副作用时,才会删除初始化语句(initializer)。 ``` node deobfuscate.js --aggressive input.js output.js ``` 请在应用程序的测试套件可用后再使用此模式。重命名可能会影响诸如 `function.name`、`Function#toString` 结果或堆栈跟踪中出现的名称等可观察行为,尽管业务逻辑通常保持不变。 #### `--keep-runtime` 替换成功恢复的字符串,但保留所有的 runtime decoder 和轮转 IIFE。当需要完全避免 runtime 清理时,此模式最为保守。 ``` node deobfuscate.js --keep-runtime input.js output.js ``` 执行 `npm link` 之后,还可以通过以下方式调用 CLI: ``` js-deobfuscate input.js output.js ``` ## 转换流程 1. 将源代码解析为 Babel AST。 2. 根据 binding、调用点(call site)、数组访问、自我赋值、算术索引以及原始 decode 操作来寻找候选 decoder。 3. 通过 identifier 声明图查找所有的 alias decoder。 4. 仅为 runtime decoder 声明构建依赖闭包(dependency closure)。 5. 涵盖触及这些 runtime 依赖的轮转 IIFE。 6. 验证 runtime AST,并在带有超时机制的 `vm.createContext()` 中运行。 7. 评估所有参数均为静态的 decoder 调用。 8. 将生成的字符串结果作为 `StringLiteral` 直接替换到 AST 中。 9. 在删除 runtime 之前保存仅恢复字符串(strings-only)的检查点。 10. 首先删除 alias,然后删除 decoder、字符串数组、缓存以及轮转 IIFE。 11. 查找悬空的 runtime 引用,并对生成器输出的源代码重新解析。 12. 如果 runtime 清理验证失败,则回滚到检查点。 13. 执行保守的 constant folding 和简化操作。重命名和未使用 binding 的清理仅在 `--aggressive` 模式下执行。 14. 使用 Babel generator 进行美化输出,并再次进行验证。 ## 沙盒 Runtime 仅接受以下 facade: - 带有 no-op 方法的 `console`。 - 受限的 `Buffer`。 - `decodeURIComponent` 和 `encodeURIComponent`。 - `atob` 和 `btoa`。 - `Date`。 - `Math`。 额外保护: - `require`、`process`、`module`、`exports`、`eval`、`Function`、动态 import 以及特定的执行/进程/文件 API 会被 AST 验证器拒绝。 - 禁用通过字符串和 WebAssembly 生成 runtime 代码。 - runtime 执行和每个 decoder 调用都使用超时机制。 - 只有被识别为 decoder 依赖声明和轮转 IIFE 的代码才会被执行;应用语句(application statements)不会参与执行。 ## 示例 输入: ``` const a0_0x2420bf = a0_0x2e8e; function a0_0x4a0b() { const a0_0x1 = ['cGF0aA==', 'bG9n', 'Tm90aWZpZXIgc3RhcnRlZA==']; a0_0x4a0b = function () { return a0_0x1; }; return a0_0x4a0b(); } function a0_0x2e8e(index) { const array = a0_0x4a0b(); index -= 0x124; return Buffer.from(array[index], 'base64').toString('utf8'); } require(a0_0x2420bf(0x124)); console[a0_0x2420bf(0x125)](a0_0x2420bf(0x126)); ``` 预期输出: ``` require("path"); console.log("Notifier started"); ``` ## 结构 ``` deobfuscator/ ├── package.json ├── README.md ├── deobfuscate.js ├── runtime.js ├── evaluator.js ├── passes/ │ ├── replaceStrings.js │ ├── removeDecoder.js │ ├── removeRotate.js │ ├── removeAliases.js │ ├── foldConstants.js │ ├── simplifyExpressions.js │ ├── cleanup.js │ ├── renameVariables.js │ └── prettyPrint.js ├── utils/ │ ├── ast.js │ ├── sandbox.js │ ├── helpers.js │ └── validation.js └── tests/ └── run.js ``` ## 现实限制 没有任何静态反混淆工具能保证对所有配置进行完全一致的还原。本工具优先处理 `javascript-obfuscator` 的默认模式及常见模式。对于那些依赖于应用状态、环境输入、特殊反调试机制、重度控制流平坦化(control-flow flattening)、域名锁定,或是会引发不在安全依赖范围内的 runtime 副作用的 decoder,将被直接跳过,不会导致进程崩溃。 如果某个 decoder 构建失败或某个调用评估失败,将保留该节点,从而避免结果在不知不觉中将数值替换为错误数据。如果移除 runtime 导致产生没有 binding 的引用或源代码无法重新解析,所有的 runtime 清理操作都将被取消,输出将恢复到仅恢复字符串(strings-only)的检查点状态。 ## 测试 ``` npm run check npm test ``` 内置测试涵盖了 Base64 还原、嵌套 alias 删除、`--keep-runtime` 模式、悬空引用检测、`this` binding 保留以及带有副作用的未使用初始化语句等场景。
标签:Babel, CMS安全, GNU通用公共许可证, JavaScript, MITM代理, Node.js, 代码分析与处理, 代码反混淆, 数据可视化, 自动化payload嵌入, 自定义脚本