mcpField
W
wakaru

wakaru

github.com/pionxzh开发1.1k67 次安装3 天前更新
代码仓库

关于 wakaru

Wakaru 是面向开发者的命令行技能,用于检查生产环境中的 JavaScript。它可反编译压缩或转译代码、拆分受支持的打包文件,并提供源映射与 Bun 可执行文件提取流程。

主要功能

  1. 01反编译压缩和转译后的 JavaScript
  2. 02将受支持的打包文件拆分为模块
  3. 03提取 Bun 可执行文件中的文件
  4. 04借助源映射恢复名称
  5. 05重建 Vue 3 SFC 文件
  6. 06解码 React Server Components 载荷

适用场景

  1. 01检查压缩堆栈跟踪所指向的代码
  2. 02审计网站或依赖实际发布的文件
  3. 03不运行 Bun 可执行文件而提取内嵌 JavaScript
  4. 04查看恢复的 Vue 组件或 RSC 载荷

安装

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

$npx skills add https://github.com/pionxzh/wakaru --skill wakaru

安装后在 Claude Code 中输入/wakaru确认已加载。

详细介绍

Wakaru — JavaScript 反编译器和捆绑包解包工具

Wakaru 可从生产环境中的 JavaScript 还原出易读的源代码:将捆绑包拆分为模块,逆转转译器辅助函数(async/await、类、JSX、展开运算符、可选链等),并撤销压缩。它以确定性方式还原结构,只应用保守的重命名启发式规则——除非提供 source map,否则大多数混淆后的局部变量名仍会很短。

适用场景

  • 文件只有一整行,或充斥着 _interopRequireDefault、 __awaiter、e,t,r 参数、void 0、!0/!1。
  • 你有 webpack 捆绑包、受支持的 Vercel ncc CommonJS 输出(带 IIFE webpack 引导程序)、Turbopack(Next.js 15.3+)分块,或 esbuild/Bun/Metro/Browserify/Cocos Creator 2.x/Closure ModuleManager/SystemJS/AMD/Rollup/Vite 捆绑包,需要提取其中的独立模块。
  • 你有一个 Bun 单文件 PE、Mach-O 或 ELF 可执行文件,需要在不运行它的情况下提取其中嵌入的 JavaScript。
  • 你有一个 Google Tag Manager 容器(gtm.js),需要将其中的 Custom HTML 和 Custom JavaScript 代码分别保存为文件:wakaru gtm.js --unpack -o out/ 会写入 macros/、tags/ 和 index.md,说明每个 __gtm_macro_<n> 的作用以及每个标签的触发时机。
  • 你需要获取 Bun 单文件可执行文件中存储的所有文件,包括二进制资源、WebAssembly、原生扩展或嵌入式数据库。
  • 你有已编译的 Vue 3 组件 JavaScript,希望生成 .vue 文件(尽力还原)以供检查。
  • 堆栈跟踪指向无法阅读的供应商代码或压缩代码。
  • 你正在审计网站或依赖项实际提供的内容。

设置

需要使用 CLI。建议通过 npx 运行(无需全局安装):

npx wakaru --version

如果需要反复调用,可安装一次:npm install -g wakaru@latest。 本文档中的 wakaru 均指 npx wakaru,除非已全局安装。

核心工作流

1. 反编译单个文件

适用于单个已压缩或已转译的文件。使用 --json 可输出结构化内容。不使用 -o 时,反编译后的源代码会包含在 JSON 的 code 字段中;使用 -o 时, 文件仍会写入磁盘,且会省略 code:

echo '<code>' | wakaru --json
# → {"code":"...readable...","warnings":[],"elapsed_ms":N}

也可将文件写入另一个文件:wakaru input.js -o output.js(省略 -o 时输出到标准输出)。

2. 解包 bundle(重要)

一个 bundle 可能会展开成数千个模块——不要把它们全部 导入上下文。先解包到目录中,检查 JSON 输出,然后只打开 需要的文件:

wakaru bundle.js --unpack --json -o out/
# → {"detected_formats":["webpack4"],"modules":[{"filename":"module-0.js"},...],
#    "total":42,"failed":0,"warnings":[],"elapsed_ms":N}

