leanrea について
leanreaは、バイナリやアプリケーションバンドル、データファイルを調べるリバースエンジニアリング用スキルです。証拠の迅速な収集と対象を絞ったコード調査を重視し、主張を根拠別に示します。
主な機能
- 01ファイル形式とマジックバイトで手順を選択
- 02迅速な調査コマンドを提示
- 03隠しファイルやバンドル内コードを検索
- 04観測・推論・不明を区別
活用シーン
- 01未知の実行ファイルの初期調査
- 02Electronアプリバンドルの確認
- 03展開したファイルからコードや文字列を検索
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ aicodeking/ leanrea --skill leanreaインストール後、Claude Code で/leanreaを入力して読み込まれたか確認します。
詳細
leanrea:最短手順
これらのツールの使い方はすでに把握している前提です。このファイルでは、対象ごとに最速のコマンドと注意点だけを記載します。
ルール
- コピー上で作業します(APFS の
cp -cなら瞬時です)。展開するごとに$TMPDIR/re/<name>の下に新しいディレクトリを作成します。すでに展開済みなら、その展開結果を再利用します。 - まず
file -b Tとマジックバイトで対象を判別します。次に、最も手軽なインベントリを実行します。それから初めて、逆コンパイルするかコードを読みます。 rgは隠しファイルと無視対象のファイルをスキップし、バンドラーはコードを隠します(Electron:.webpack/、.vite/)。展開したツリーは必ずrg -uuで検索します。- 大きなファイルやミニファイされたファイルを
catしてはいけません。必要な部分だけを切り出します:rg -o '.{0,200}NEEDLE.{0,200}' F | head。逆アセンブル結果は一度ファイルに出力し、そのファイルをgrepします。 - 独立した調査は、
;とecho '--- name'のマーカーを使って、1 回のシェル呼び出しにまとめます(引用符で囲みます。zsh は引用符のない==xを展開します)。すべての出力はheadまたは-mで制限します。 - 根拠に基づいて回答します。回答では各主張に 確認済み(file:offset、またはシンボル/文字列を引用)か 推定 の印を付けます。見つからなかった事実は「不明」であり、「存在しない」ではありません。静的な読み取りは、実行時の観測ではありません。
- ツールが見つからなくても、作業を中断する必要はありません。同じ行の代替手段を使うか、ユーザーが許可している場合は
brew install <x>を実行します。 - バイト数よりターン数のほうがコストがかかります。ツール呼び出しは 2~4 回を目安にします。偵察をまとめて 1 回、必要な部分を狙って 1~2 回確認してから回答します。依頼された項目すべてに根拠がそろったら終了します。すでに確認したことは再確認しません。
最初の手順:それぞれ 1 回だけ呼び出し、貼り付けて実行
ネイティブ(Mach-O):
T=./bin; D=$TMPDIR/re/$(basename $T); mkdir -p $D; file $T; lipo -info $T; otool -L $T; echo '--- EXPORTS'; nm -gU $T | head -150; echo '--- IMPORTS'; nm -u $T | rg -v '^__Z|napi_' | tr '\n' ' '; echo; echo '--- STRINGS'; strings -a -n 6 $T | rg -v '^[_$.]' | head -400; otool -tV $T > $D/dis.txt; echo '--- FUNCS'; rg '^[_a-zA-Z].*:$' $D/dis.txt | rg -v '^_?_?Z?N?St3' | head -150
次に関数を 1 つ読みます:awk '/^FUNC_LABEL:$/{p=1;next} p&&/^[_a-zA-Z].*:$/{exit} p' $D/dis.txt | rg 'bl|b\.|cmp|literal|mov\s+w2'。
Electron:
A=./app.asar; X=$TMPDIR/re/asar; rm -rf $X; npx -y @electron/asar extract $A $X && jq '{name,version,main}' $X/package.json; find $X -path '*/node_modules' -prune -o -type f \( -name '*.js' -o -name '*.node' \) -print | head -60; ls ${A}.unpacked/node_modules 2>/dev/null; rg -uu -l --glob '!node_modules' 'exposeInMainWorld' $X
次に、バンドル全体から検索語の周辺を切り出します:rg -uu -o --glob '!node_modules' '.{0,250}NEEDLE.{0,250}' $X | head -20。
Go:通常は go version -m T だけで質問に答えられます。
ルーター
| 対象(検出方法) | 最速の手順 |
|---|---|
Electron アプリ(*.app/Contents/Resources/app.asar) | npx -y @electron/asar extract app.asar X(.node アドオンについては app.asar.unpacked/ も確認してください)。エントリーポイント:jq .main X/package.json。サーフェス:`rg -n -o '.{0,120}(ipcMain.(handle |
| Web バンドル/最小化された JS | sourceMappingURL を探します。マップに sourcesContent がある場合は、ソースを出力します:jq -r '.sources[]'。webpack の場合はモジュール ID を grep し、ヒットした箇所の前後を切り出します。整形するには:npx -y prettier --parser babel F > F.pretty.js を実行し、行範囲を指定して読みます。 |
| Hermes/bytenode/V8 スナップショット | Hermes のマジックナンバーは c6 1f bc 03(index.android.bundle)です。hermes-dec/hbc-decompiler を使います。.jsc(bytenode)は逆コンパイルできません。strings とランタイムフックを使います。 |
Mach-O(file の結果が Mach-O) | lipo -info(先に薄型化:lipo -thin arm64 -o T1 T)。続いて:otool -L(ライブラリ)、xcrun dyld_info -exports -imports、nm -gU(エクスポート)/nm -u(インポート=機能マップ)、strings -a -n 6、otool -l | rg -A3 'LC_(RPATH|BUILD_VERSION)'、codesign -d --entitlements :- T、otool -ov(ObjC クラス/メソッド)、xcrun swift-demangle --compact。 |
| ネイティブコードのロジック | otool -tV T > $D/dis.txt(または llvm-objdump -d --no-show-raw-insn -C)。関数を探します:rg -n '^_?.*Name.*:$'。呼び出し/文字列の手がかりを読み取ります:awk '/^FUNC:/{p=1} p&&/^[_a-zA-Z].*:$/&&!/FUNC/{exit} p' dis.txt | rg 'bl|call|literal|cmp'。otool は文字列リテラルとスタブをインラインで表示するため、多くの場合これで十分です。擬似コードが本当に必要な場合に限り、Ghidra の analyzeHeadless $D p -import T -postScript DecompileAll.java -deleteProject または r2 -qc 'aaa; pdc @ sym.X' T を使います。 |
| ELF/PE | readelf -hlSW、readelf -dW(NEEDED)、nm -D --defined-only、objdump -d -M intel。PE:objdump -x、リソースには pefile/wrestool。checksec:pwn checksec。 |
Go(__go_buildinfo、.go.buildinfo) | go version -m T を実行すると、ツールチェーン、パス、mod、依存関係、ビルドフラグを一度に取得できます。Go がない場合:strings -a T | rg -m1 -A200 'Go buildinf' 。シンボルは strip 後も残ります:go tool nm T | rg main\.。 |
| Rust | 依存関係(cargo-auditable):cargo audit bin T を実行するか、.dep-v0/__DATA,.dep-v0 を抽出して zlib で展開します。コンパイラー:strings -a T | rg -o '/rustc/[0-9a-f]{40}' | sort -u。パスからクレートを取得:strings -a T | rg -o '[a-z0-9_-]+-[0-9]+\.[0-9]+\.[0-9]+/src' | sort -u。 |
.NET(file の結果が Mono/.Net) | ilspycmd -p -o X T.dll(インストール:dotnet tool install -g ilspycmd)を実行し、その後 rg を使います。P/Invoke:rg DllImport X。NativeAOT/IL2CPP はネイティブです。ネイティブの行を参照してください。 |
| Android APK | jadx -d X --show-bad-code a.apk を実行し、その後 rg を使います。リソース/マニフェストのみの場合:apktool d --no-src -o X a.apk。簡易確認:unzip -l a.apk。lib/*/libflutter.so があれば Flutter(Dart AOT:blutter)、libhermes.so があれば React Native(Hermes の行を参照)です。 |
| Flutter スナップショットハッシュ | libapp.so 内でマジックナンバー f5 f5 dc dc を探します。+20 にある 32 桁の 16 進数がスナップショットハッシュです。対応する Dart バージョンを調べます。 |
| WASM | wasm-objdump -x、wasm2wat、wasm-decompile(WABT)。 |
| SQLite | DB をコピーします。存在する場合は -wal ファイルもコピーします。sqlite3 'file:C?mode=ro' '.schema' を実行し、続いて SELECT * FROM t LIMIT 20 を実行します。 |
| plist/nib/car | plutil -p F。Info.plist にはバンドル ID、URL スキーム、プライバシー文字列が含まれます。アセットカタログ:assetutil --info Assets.car。 |
| pcap/HAR | tshark -r C -q -z conv,tcp、tshark -r C -T fields -e dns.qry.name -e tls.handshake.extensions_server_name -e http.request.full_uri | sort -u。HAR:jq -r '.log.entries[].request | .method+" "+.url'。 |
| ファームウェア | binwalk -e F または unblob -e X F。その後、抽出された各バイナリをこの表に沿って調べます。 |
| Web サイト | HTML と script src を取得するには curl -sL URL を使い、その後 JS を取得して必要な箇所を切り出します。ランタイムの動作を確認するには、ネットワークログを記録するヘッドレスブラウザー(Playwright の page.on('request'))または Chrome の --remote-debugging-port を使います。 |
| 実行中のアプリ/ランタイム | macOS:lldb -p PID、dtrace/fs_usage/log stream --predicate 'process=="X"'。Electron のメインプロセス:--inspect(RunAsNode/inspect フューズが許可する場合のみ)。Android:adb logcat、frida -U -f pkg -l hook.js。 |
速度を重視したパターン
- まず文字列とインポートを確認します。小さなバイナリなら、
nm -u、strings、エクスポート一覧で、逆アセンブルせずに通常は「何をするのか」が分かります。 - 文字列を使用している箇所を見つけるには、
otool -tVの「literal pool for」コメントからアドレスを取得するか、rg -n 'literal pool for: "X'を使います。その行より上にある、対象を囲む関数ラベルを探します。 blのターゲットにある C++ の名前から、プロトコルがすでに分かる場合があります(例:getpeereid、write、std::hash)。すべての命令ではなく、呼び出しの流れを読みます。- JS では、ネイティブ/JS 境界から内側へたどります。ネイティブ呼び出しを見つけ、次にその呼び出し元のクラス、IPC の登録、レンダラー/プリロードの順に確認します。
- バージョンを比較するときは、コードを比較する前にインベントリ(
nm、モジュール一覧、go version -m)の差分を確認します。 - 再構築では、最小限の再実装を書き、オリジナルと照合してテストします(入力と出力を同じにします)。
インストール(リクエストがあった場合のみ)
brew install jadx apktool radare2 binwalk wabt wireshark ghidra · npm i -g @electron/asar · dotnet tool install -g ilspycmd · cargo install cargo-audit.
コメント
Markdown 対応