mcpField
R
reverse-engineer-js

reverse-engineer-js

github.com/vladmdgolam開発946 回インストール2 週間前に更新
リポジトリ

reverse-engineer-js について

圧縮・難読化された JavaScript を LLM で読みやすくするためのスキルです。ソースマップの確認、モジュール抽出、規模に応じたバンドル処理の手順を開発者向けに案内します。

主な機能

  1. 01ソースマップを先に確認
  2. 02LLM による識別子の改名
  3. 03webcrack によるモジュール抽出
  4. 04TTY と出力ファイルの問題対処

活用シーン

  1. 01有効な難読化コードの名前を復元
  2. 02大規模バンドルから対象モジュールを処理
  3. 03CI やシェルでの 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-beautify1 行に圧縮されたコードを整形必要に応じて humanify の前
webcrackwebpack / browserify のバンドルをモジュールごとのファイルに展開バンドルが 1 つの大きなファイルの場合
ソースマップの調査バンドルの近くに // # sourceMappingURL=... がある場合があります必ず最初に確認してください。LLM による名前変更より確実です

humanify にトークンを使う前に、必ずソースマップ(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. 非対話型シェルでは疑似 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 を 1 回呼び出します。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 / generative-art のバンドルでよくあるケースです。1 つのバンドルに多数の 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 バンドルを難読化解除する

ユーザーの発言:「この fxhash プロジェクトの bundle.js を humanify してください」

操作:

  1. ソースマップを確認します: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:巨大なバンドルから 1 つのモジュールを難読化解除する

ユーザーの発言:「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:cursorTo is not a function で humanify がクラッシュする

ユーザーの発言:「humanify がすぐに終了してしまいます」

**診断:**stdout が非 TTY です(Claude Code の bash ツールではこれがデフォルトです)。

対処法:script -q /dev/null <cmd>(macOS)または script -qc "<cmd>" /dev/null(Linux)でラップして再実行します。

トラブルシューティング

cursorTo is not a function

**原因:**stdout が TTY ではありません。対処法:script -q /dev/null でラップします。

GEMINI_API_KEY が設定されていない

**原因:**humanify はプロジェクトの .env から自動的に読み込まず、プロセス環境変数を読み込みます。**対処法:**同じシェルで set -a; source .env; set +a を実行するか、インラインで渡します:GEMINI_API_KEY=... npx humanifyjs gemini ...。

出力が入力と同一(名前が変更されない)

**原因:**humanify は難読化されていると判断した識別子(短い名前や 1 文字の名前)だけを変更します。すでに読みやすいコードはそのままになります。**対処法:**入力が本当に難読化されていることを確認します。useState のような名前がそのまま残るのは想定どおりで、バグではありません。

Gemini のレート制限/クォータエラー

**原因:**ファイルが大きく、トップレベル関数が多いため、LLM 呼び出しが順次多数実行されます。**対処法:**無料でローカルの名前変更ができる local バックエンドに切り替えるか、バンドル全体ではなく単一のモジュールを抽出します(ワークフロー B)。

humanify の実行に何時間もかかる

**原因:**バンドルが大きく、関数ごとの呼び出しが順次実行されます。**対処法:**事前に webcrack で分割するか、必要なモジュールだけを humanify にかけます。

出力ディレクトリに古い deobfuscated.js が残っている

**原因:**humanify は固定のファイル名で書き込みます。2 回目の実行に失敗すると、前回の正常な出力が使われます。**対処法:**各実行の前に rm -f $OUT/deobfuscated.js を実行します。

コメント