使用一个全新的输出目录。除非传入 --force,否则 Wakaru 不会写入非空 目录;只有确认可以覆盖其中的内容后,才使用 --force。

然后按需读取 out/ 中的特定文件(例如 out/module-0.js)。先按大小 筛选(ls -lS out/)——最大的模块通常是随应用打包的库;应用代码往往更小、 数量也更多。

变体:

wakaru dist/ --unpack --json -o out/          # scan a build-output directory
wakaru entry.js chunk.js --unpack -o out/     # explicit entry + chunk files
wakaru ./compiled-app --unpack --raw -o out/  # extract a Bun single-file executable safely
wakaru bun extract ./compiled-app -o raw/     # dump every embedded Bun file as the bundler emitted it

提取 Bun 单文件可执行文件时,必须显式指定可执行文件路径 (目录扫描和 stdin 仅支持 JavaScript)。--unpack 会选择类似 JavaScript 的记录,并将它们 送入 bundle 拆分流程。如果 stderr 显示 note: <input> embeds N original source file(s) in Bun source maps,请运行它指定的 wakaru bun extract 命令,并在反编译输出之前读取 sources/。比较随包发布的表示形式时,优先使用 --raw: 它仍会拆分语句和声明,使各个块能够作为 ESM 链接,因此当字节内容必须完全一致时,请使用 wakaru bun extract。

当任务需要容器本身时,请改用 wakaru bun extract。它会将每条经过验证的文件记录写入 raw/files/,并将加载器元数据和字节范围记录在 raw/manifest.json 中。文件以字节形式存储,但 "encoding": "utf16le" 的记录(Bun 1.4.1+ 中的非 ASCII 文本)会以 UTF-8 写入;其中的 bytes 和 executable_range 描述存储的 UTF-16 数据,而 output_bytes 描述写入文件的数据。

如果应用使用 --sourcemap 编译,raw/sources/ 会包含 bundle 之前的原始文件 (例如带类型和注释的 TypeScript),而清单的 sources 列表会将每个原始 path 映射到其 output_path。反编译前请先检查它:如果该目录存在,请读取其中的文件,而不要读取打包后的 JavaScript。路径中的 .. 组件会写为 %2E%2E。列在 skipped_sources 中的记录对应的映射文件无法被 Wakaru 读取; 其中缺少源文件,因此请改用其反编译后的 JavaScript。添加 --json 可在 stdout 上输出相同的清单。 --include-internals 还会输出不透明的 Bun source map、JavaScriptCore 字节码 和 module-info 区域;不要将 source-map.bunmap 视为 v3 JSON map。 不加此标志时会写入原始源文件。 提取器支持 Bun 1.3.3+。

除非 bundle 带有可用的命名提示(资源路径、依赖映射请求、原始输入路径),否则 模块文件名为 module-<id>.js;有命名提示时会生成易读的相对文件名,发生冲突时会确保文件名唯一。原始输出 会保留暂定的提取名称。

需要将恢复出的模块与输入进行核对时,请在解包命令中添加 --provenance。在 out/provenance.json 中查找该模块生成的文件名,以查看源输入和 [start, end) 字节范围。

3. 存在 map 时恢复名称 / 原始源文件
wakaru input.js --source-map input.js.map -o output.js   # recover names
wakaru extract input.js.map -o src/                       # dump sourcesContent

输入参数 --source-map 仅支持单文件,且不能与 --unpack 一起使用;提取出的模块不会保留 bundle 生成的坐标。 需要为解包后的输出生成 map 时,请使用 --emit-source-map。

4. 将 Vue 3 组件恢复为 SFC 工件

当输入是编译后的 Vue 3 组件 JavaScript 或可能包含此类内容的 bundle 时,使用 --vue-sfc。恢复采用尽力而为的方式,且只会添加内容:解包模式 仍会为每个模块写入 JavaScript,可恢复的 Vue 模块还会生成同级 .vue 工件。

wakaru input.js --vue-sfc -o output.js
wakaru input.js --vue-sfc -o App.vue
wakaru bundle.js --unpack --vue-sfc --json -o out/

