mcpField
R
reverse-engineer-js

reverse-engineer-js

github.com/vladmdgolam开发946 次安装2 周前更新
代码仓库

关于 reverse-engineer-js

面向开发者的 JavaScript 反混淆技能,借助 LLM 将压缩或混淆代码恢复为更易读的形式,并指导源码映射检查、模块提取及处理不同规模的打包文件。

主要功能

  1. 01优先检查源码映射
  2. 02基于 LLM 重命名标识符
  3. 03用 webcrack 提取模块
  4. 04处理 TTY 与输出文件问题

适用场景

  1. 01恢复有效混淆代码中的名称
  2. 02处理大型打包文件中的指定模块
  3. 03诊断 CI 或 Shell 中的 humanifyjs 故障

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vladmdgolam/agent-skills --skill reverse-engineer-js

安装后在 Claude Code 中输入/reverse-engineer-js确认已加载。

详细介绍

逆向工程 JS

借助 humanifyjs 和用于提取及重新集成模块的配套工具,使用 LLM 辅助反混淆缩小或混淆后的 JavaScript。

工具一览

工具用途使用时机
humanifyjs (npx)借助 LLM 重命名混淆的变量/函数代码混淆,但本身是有效的 JS
prettier / js-beautify重新格式化单行缩小代码如有需要,在 humanify 之前使用
webcrack将 webpack / browserify 包拆分为单个模块文件包是一个大文件时
Source map 追踪包旁边可能存在 // # sourceMappingURL=...务必先检查——效果胜过 LLM 重命名

在耗费 token 使用 humanify 之前,务必先检查 source map(bundle.js.map 或文件中的 sourceMappingURL 注释)。如果找到了,就可以免费还原原始源代码。

humanify 默认调用方式

除非用户另有要求,否则使用 Gemini:

npx humanifyjs gemini -m "gemini-3.8-flash" -o <outputDir> <inputFile>

需要在环境变量中设置 GEMINI_API_KEY(通常放在项目根目录的 .env 文件中)。

其他后端:

# OpenAI
npx humanifyjs openai --apiKey="$OPENAI_API_KEY" --model gpt-4.1-mini bundle.js

# Local (slower, free)
npx humanifyjs local bundle.js

关键注意事项

1. 非交互式 shell 中需要伪 TTY

humanify 会调用 process.stdout.cursorTo(...) 来显示进度条。在 Claude Code / CI / 管道环境中,stdout 不是 TTY,因此会因 TypeError: process.stdout.cursorTo is not a function 崩溃。

请使用 script -q /dev/null 包装调用:

script -q /dev/null npx humanifyjs gemini -m "gemini-3.8-flash" -o "$OUT" "$IN"

(macOS/BSD 的 script 语法。Linux 上使用:script -qc "<cmd>" /dev/null。)

2. 输出文件名固定

humanify 始终将结果写入 <outputDir>/deobfuscated.js,与输入文件名无关。不要指望输入 module_1234.js 后会输出 module_1234.js。

3. 文件越长,运行时间越久

humanify 会按顺序处理顶层声明,每个声明都会调用一次 LLM。一个 5 MB 的包可能需要 30 分钟以上。可采用以下策略:

  • 对从包中提取的单个模块运行,而不是处理整个包
  • 如果包使用 webpack 打包,先用 webcrack 拆分
  • 选择能充分理解代码且成本最低的模型。这里默认使用 gemini-3.8-flash;对于非常大的包,轻量版(gemini-3.5-flash-lite)能降低每个声明的成本,但处理密集代码时准确度会有所下降

4. 保留原始文件存档

进行任何重写之前,先将未经修改的包复制到 archive/bundle.original.js。humanify 的结果并非确定性的,你可能会想换个模型重新运行。

工作流 A:整个包的反混淆

适用于较小的包(<500 KB)或时间充裕的情况:

mkdir -p archive out
cp bundle.js archive/bundle.original.js
script -q /dev/null npx humanifyjs gemini \
  -m "gemini-3.8-flash" \
  -o out bundle.js
# result: out/deobfuscated.js

之后可以选择运行 prettier --write out/deobfuscated.js,以改善格式。

工作流 B:提取单个模块(大型包)

