dolphinwesting248/deob
GitHub: dolphinwesting248/deob
一个通用的 AST 级别 JavaScript 去混淆预处理框架,专为 LLM 辅助逆向工程设计,通过 17 步语义保持变换重构代码结构。
Stars: 0 | Forks: 0
# deob
一个通用的 AST 级别 JavaScript 去混淆预处理框架。所有转换都是**保持语义的** — 它们重组代码而不改变运行时行为。旨在作为**面向 LLM 辅助逆向工程的数据准备步骤**。
- **通用性。** 无需混淆器检测,无需特征匹配。obfuscator.io、JSVMP、webpack bundles、自定义混淆器 — 全部一视同仁。
- **保持语义。** 拆分、展开、简化、内联 — 每一个处理过程都保持原始逻辑不变。仅改变代码结构。
- **面向 LLM。** 结构报告、调用图、字符串警报、词汇查找索引、SQLite 代码索引 — 输出专为 LLM 消费而设计,而不仅仅是为了人类可读性。
## 快速开始
```
npm install
npm link
# 单个文件
deob main.js # → main.deob/main.js
deob main.js --split # → main.deob/ (per-function files)
deob main.js --metrics # → main.deob/ + metrics.html
deob main.js --md --json # → main.deob/ + structure reports
deob main.js --index # → main.deob/ + code index
# 目录(跨文件汇总)
deob src/ --md --json # → src.deob/ + summary.md
# Config-driven(自动检测 deob.config.js)
deob init # generate config template
deob # run with ./deob.config.js
deob --config path/to/config.js # explicit config path
```
所有输出都会进入一个目录:
```
main.deob/
├── main.js ← deobfuscated code
├── metrics.html ← readability report (--metrics)
├── structure.md ← function inventory + hotspots + alerts (--md)
├── structure.json ← machine-readable (--json)
└── .index/ ← code intelligence index (--index)
```
## CLI 参考
```
deob auto-detect deob.config.js in cwd
deob [output-dir] [options]
deob --config
deob init [--force]
```
| 标志 | 输出 | 描述 |
|------|--------|-------------|
| (默认) | `main.js` | 单个去混淆文件 |
| `--split` | 按函数拆分的文件 | 每一个 `_sub_` 函数都在单独的文件中,按父级分组 |
| `--metrics` | `metrics.html` | 使用 Chart.js 进行转换前后的可读性对比 |
| `--md` | `structure.md` | 函数清单、调用图、热点、警报、查找索引 |
| `--json` | `structure.json` | 与 `--md` 内容相同,格式为机器可读的 JSON |
| `--index` | `.index/` | 用于 AI 辅助探索的 SQLite 知识图谱 |
| `--config ` | — | 从配置文件加载选项,忽略其他标志 |
| `init` | `deob.config.js` | 在当前目录生成配置模板 |
**目录输入:** 独立处理每一个 `.js` 文件,随后生成包含跨文件热点、合并警报以及组合查找索引的 `summary.md` / `summary.json`。
**配置格式**(`deob init` 生成模板):
```
module.exports = {
input: "src/main.js", // file, directory, or array
// input: ["a.js", "b.js", "sub/"],
// output: "out/", // optional
split: false,
metrics: false,
md: true,
json: false,
index: false,
};
```
## 结构报告章节
| 章节 | 内容 |
|---------|---------|
| Summary (摘要) | 函数总数、子函数/原始函数细分、最大嵌套深度、提取类型 |
| Hotspots (热点) | 调用最频繁的函数、根入口点、叶子终端、热点组 |
| Hot Groups (热点组) | 具有最多跨函数调用边的目录 |
| Quick Lookup (快速查找) | 单词 → 函数索引(将 `_sub_program_init_vars` 拆分为 `program` · `init` · `vars`) |
| String Alerts (字符串警报) | 与安全相关的模式:API endpoint、token、加密、eval、存储、DOM sink |
| Call Graph (调用图) | 跨函数调用的 Mermaid 图表 |
| Function Inventory (函数清单) | 包含名称、行数、参数、调用、被调用的完整表格 |
## 代码索引
使用 `node:sqlite` + `@babel/traverse` 构建 SQLite 知识图谱。
**Schema:** `nodes`(函数、类、变量)、`edges`(调用、包含、引用)、FTS5 搜索。
**针对去混淆输出的优化:**
- 跳过 `index.js` 胶水文件
- 为每个节点标记 `metadata.group`(父目录)
- 从混淆标识符中过滤掉无意义的 `MemberExpression` 引用
**查询示例:**
```
SELECT name, file_path FROM nodes
WHERE json_extract(metadata, '$.group') = 'misc' AND kind = 'function';
SELECT target, COUNT(*) as c FROM edges
WHERE kind = 'calls' GROUP BY target ORDER BY c DESC LIMIT 10;
```
## Pipeline
| 步骤 | 处理过程 | 描述 |
|------|------|-------------|
| 1 | `traverse` | 收集所有函数节点,从最内层开始处理 |
| 2 | `wrapper` | 从逗号链中提取顶层的 IIFE |
| 3 | `hoist` | 将 var/let/const/function 移动到每个作用域的顶部 |
| 4 | `extract-inline` | 提取嵌入式函数表达式(return、赋值、IIFE、MemberExpression) |
| 5 | `simplify` | 折叠常量,简化布尔值,折叠字符串操作,标准化 AST |
| 6 | `short-circuit` | 将 `A\|\|B\|\|(C,D)` 转换为 `if(!A&&!B){C;D;}`,三元表达式转换为 if/else,`var x=cond?a:b` 转换为 if/else |
| 7 | `expand-seq` | 将逗号链拆分为独立的语句 |
| 8 | `short-circuit` | 第二次处理 — 捕获由逗号拆分暴露出的 LogicalExpression |
| 9 | `dead-code` | 移除 if(false)、return 后的不可达代码、空的 catch |
| 10 | `inline-props` | 用字面量值替换 config.PROP |
| 11 | `unused` | 移除从未被引用的辅助函数 |
| 12 | `conditions` | 简化 `a?true:false→!!a`,if/return 模式 |
| 13 | `wrappers` | 内联纯粹的 wrapper 函数 |
| 14 | `call-tree` | 拓扑排序:被调用者在调用者之前 |
| 15 | `single-caller` | 内联仅从一个地方调用的函数 |
| 16 | `normalize` | 多重声明拆分,链式赋值拆分,for(;;) 转换为 while(true) |
| 17 | `extract-inline` | 第二次处理 — 捕获因重构暴露出的模式 |
## 输出示例
所有示例均根据实际的 pipeline 输出进行了验证。
### 逗号操作符展开 + 内联函数提取
```
// Input
function a0_0x5465(_0x147aca,_0x1c469e){var _0x477d9d=a0_0x1cb6();return (a0_0x5465=function(_0x593f2d,_0x4d5e1e){var _0x2a8c=_0x477d9d[_0x593f2d];return _0x2a8c?_0x2a8c(_0x4d5e1e,_0x147aca):_0x4d5e1e}),a0_0x5465(_0x147aca,_0x1c469e)}
// Output
function a0_0x5465(_0x147aca, _0x1c469e) {
var _0x477d9d = a0_0x1cb6();
a0_0x5465 = _sub_return_fn1;
return a0_0x5465(_0x147aca, _0x1c469e);
}
function _sub_return_fn1(_0x593f2d, _0x4d5e1e, _0x477d9d, _0x147aca) {
var _0x2a8c = _0x477d9d[_0x593f2d];
return _0x2a8c ? _0x2a8c(_0x4d5e1e, _0x147aca) : _0x4d5e1e;
}
```
↑ `return (a=fn, b)` → 两个语句;内联的 `function` 被提取至 `_sub_return_fn1`,外部引用作为参数传入。
### 短路 polyfill → if 块
```
// Input
"undefined"==typeof Element||Element.prototype.addEventListener||(u=[],Ao=function(n,t){for(var e=0;e` | 父函数名、方法名,或对于匿名函数使用 `lnXXXX` |
| `` | 两位数的提取顺序 |
| `` | `if`、`else`、`try`、`catch`、`init_vars`、`iife_body`... |
## API
```
const { main } = require("./scripts");
main({ input: "obfuscated.js", output: "out/", split: true });
```
## 目录结构
```
deob.js ← CLI entry point
scripts/
├── pipeline.js ← Main orchestration (17 passes)
├── extract.js ← Syntactic splitting (IIFE, try-catch, if-else, switch, …)
├── passes.js ← All post-processing passes
├── traverse.js ← Innermost-first function collection
├── metrics.js ← Readability analysis + HTML Chart.js report
├── structure.js ← Function inventory, hotspots, alerts, lookup index
├── ast-utils.js ← AST walker, detectors, clone
├── scope.js ← Variable scope & external reference analysis
├── emit.js ← Sub-function declaration builder
├── naming.js ← Naming convention helpers
├── wrapper.js ← Top-level IIFE extraction
├── config.js ← Parser, generator, globals
├── index.js ← Public API exports
└── indexer/ ← Code intelligence indexer
├── index.js ← Orchestration: scan → extract → store → resolve
├── extract.js ← Babel-based JS symbol & call-graph extractor
├── schema.js ← SQLite schema
└── store.js ← node:sqlite database operations
```
## License
ISC
标签:CMS安全, JavaScript, LLM辅助, MITM代理, 云安全监控, 云资产清单, 代码反混淆, 数据可视化, 自定义脚本, 逆向工程, 静态分析