处理单个文件时,使用 -o 写入恢复后的 Vue 输出;不使用时,标准输出 仍为 JavaScript。进行批量分析时,建议使用 --unpack --vue-sfc --json,并先 检查 JSON 输出。每个 modules 条目都描述一个输出工件。其 kind 为 javascript 或 vue_sfc;与 Vue 相关的 status 值为 recovered_vue_sfc、vue_sfc_source_js 和 vue_sfc_fallback_js。打开 恢复后的 .vue 文件检查模板,但如果恢复过程回退或结果看起来过于依赖启发式方法, 请保留配对的 JavaScript 工件。不要将恢复后的 SFC 当作原始源码展示。

5. 解码捕获的 React Server Components 载荷(实验性)

React Server Components 载荷(React 的 Flight 线格式)是数据,而不是 JavaScript:不要对其进行反编译或解包。Next.js 会在页面 HTML 中将其嵌入为 self.__next_f.push(...) 调用。

wakaru rsc page.html -o rsc.json       # saved Next.js page or script
wakaru rsc payload.rsc -o rsc.json     # raw payload rows

JSON 包含 rows(十六进制 id、tag、kind、原始 payload,以及 React 解析后的 json)、assets(载荷加载的脚本和样式表路径)、 scripts(页面的 <script src> 文件,其中包含载荷未指明的打包器运行时;下载这两个列表中的文件并传递给 --unpack),以及 problems(未能解码的行和 push 调用;其余内容仍会写入)。format 的值为 raw 或 next.__next_f。不含载荷的输入会报错。模型行会将 React 的 $ 引用("$L3")保留为字符串:不会重建这些引用所描述的树。 输出内容可能在任何版本中发生变化。

重度混淆的输入

对于字符串数组编码、控制流扁平化、VM 保护器及类似混淆,先使用 webcrack 去除混淆。解包和反混淆交由 Wakaru 处理:

# 1. Strip the obfuscation; leave unpacking and unminifying to Wakaru.
npx webcrack --no-unpack --no-unminify obfuscated.js > deobfuscated.js

# 2. Recover readable modules.
npx wakaru deobfuscated.js --unpack -o out/

重写级别——根据意图选择

  • --level minimal — 几乎不改变语义。安全审查、审计或差异比较时优先使用, 以尽量降低语义风险,但不要将其输出视为正式的等价性保证。
  • --level standard — 默认值;兼顾可读性和正确性。
  • --level aggressive — 最大限度提升可读性;使用更强的启发式方法,可能会 改变边缘情况下的行为。只需理解代码时使用。
  • --unpack=inspect — 递归保留更细粒度的作用域提升边界,以便进行 静态检查。生成的模块图可能无法保留运行时初始化顺序;每次使用此模式时,CLI 都会打印警告。 可另外添加 --raw 以跳过可读性处理流程;使用 --raw 时,--level 不起作用。

默认情况下,Wakaru 只会移除自身转换引入的死代码,并保留输入中原有的死代码。 需要进行完整的可达性扫描时,使用 --dce。

