> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# 混淆还原
- URL: https://blog.vercanti.com/hun-yao-huan-yuan/
- Published: 2026-08-28T14:35:12.000Z
- Updated: 2026-08-28T14:58:17.000Z
- Description: 目前最常见的商业混淆方案。 特征识别： 1. 文件顶部有一个超大数组（几十至几百个 Base64 字符串） 2. 紧随其后有一个数组位移函数（自执行，通过对数组进行旋转还原顺序） 3. 所有字符串通过 _0xXXXX(index) 形式的函数取值 还原方法： 还原方法：将 eval 替换为 console.log，直接打印出解密后的代码。 JJEncode 将代码转换为全由 $ 和 + 等符号组成的字符串；AAEncode 转换为表情符号。 还原：直接在 Console 中粘贴执行，或使用在线工具解码。 仅使用 , !, (, ), + 6 个字符编写任
- Author: yellowdog
- Tags: js逆向, 技巧步骤

> 官方文档：<https://babeljs.io/docs/>  
> 适用场景：识别并还原 JS 代码混淆（OB混淆、JsJiami、压缩混淆等），提取可读逻辑

---

## 混淆类型

### OB 混淆（obfuscator.io）

目前最常见的商业混淆方案。

**特征识别：**

1. 文件顶部有一个**超大数组**（几十至几百个 Base64 字符串）
2. 紧随其后有一个**数组位移函数**（自执行，通过对数组进行旋转还原顺序）
3. 所有字符串通过 `_0xXXXX(index)` 形式的函数取值

```javascript
// 典型 OB 混淆结构
var _0x1a2b = ['aGVsbG8=', 'd29ybGQ=', ...];
(function(_0x1a2b, _0x3c4d) {
    // 数组位移还原逻辑
})(_0x1a2b, 0x1234);
function _0x5e6f(_0x7g8h) {
    return _0x1a2b[_0x7g8h];
}

```

**还原方法：**