常见于 fxhash / 生成艺术包:单个包包含许多 id: (params) => { ... } 模块,而你只想对其中几个有意思的模块运行 humanify。

  1. 确定模块 ID——在包中搜索独特字符串,然后追溯到外层的 <id>: (e, t, n) => { 开始位置。
  2. 提取模块主体——找到开始位置后,统计花括号数量(注意字符串),直到匹配的 }。
  3. 包装为独立箭头函数——将 (e, t, n) => { <body> } 写入临时文件。humanify 需要有效的顶层代码。
  4. 运行 humanify——script -q /dev/null npx humanifyjs gemini -m "gemini-3.8-flash" -o "$OUT" "$IN"。
  5. 拆除包装——从结果中移除 (e, t, n) => { ... } 外壳。
  6. 保存并打桩——将反混淆后的主体写入 modules/module_<id>_<name>.js,并将包中的主体替换为类似 <id>: (e, t, n) => { window.DEOBF[<id>](e, t, n); } 的桩代码。

如果项目已有 scripts/deobfuscate.js(或类似脚本),请重复使用——花括号计数和字符串转义逻辑相当棘手,每次处理新包都重新编写会浪费时间。典型脚本会接收数字模块 ID,从 bundle.js 提取主体,对包装后的函数运行 humanify,拆除包装,并将结果写入 modules/module_<id>_<name>.js。

工作流 C:Webpack 打包文件 → 单模块文件

处理 Webpack 打包文件时,webcrack 会在运行 humanify 之前清晰地拆分模块:

npx webcrack bundle.js -o webcracked/
# Then humanify each module of interest:
script -q /dev/null npx humanifyjs gemini \
  -m "gemini-3.8-flash" \
  -o humanified webcracked/modules/123.js

示例

示例 1:反混淆一个小型 fxhash 打包文件

用户说:“humanify 这个 fxhash 项目中的 bundle.js”

操作:

  1. 检查 sourcemap:head -c 4096 bundle.js | tail -c 200 — 查找 sourceMappingURL。没有?继续。
  2. 确认 .env 中有 GEMINI_API_KEY。加载环境变量:set -a; source .env; set +a。
  3. 归档:mkdir -p archive && cp bundle.js archive/bundle.original.js。
  4. 运行:mkdir -p out && script -q /dev/null npx humanifyjs gemini -m "gemini-3.8-flash" -o out bundle.js。
  5. 美化代码:npx prettier --write out/deobfuscated.js。

**结果:**生成 out/deobfuscated.js,变量已重命名,可以开始阅读。

示例 2:反混淆大型打包文件中的一个模块

用户说:“反混淆 og/bundle.js 中的模块 3658,并将其命名为 treeGenerator”

操作:

  1. 如果项目已有反混淆辅助脚本(例如 scripts/deobfuscate.js),运行它:node scripts/deobfuscate.js 3658 treeGenerator。
  2. 否则:通过统计大括号提取模块主体,将其包装为 (e, t, n) => { ... },使用 script -q /dev/null 运行 humanify,去除包装,将结果保存到 modules/module_3658_treeGenerator.js,并为打包文件添加桩代码。

结果:modules/ 中新增一个可读的函数文件;通过添加桩代码缩小 bundle.js。

示例 3:humanify 因 cursorTo is not a function 崩溃

用户说:“humanify 一运行就挂了”

**诊断:**标准输出不是 TTY(这是 Claude Code 的 bash 工具的默认状态)。

**修复:**使用 script -q /dev/null <cmd>(macOS)或 script -qc "<cmd>" /dev/null(Linux)包装命令后重新运行。

故障排除

cursorTo is not a function

**原因:**标准输出不是 TTY。**修复:**使用 script -q /dev/null 包装命令。

未设置 GEMINI_API_KEY

**原因:**humanify 从进程环境变量读取,而不会自动读取项目的 .env。**修复:**在同一个 shell 中运行 set -a; source .env; set +a,或直接传入:GEMINI_API_KEY=... npx humanifyjs gemini ...。

输出与输入完全相同(没有重命名)

**原因:**humanify 只会重命名它认为经过混淆的标识符(较短的单字母标识符)。可读代码会保持原样。**修复:**确认输入确实经过混淆;如果 useState 之类的名称保留了下来,这是预期行为,不是 bug。

Gemini 限流/配额错误

**原因:**文件较大、顶层函数较多 → 需要多次顺序调用大模型。**修复:**切换到 local 后端进行免费的本地重命名,或者提取单个模块(工作流 B),而不是处理整个打包文件。

humanify 需要数小时

**原因:**打包文件较大,且逐个函数顺序调用。**修复:**先用 webcrack 拆分,或者只对实际需要的模块运行 humanify。

输出目录中包含过时的 deobfuscated.js

**原因:**humanify 会写入固定文件名。第二次运行失败时,会拾取上次成功生成的输出。**修复:**每次运行前执行 rm -f $OUT/deobfuscated.js。

评论

支持 Markdown