# JS 解密 API

服务地址：`https://js.xueyuanpie.com`
所有接口返回 JSON（`/api/deobfuscate/raw` 除外，它直接返回源码）。

---

## 1. 还原代码（JSON）

```
POST /api/deobfuscate
Content-Type: application/json
```

### 请求字段

| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
| `code` | string | — | **必填**。待还原的 JS 或 HTML 源码 |
| `name` | string | `input.js` | 文件名，仅用于下载时命名 |
| `keepScaffold` | bool | `false` | 是否保留混淆脚手架（数组/解密器）。调试时设 `true` |
| `maxRounds` | int | `12` | 通用 passes 的最大迭代轮数 |
| `pLiterals` | bool | `true` | 十六进制 / Unicode 转义还原 |
| `pConstantFold` | bool | `true` | 常量折叠、死分支消除 |
| `pArrayInline` | bool | `true` | 字符串数组内联 |
| `pObjectInline` | bool | `true` | 对象属性内联、方法调用求值 |
| `pDeadBranch` | bool | `true` | `if(false)` / `while(false)` 等死分支消除 |
| `pUnflatten` | bool | `true` | 控制流平坦化还原 |
| `pDeadDecls` | bool | `true` | 无用声明清理 |
| `pCleanScaffold` | bool | `true` | 混淆残骸清理 |

### 响应

```json
{
  "ok": true,
  "jobId": "b66d3fc92f8ece0c",
  "ms": 379,
  "inputBytes": 24005,
  "outputBytes": 29747,
  "report": {
    "family": "jsjiami", "version": "v7", "confidence": 1,
    "markers": ["版本标记 jsjiami.com.v7", "自校验变量 _0xodS", "..."],
    "jsjiami": {
      "table": 116, "callSites": 116, "coverage": 1,
      "replaced": 117, "removedScaffold": 13,
      "arrayLength": 123, "decoder": "_0x3f50",
      "formula": { "BASE": 490, "A": "abcdefghijklmnopqrstuvwxyzABC..." }
    },
    "passes": [ { "name": "字面量还原", "count": 5 }, ... ],
    "warnings": [], "errors": [],
    "inputType": "js", "ms": 379
  },
  "code": "……还原后的完整源码……",
  "download": "/api/download/b66d3fc92f8ece0c"
}
```

失败时返回 HTTP 4xx/5xx 与 `{ "ok": false, "error": "CODE", "message": "..." }`。

---

## 2. 还原代码（纯文本）

```
POST /api/deobfuscate/raw
Content-Type: application/json

{ "code": "..." }
```

直接返回 `text/plain` 的还原源码。响应头附带：

- `X-Job-Id`：任务 ID
- `X-Report`：URL 编码的报告 JSON（截断到 4000 字符）

---

## 3. 上传文件还原

```
POST /api/upload
Content-Type: multipart/form-data

file = <文件>          # 必填，支持 .js/.mjs/.cjs/.txt/.html/.htm，以及 gzip 压缩的 .js.gz
# 其余字段同 /api/deobfuscate
```

单文件上限 8 MB（`MAX_MB` 环境变量可调）。

- 传入 HTML 时会自动识别 `<script>` 块并按作用域合并处理；
  文件名以 `.html` 结尾但内容不像 HTML 时，会把整份内容当一段 JS 处理
- `.js.gz` / zlib 压缩的输入会**自动解压**后再还原
- 二进制文件会被拒绝（HTTP 415 `BINARY_FILE`），判据为三重：
  已知格式魔数（PNG/ZIP/ELF/PDF/JPEG…）、非法 UTF-8 序列、控制字符占比 >2%

---

## 4. 批量还原

```
POST /api/batch
Content-Type: multipart/form-data

files = <文件1>
files = <文件2>        # 最多 20 个
```

响应：