解读输出

  • 退出代码 0 = 成功,非零 = 致命错误(参数错误、I/O 错误)或至少出现一条错误级警告。错误会输出到 stderr;--json 的输出会写入 stdout。
  • 检查每条 JSON 警告中的 is_error 字段。is_error: false 的条目不会导致致命错误。出现错误级警告时,命令会以非零代码退出,但 Wakaru 仍会写入 JSON 输出和所有产物;没有错误级警告的模块仍可使用。
  • 使用 --diagnostics 检测反编译或解包时产生的声明冲突,包括 var 与 let/const 冲突,以及无法重新解析的输出。duplicate_declaration 和 output_parse_* 警告属于错误级警告;合法的重复 var 声明不会被视为错误。未使用 --diagnostics 时,不会重新解析生成的模块。
  • cross_module_class_call 警告(非错误)表示该模块在运行时可能会不带 new 调用恢复出的类。如果输出必须能够执行,请使用 --level minimal 重新运行。
  • commonjs_export_unrecovered 警告(非错误)表示 ESM 输出模块仍通过 exports 或 module.exports 访问所列导出。这些访问在运行时会抛出 ReferenceError,因此应将该模块的导出接口视为不完整。
  • 如果某个模块保留了 require() 调用(例如延迟调用、条件调用或副作用之后的调用),它就会保持为 CommonJS:ESM 不支持 require。commonjs_require_unrecovered 警告(非错误)表示 ESM 输出模块仍会为所列说明符调用 require;这些调用在运行时会抛出错误。
  • commonjs_require_of_esm 警告(非错误)表示 CommonJS 输出模块要求加载一个现在已恢复为 ESM 的模块,因此 require 会返回该模块的命名空间;如果读取已被替换的 module.exports 值,或读取互操作处理已移除、或提供方没有默认导出的模块的默认导出,得到的会是命名空间;写入该命名空间则会失败。
  • commonjs_exports_escape 警告(非错误)表示某个模块将提供方的 exports 对象传递给了另一个模块中的函数,或传递给了 Wakaru 无法跟踪的函数,而该提供方现在是 ESM。如果该函数写入此对象,运行时就会抛出错误;运行输出前请检查该调用。
  • missing_imported_name 警告(非错误)表示 ESM 输出模块导入了其同级 ESM 模块未导出的名称。Node 无法链接该模块,因此应将该依赖边以及所有通过它导入的模块视为损坏的输出,而不是到其他位置查找缺失的导出。
  • 使用 --diagnostics 时,es_source_kept_commonjs 警告(非错误)会指出某个模块虽然输入看起来是从 ES 模块编译而来,却仍保留为 CommonJS,并提供依据和保留的 require 调用。其导入仍是 require 调用;请将它们视为导入处理。
  • .cjs 或 .cts 输入会被反编译为 CommonJS:其 require() 调用和 module.exports 会保留,因此输出中没有可跟踪的导入。--unpack 目前尚不适用此规则:.cjs 输入或恢复出的模块仍可能生成 ESM 语法,而 Node 无法从 .cjs 文件加载该语法。
  • 使用打包器重新构建解包输出前,请检查默认导入了设置 __esModule 的 CommonJS 同级模块的 ESM 模块:Node 会读取 module.exports,而打包器会读取 exports.default(通常为 undefined)。将所有不含模块语法(import、export、import.meta 或顶层 await)的文件改名为 .cjs,包括脚本文件;更新说明符,并在输出根目录添加 {"type": "module"}。
  • runtime_residual 警告(非错误)表示该模块仍会调用输出中未定义的打包器运行时辅助函数,例如 __turbopack_context__.L(...)(Turbopack 分块加载)、__turbopack_context__.P(...)(路径解析)、延迟 import() 外层的 webpack require.e / require.t,或输出未链接任何模块的 webpack require(id)(消息会说明该 id 不在任何输入中、位于不透明模块中,还是存在于构建中的多个模块中)。照常阅读该模块;只有该调用尚未解析。对于 webpack,请确认包含所加载模块的分块文件已列入输入,且仅需确认一次。
  • 解包 JSON 中的 failed 统计至少出现一条错误级警告的不同模块数量(反编译失败、声明冲突、输出解析失败);total 是模块总数。failed > 0 时,退出代码必定非零。如果 failed < total,请照常阅读其他模块,并报告失败的模块。
  • 使用 --vue-sfc 时,recovered_vue_sfc 表示已写入 .vue 产物;vue_sfc_source_js 是该恢复模块对应的 JavaScript;vue_sfc_fallback_js 表示该模块看起来像 Vue 模块,但仍以 JavaScript 形式保留。
  • 如果没有源映射,输出中出现混淆后的短名称(e、t、n)是正常的——Wakaru 只会在代码提供依据时重命名。如果名称很重要,请结合 LLM 重命名工具或源映射使用。

安全

仅反编译你获准分析的代码。阅读第三方发布的内容属于合法的安全研究和调试活动;未经同意将其用于攻击目标可能违法。你有责任遵守相关规定。

评论

支持 Markdown