Golang转换
转换成Golang。请求部分使用 github.com/go-resty/resty/v2。 JSON解析部分使用 github.com/tidwall/gjson。
代码要求:
无注释,异常打上中文日志。
方便维护。变量名简介易看懂。各方法保持解耦。
Ast skills
1 | |
project/
├── source/
│ └── original/ # 原始混淆文件(只读,不修改)
│ └── target.js
├── scripts/ # 解混淆脚本(每步一个,一次性,针对目标定制)
│ ├── step1_string_decrypt.js
│ ├── step2_expr_simplify.js
│ └── …
├── intermediate/ # 中间产物(每步输出,可回退)
│ ├── target_step0.js # 格式化 + 去反调试
│ ├── target_step1.js # 字符串解密后
│ ├── target_step2.js # 表达式简化后
│ └── …
└── source/
└── deobfuscated/ # 最终输出
└── target_deobf.js
1 | |
执行策略
整体原则
本 skill 是百科全书,不是固定流水线。执行者根据实际代码灵活选择步骤:
- 进入计划模式: 先完成 Step 0 分析,然后进入计划模式,向用户展示分析结果和建议的步骤组合,获得确认后再执行
- 按需跳步: 没有字符串数组就跳过 Step 1,没有控制流平坦化就跳过 Step 4
- 随时调整: 任何步骤执行后发现新情况,都可以回退或插入额外步骤
- MCP 用于分析和验证: js-reverse MCP 用于获取源码、搜索特征、小规模验证方案可行性;大批量转换和验证通过 Node.js 脚本执行
MCP 使用时机
MCP 的定位是分析工具和验证探针,不是批量执行引擎。
| 阶段 | MCP 用法 |
|---|---|
| 分析阶段 | search_in_sources 搜索混淆特征;get_script_source 获取目标脚本 |
| 方案验证 | evaluate_script 执行几个解密调用,确认方案可行后再写批量脚本 |
| 中间验证 | evaluate_script 抽样对比解混淆前后的函数输出 |
| 控制流分析 | set_breakpoint + step_over 动态跟踪 switch-case 执行顺序 |
| 最终验证 | 在浏览器中用解混淆后的代码替换原始代码,验证功能正常 |
大批量字符串解密、表达式简化、死代码移除等转换工作,全部通过 Node.js 脚本 + Babel AST 完成。
Subagent 并行策略
可并行的任务组合:
- Step 0 中:特征识别 + 反调试代码搜索
- Step 2 中:Proxy 函数识别 + 对象字典识别(互不依赖)
- 验证阶段:多组测试数据可并行验证
技术手册
以下是各类解混淆技术的详细参考。根据 Step 0 的分析结果,选择需要的章节执行。
Step 0: 预分析与特征识别
目标: 了解混淆类型和程度,为后续步骤提供参考(非硬性路由)。
0.1 获取源码
1 | |
0.2 格式化: 用 prettier 或 babel generator 统一缩进,保存为 intermediate/target_step0.js
0.3 AST 体检报告(可选)
文件较小时直接全文阅读是最快最全面的方式。但即使能读全文,统计脚本的价值在于把非结构化代码转化为结构化数据 — “callee 频次 Top 5”这类信息,人肉读代码很难准确统计。写一个脚本对 AST 做全局扫描:
1 | |
0.4 特征指纹(仅供参考)
| 特征 | 混淆类型 | 常见处理 |
|---|---|---|
高频前缀(如 _0x/a0_)+ 大字符串数组 + 旋转 IIFE |
Obfuscator.io 及变种 | Step 1→2→3→4→5→6 |
| 高频前缀但无字符串数组 | 轻度混淆 | Step 2→3→5→6 |
while(true){switch} + 顺序数组 |
控制流平坦化 | Step 2→4→5→6 |
jsjiami.com 标记 |
sojson | 先去壳,再按实际情况选步骤 |
| Webpack/Parcel 模块包裹 | 打包工具 | 先 unpack 提取模块,再对单模块解混淆 |
大量 eval / Function() |
代码加密 | 沙箱执行解密层,再按实际情况选步骤 |
0.5 移除反调试(如存在)
搜索并删除:
debugger语句(尤其在定时器/循环中)setInterval(() => { debugger }, ...)constructor("debu")反格式化检测console重写/禁用代码
0.6 进入计划模式
向用户展示:
- 识别到的混淆特征
- 建议执行的步骤组合
- 预估的复杂度
用户确认后开始执行。执行过程中发现新情况随时可调整计划。
产出: intermediate/target_step0.js + 分析报告
Step 0.5: Webpack/打包工具 Unpack(可选)
适用条件: 代码是 Webpack/Browserify/Parcel 打包的 bundle。识别特征:
1 | |
处理方向
1 | |
产出: intermediate/modules/module_N.js(每个模块独立文件)
⚠️ unpack 后,后续步骤对每个模块文件独立执行,而非对整个 bundle。
Step 1: 字符串解密与大数组回填
适用条件: 代码中存在字符串数组 + 访问器函数模式。若无此模式则跳过。
目标: 将所有加密/编码的字符串还原为明文。这是后续步骤的基础。
1.1 识别字符串数组结构
典型模式包含三部分(函数名前缀不固定,可能是 _0x、a0_、_$ 或任意混淆名):
- 字符串数组函数: 返回包含所有字符串的数组
- 旋转 IIFE: 对数组执行 push/shift 旋转,打乱原始顺序
- 访问器函数: 接受索引参数,返回数组中对应字符串(通常有偏移量计算)
识别方法: 通过 AST 体检报告中的”最大 ArrayExpression”和”CallExpression callee 频次 Top 5”定位
1.2 处理方向
1 | |
1.3 转义字符串还原
1 | |
1.4 数值还原
1 | |
验证: 用 MCP evaluate_script 抽样对比几个解密结果是否正确
产出: intermediate/target_step1.js
Step 2: 表达式简化
适用条件: 几乎所有混淆代码都需要此步骤。与 Step 5 循环执行效果最佳。
2.1 常量折叠 (Constant Folding)
1 | |
2.2 布尔值还原
1 | |
2.3 Proxy 函数内联
1 | |
2.4 对象属性字典内联
1 | |
产出: intermediate/target_step2.js
Step 3: 引用与属性访问标准化
适用条件: 代码中大量使用 obj['prop'] 方括号访问。需在字符串解密(Step 1)之后执行。
3.1 方括号转点号
1 | |
3.2 逗号表达式拆分
1 | |
产出: intermediate/target_step3.js
Step 4: 控制流平坦化还原
适用条件: 代码中存在 while(true){ switch(...) { case... } } 结构。强依赖 Step 2 的常量折叠。
4.1 分析实际结构
不预设固定模式,根据实际代码分析控制流结构:
1 | |
4.2 还原方向
1 | |
验证: 用 MCP evaluate_script 执行还原后的函数,对比原始输出
产出: intermediate/target_step4.js
Step 5: 死代码移除
适用条件: 几乎所有混淆代码都需要。与 Step 2 循环执行效果最佳。
5.1 不可达分支清理
1 | |
5.2 无用变量/函数剔除
1 | |
5.3 空语句清理
1 | |
5.4 循环策略(Step 2 + Step 5)— 必须实现,不可跳过
单次执行 Step 2 和 Step 5 会遗漏大量可简化代码。原因:死代码移除后暴露新的常量表达式,常量折叠后又暴露新的死代码。
1 | |
每轮循环保存中间文件: intermediate/target_step5_round{N}.js
产出: intermediate/target_step5.js
Step 6: 变量重命名与最终整形
适用条件: 代码中存在无意义的混淆变量名时执行。仅执行确定性的模式化重命名,不做语义推导。
6.1 模式化重命名(确定性的)
1 | |
不确定语义的变量保留原始混淆名,不做规则编号(如 var_1)或语义推导,避免引入错误。
6.2 代码整形
1 | |
6.3 最终验证
1 | |
产出: source/deobfuscated/target_deobf.js + scripts/deobfuscate_target.js
安全边界
- 沙箱隔离: 执行混淆代码必须在
vm模块、isolated-vm或浏览器(MCP)中,禁止直接eval - 迭代上限: Step 2↔5 循环设硬上限(50 次),防止无限循环
- 副作用保护: 删除代码前检查是否有副作用(网络请求、DOM 操作、赋值)
- 作用域安全: 重命名必须通过
path.scope.rename(),不能直接修改node.name - 验证: 每步完成后用
@babel/parser重新解析,确认语法合法
Babel 关键 API 速查
| 用途 | API |
|---|---|
| 静态求值 | path.evaluate() → { confident, value } |
| 布尔求值 | path.get("test").evaluateTruthy() → true/false/undefined |
| 替换节点 | path.replaceWith(node) / path.replaceWithMultiple([nodes]) |
| 删除节点 | path.remove() |
| 获取绑定 | path.scope.getBinding(name) → { constant, referenced, references, referencePaths } |
| 安全重命名 | path.scope.rename(oldName, newName) |
| 刷新作用域 | path.scope.crawl() — 删除/替换节点后必须调用 |
| 值转节点 | t.valueToNode(value) — 注意 Infinity/undefined 返回非 Literal |
注意事项
- Step 2↔5 循环不可省略 — 这是最容易被跳过但影响最大的环节,单次执行会遗漏大量可简化代码
- Webpack bundle 必须先 unpack — 不拆模块直接解混淆,9000 行混在一起对 LLM 和调试都不友好
- 逗号表达式不只在 ExpressionStatement 中 — return、if、for 里都有,必须全部覆盖
- 常量折叠要覆盖所有类型 — string、number、boolean 都要处理,不要只处理部分
- 模式化重命名优先 — Webpack 参数等确定性模式先处理,不确定的保留原始名
- 混淆前缀不只是
_0x— 也可能是a0_、_$、__x等,用 AST 统计高频前缀来判断 - 每步写独立 visitor — 不要在一个 traverse 中混合多种转换,容易产生节点失效
- scope 变更后刷新 — 删除/替换节点后,后续 traverse 需调用
scope.crawl() - 保留不确定的代码 — 宁可保留看不懂的代码,也不要错误删除有用逻辑
- 中间文件是安全网 — 任何步骤出错都可以从上一步的中间文件重来
- 灵活调整步骤 — 实际混淆千变万化,本手册是参考而非教条
1 | |
name: find-crypto-entry
description: 定位 JS 中参数的生成入口。当用户问”xxx 参数在哪生成”、”找加密入口”、”定位签名函数”、”请求参数怎么加密的”时使用
argument-hint: [参数名]
find-crypto-entry
定位加密参数 $ARGUMENTS 的生成入口。
输出目标:入口位置(脚本URL + 行号/列号 + 函数名)
执行流程
Step 0: 网络请求定位(首选)⭐
最高效路径,直接获取已发生请求的调用栈:
list_network_requests({ resourceTypes: ["xhr", "fetch"] })- 列出请求get_network_request(reqid)- 确认 $ARGUMENTS 参数位置(URL/Header/Body)get_request_initiator(requestId)- 直接获取 JS 调用栈
结果判断:
- ✅ 调用栈有效 → 跳到 Step 3
- ❌ 调用栈为空或无意义 → 继续 Step 1
Step 1: 静态搜索
当 Step 0 无法定位时,进行静态分析:
search_in_sources({ query: "$ARGUMENTS" })- 搜索加密参数名分流逻辑:
- 结果 ≤ 50:检查赋值语句(如
headers.$ARGUMENTS = ...) - 结果 > 50:转向搜索拦截器特征:
interceptors.request.useXMLHttpRequest.prototype.sendwindow.fetch =
- 结果 ≤ 50:检查赋值语句(如
find_in_script(scriptId, query)- 获取精确行号列号
结果判断:
- ✅ 找到明确赋值位置 → 跳到 Step 4
- ❌ 结果太多或无法确定 → 继续 Step 2
Step 2: 动态断点
静态搜索无法锁定时,设置断点:
方案 A(推荐):
1 | |
方案 B:
1 | |
⚠️ 提示用户:请在浏览器中触发包含 $ARGUMENTS 参数的请求
结果判断:
- ✅ 断点命中 → 继续 Step 3
- ❌ 断点未命中 → 检查断点条件是否正确,或尝试其他方案
Step 3: 堆栈分析
获取调用栈后,执行灰名单过滤:
1 | |
过滤规则:
| 级别 | 特征 | 动作 |
|---|---|---|
| Level 1 跳过 | axios, react-dom, vue, jquery, webpack/runtime |
框架噪声,跳过 |
| Level 2 检查 | 含 security, encrypt, auth, fingerprint 的 node_modules |
可疑安全SDK,检查 |
| Level 3 优先 | 非 node_modules(/src/, /app/) |
业务代码,优先检查 |
结果判断:
- ✅ 找到可疑帧 → 继续 Step 4
- ❌ 全是框架代码 → 返回 Step 2 尝试其他断点位置
Step 4: 确认入口
在可疑帧上验证:
1 | |
寻找加密特征:
- 位运算:
^,&,|,>>> - 魔法常量:
0x67452301(MD5),0x6a09e667(SHA256)
结果判断:
- ✅ 确认是加密入口 → 输出结果
- ❌ 不是加密逻辑 → 检查调用栈上一帧,或返回 Step 3 检查其他帧
输出格式
找到入口后,输出:
1 | |
注意事项
- 优先使用
get_request_initiator- 无需断点,最高效 - 优先使用
set_breakpoint_on_text- 对压缩代码友好 - 只找入口,不做算法还原 - 保持 skill 单一职责
- 每个 Step 失败后有明确跳转 - 避免卡住