```json
{
  "ok": true,
  "count": 2,
  "results": [
    { "ok": true, "name": "a.js", "jobId": "...", "inputBytes": 123,
      "outputBytes": 456, "ms": 120, "report": {…}, "download": "/api/download/..." }
  ]
}
```

---

## 5. 下载结果

```
GET /api/download/:jobId
```

以 `附件` 形式返回还原后的 `.js` 文件。产物保留 24 小时。

---

## 6. 任务查询

```
GET /api/jobs/:jobId
```

返回任务元数据（不含产物内容）：

```json
{ "ok": true, "job": { "id": "...", "name": "a.js", "status": "done",
  "createdAt": 1791350762235, "inputBytes": 24005, "outputBytes": 29747,
  "ms": 379, "report": {…} } }
```

---

## 7. 健康检查

```
GET /api/health
```

```json
{ "ok": true, "service": "js-deobfuscate-api", "version": "1.0.0",
  "uptime": 19, "node": "v24.0.0", "jobs": 1,
  "limits": { "maxBytes": 8388608, "rateMax": 60 } }
```

---

## 8. 接口文档

```
GET /api/doc
```

返回本文件的 markdown 原文。

---

## 调用示例

### curl

```bash
# 还原一个文件
curl -s -X POST https://js.xueyuanpie.com/api/upload \
  -F "file=@obfuscated.js" | python3 -m json.tool | head -30

# 直接拿还原后的源码
curl -s -X POST https://js.xueyuanpie.com/api/deobfuscate/raw \
  -H 'Content-Type: application/json' \
  -d '{"code":"var _0x1=..."}' -o decoded.js
```

### Python

```python
import json, urllib.request

code = open('obfuscated.js', encoding='utf-8').read()
req = urllib.request.Request(
    'https://js.xueyuanpie.com/api/deobfuscate',
    data=json.dumps({'code': code}).encode(),
    headers={'Content-Type': 'application/json'},
)
d = json.load(urllib.request.urlopen(req, timeout=60))

print(d['report']['family'], d['report']['version'])
print('覆盖:', d['report']['jsjiami']['coverage'])
open('decoded.js', 'w', encoding='utf-8').write(d['code'])
```

---

## 支持范围

### 深度支持（专用引擎）

- **jsjiami v6 / v7**：字符串数组 + 洗牌 + CRC 式自校验 + 自定义 BASE64 + ARC4 + 记忆化解密器。
  做法是**真跑一遍**（隔离沙箱）取洗牌后数组与解密器调用记录，再用固定形态公式做完整性裁决，
  典型样本可达 100% 字符串覆盖。

### 通用支持（AST passes）

- 十六进制 / Unicode 转义还原
- 常量折叠（含 `!![]` → `true`、`void 0` → `undefined`）
- 字符串数组内联（`_a[0]` → `'x'`）
- 对象属性 / 方法内联（`o.f(1,2)` → `3`）
- 死分支与死循环消除（`if(false)`、`while(false)`）
- 控制流平坦化还原（`while(true){switch('1|0|2'.split('|')[i++]){...}}`）
- 无用声明与混淆残骸清理

### HTML 输入

传入 HTML 时自动识别 `<script>` 块。相邻的「纯声明配置块」（如
`const ALLOW_URLS=''; const LOADER_SHOW=2;`）会与混淆块**合并成一份 JS 文档**一起处理——
否则混淆代码里引用的常量会变成未定义。

---

## 限制与说明

- 单次请求体上限 **8 MB**，批量最多 **20 个文件**
- 限流：每 IP 每分钟 **60 次**
- 产物保留 **24 小时**，任务记录保留最近 500 条
- 目标代码只在 **`vm` 隔离沙箱**内执行：无 `require`、无网络、无文件系统，
  定时器回调会被立即执行一次以触发延迟代码路径，有超时与内存上限
- **不保证 100% 还原**，也不保证与原始逻辑完全一致；请仅对你有合法授权的文件使用