- **工具**：[ob-decrypt](https://github.com/DingZaiHub/ob-decrypt)（AST 分析还原）
- **手动**：在 Console 中调用 `_0xXXXX(index)` 逐个查看字符串内容
- **本地调试**：将整个 JS 文件下载，使用 Node.js 运行后打印字符串数组

### eval 混淆

```javascript
eval(function(p,a,c,k,e,r){...}('...', 62, 100, '...'.split('|'), 0, {}))

```

**还原方法**：将 `eval` 替换为 `console.log`，直接打印出解密后的代码。

```javascript
// 在 Console 中执行
(function(p,a,c,k,e,r){console.log(...)}('...'))

```

### JJEncode / AAEncode

JJEncode 将代码转换为全由 `$` 和 `+` 等符号组成的字符串；AAEncode 转换为表情符号。

**还原**：直接在 Console 中粘贴执行，或使用在线工具解码。

### JSFuck

仅使用 `[]`, `!`, `(`, `)`, `+` 6 个字符编写任意 JS 代码。

**还原**：Console 直接执行，或使用 [JSFuck 解码器](https://enkhee-osiris.github.io/Decoder-JSFuck/)。

---

## 代码格式化

| 工具                    | 使用方式                                   |
| --------------------- | -------------------------------------- |
| DevTools Pretty Print | Sources 面板 {} 按钮                       |
| Prettier              | npx prettier --write file.js           |
| js-beautify           | npx js-beautify -o out.js in.js        |
| 在线工具                  | [beautifier.io](https://beautifier.io) |

---

## AST 分析还原

对于复杂的变量名混淆（变量名改为 `_0x1a2b` 等），手动分析效率低，可借助 AST 工具批量还原。

**工具链：**

```
@babel/parser    → 解析 JS 为 AST
@babel/traverse  → 遍历和修改 AST 节点
@babel/generator → 将修改后的 AST 生成代码

```

**常见还原操作：**

1. **字符串还原**：找到字符串解密函数，在 AST 遍历时调用它，将 `CallExpression` 替换为字面量字符串
2. **变量名还原**：将单字母或 `_0x` 开头的变量名替换为语义化名称
3. **控制流还原**：将 `switch` 状态机结构还原为顺序代码

```javascript
// 基础 AST 处理模板
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const fs = require('fs');

const code = fs.readFileSync('obfuscated.js', 'utf-8');
const ast = parser.parse(code);

traverse(ast, {
    // 替换字符串调用为字面量
    CallExpression(path) {
        const { node } = path;
        // 判断是否为字符串解密函数调用
        // 调用解密函数，替换为结果字面量
    }
});

const result = generator(ast).code;
fs.writeFileSync('restored.js', result);

```

---

## 常用在线工具

| 工具           | 地址                        | 功能           |
| ------------ | ------------------------- | ------------ |
| JS Nice      | <http://www.jsnice.org>   | 变量重命名 + 类型推断 |
| Beautifier   | <https://beautifier.io>   | 代码格式化        |
| de4js        | <https://de4js.kshift.me> | 多种混淆还原       |
| BFW工具箱       | <https://tool.bfw.wiki>   | 混淆/反混淆/加解密   |
| AST Explorer | <https://astexplorer.net> | 可视化 AST 分析   |

---

## 分析建议

1. **先格式化**，再找入口（搜索加密关键字）
2. OB 混淆不必完全还原，找到字符串解密函数后可以直接在 Console 调用它查看任意位置的明文
3. 遇到多层套娃（eval 里再 eval）时，逐层剥离，每层单独处理
4. 保留一份原始混淆文件备份，避免修改出错后无法恢复

---

## 最佳实践

**OB 混淆优先找字符串解密函数**：OB（Obfuscator.io）混淆后所有字符串都通过一个解密函数（`_0x1234`）取出，在 DevTools Console 中调用这个函数可以直接看到所有明文字符串，无需完整还原代码。

**压缩混淆用工具自动格式化**：`prettier --parser babel` 或 Chrome DevTools 的"Pretty Print"（`{}`按钮）可快速格式化压缩代码，格式化后再手动阅读，效率远高于阅读压缩代码。

**变量名混淆用 Babel 重命名**：用 AST 工具（`@babel/traverse`）遍历变量声明，按词法作用域分析后重命名为有意义的名称；对于 `_0xabc` 这类短名，可以用 Chrome DevTools Snippets 批量重命名。

**`eval` 混淆的代码拦截执行**：Hook `eval` 函数打印被执行的代码：`const _eval = eval; eval = (code) => { console.log(code); return _eval(code) }`，每层 `eval` 都会被记录，逐层查看解混淆后的代码。

**利用断点查看中间状态而非完整还原**：在关键操作前打断点（如 XHR send、加密函数入口），直接从 Scope 面板读取明文数据，比把整段代码还原为可读代码效率高，也更可靠。

---

## 常见陷阱

### 陷阱：格式化工具破坏了代码逻辑

**现象：** 用 Prettier 格式化后代码报语法错误或行为改变。  
**原因：** 部分混淆代码利用了 JS 边缘语法（逗号运算符、连续赋值、条件表达式链），Prettier 在不理解语义的情况下格式化可能改变运算优先级。  
**解决：** 先用 Chrome DevTools 内置的 Pretty Print（只做缩进，不改语法），确认逻辑正常后再考虑更激进的格式化；格式化后务必在 Console 测试一个已知输入的输出结果。

### 陷阱：字符串解密函数有反调试检测

**现象：** 在 Console 调用字符串解密函数时，页面触发 `debugger` 或函数返回错误结果。  
**原因：** OB 混淆有时在字符串解密函数中加入调用次数检测或时间检测，直接调用会触发反调试分支。  
**解决：** 先找到反调试逻辑（通常是 `setInterval` 循环 `debugger`），用 "Never pause here" 跳过；或直接把整个混淆文件里的 `debugger` 语句全部替换为空，再重新执行。

### 陷阱：还原后的代码在 Node.js 中运行不了

**现象：** 在 Console 测试正常的代码复制到 Node.js 后报错。  
**原因：** 代码依赖浏览器 API（`window`、`document`、`fetch`），Node.js 中这些不存在。  
**解决：** 参考补环境方案，在 Node.js 中模拟缺失的浏览器 API；或用 `vm.runInNewContext` 提供最小化环境。

---

## 参见

[Babel AST入门](https://blog.vercanti.com/babel-ast-ru-men/)  
[js逆向调试技巧](https://blog.vercanti.com/js-ni-xiang-diao-shi-ji-qiao/)  
[补环境](https://blog.vercanti.com/bu-huan-jing/)  
[Webpack逆向还原](https://blog.vercanti.com/webpack-ni-xiang-huan-yuan/)