hopper-debugger について
hopper-debugger は、Hopper でバイナリを調査し、ソースコードや実行時の証拠と照合する開発者向けスキルです。mcporter と Hopper 内蔵の MCP サーバーを使うクエリやトラブルシューティングの手順をまとめています。
主な機能
- 01逆アセンブル文書のクエリ
- 02プロシージャと文字列の検索
- 03擬似コードとプロシージャ情報の確認
- 04LLDB と実行時の証拠による検証
- 05MCP 呼び出しのトラブル対応
活用シーン
- 01シンボルのバイナリ実装を調査
- 02macOS フレームワークの動作を調査
- 03実行時プローブで逆アセンブルの仮説を検証
MCP 対応
mcporter 経由で Hopper 6.0+ 内蔵の MCP サーバーに接続し、stdio で逆アセンブルのクエリツールを呼び出します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ steipete/ agent-scripts --skill hopper-debuggerインストール後、Claude Code で/hopper-debuggerを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yaml
詳細
Hopper Debugger
mcporter を通じて Hopper をクエリ可能な逆アセンブラーとして使用し、ローカルのソース、LLDB、ログ、対象を絞った再現手順と組み合わせます。
セットアップ(初回のみ)
Hopper 6.0+ には独自の MCP サーバーが同梱されています。サードパーティ製のものはインストールしないでください。
mcporter config add hopper --scope home \
--command "/Applications/Hopper Disassembler.app/Contents/MacOS/HopperMCPServer" \
--description "Hopper Disassembler built-in MCP server (stdio)"
--scope home は必須です。デフォルトのスコープは project で、作業中のリポジトリに config/mcporter.json を書き込みます(追跡されないリポジトリの変更が残り、ワークツリーとともに失われます)。
確認します。
MCPORTER_LIST_TIMEOUT=25000 timeout 40 mcporter list hopper --brief
呼び出し規約 — ここでつまずきます
引数は常に --args で渡してください。--params は使わないでください。 mcporter は不明な --params フラグを黙って受け入れ、ペイロードを破棄するため、引数なしで呼び出されます。すると Hopper は Document not found. と応答します。これはライセンスや状態の問題のように見えますが、そうではありません。
# WRONG — arguments silently dropped, fails with "Document not found."
mcporter call hopper.list_segments --params '{"document":"AppKit"}'
# RIGHT
mcporter call hopper.list_segments --args '{"document":"AppKit"}'
関連事項:サーバーは現在のドキュメントにフォールバックしません。current_document が有効な名前を返す場合でも、document 引数なしの呼び出しは失敗します。ドキュメントを対象とする呼び出しでは、毎回 document を渡してください。
ドキュメント名は list_documents から取得でき、.hop 拡張子は含まれません(ウィンドウタイトルが AppKit.hop → ドキュメント名は AppKit)。
それでも呼び出しが失敗する場合は、サーバーに stdio 経由で直接アクセスして、生の JSON-RPC を確認します。これにより mcporter を完全に迂回し、原因の所在を切り分けられます。
"/Applications/Hopper Disassembler.app/Contents/MacOS/HopperMCPServer" <<'EOF'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"p","version":"1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_documents","arguments":{}}}
EOF
ライセンス — 誤診しないでください
Hopper 6.x でも、バンドル ID と環境設定ドメインには com.cryptic-apps.hopper-web-4 が使われています。hopper-web-6 ドメインが存在しないからといって、アプリのライセンスがないという意味ではありません。About パネル(Hopper Disassembler > About Hopper Disassembler)で実際の状態を確認してください。ライセンシー、注文 ID、アップデートプランの有効期限が表示されます。Hopper 6 には「登録」/「ライセンス」メニュー項目はありません。
Peter のライセンス:注文 HOP140213-7833-95831、アップデートは 2027-05-17 まで有効です。.hopperLicense ファイルは 1Password の「Molty」ボルトにあるドキュメント項目「Hopper Disassembler License (HOP140213-7833-95831)」にあります。$one-password を読み込むと取得できます。.hopperLicense ファイルを Hopper で開いても登録されません。Hopper はそれをドキュメントとして逆アセンブルします。
ドキュメントを開く
open -a "Hopper Disassembler" /path/to/Binary
小さなバイナリはダイアログなしで読み込まれます。大きなフレームワークは数分かかるため、待機するのではなくポーリングしてください。
until mcporter call hopper.list_documents --output json 2>/dev/null | grep -qi "appkit"; do sleep 10; done
初回に表示されるダイアログは、合成クリックではなく Accessibility のプレス操作で閉じてください。クリックすると Peter の物理ポインターが動きます。
osascript -e 'tell application "System Events" to tell process "Hopper Disassembler" to perform action "AXPress" of (button 1 of window 1)'
Apple フレームワーク
Apple フレームワークはディスク上ではなく、dyld 共有キャッシュ内にあります。方法は 2 つあります。
- Peter が事前に作成したエクスポート(
~/Library/CloudStorage/Dropbox/Hopper/)を優先します。AppKit、AccessibilityKit などの.hopドキュメントと.m擬似コードダンプがあります。.mを grep するほうが、MCP のラウンドトリップより速いことがよくあります(AppKit.mは 244 MB です)。 バージョンを調べる際は、信頼する前に出所を確認してください。 これらはスナップショットです。2026-08 時点で macOS 26.6 と 27.0 より古く、クラス/メソッドの一覧は両方の実行環境と異なります。構造の確認には適していますが、OS バージョン間の差分調査には信頼できません。 /usr/lib/dsc_extractor.bundleを使って新たに抽出します(macOS に存在します。すべての dylib を抽出するため、数 GB の容量が必要で、時間もかかります)。
クエリのワークフロー
- 説明しようとしているローカルのソースパスまたは実行時シンボルから始めます。
- シンボルを見つけてから、一度に小さな対象を 1 つずつ調べます。
mcporter call hopper.search_procedures --args '{"document":"AppKit","pattern":"addCursorRect"}' --output json
mcporter call hopper.procedure_pseudo_code --args '{"document":"AppKit","procedure":"0x185475b2c"}' --output json
procedure はシンボル名または 16 進アドレスを受け付けます。ほかに便利なツール:list_documents、current_document、set_current_document、list_segments、list_procedures、list_strings、search_strings、procedure_info、procedure_address、current_procedure。
- 関連する制御フローを要約し、大きな逆コンパイル結果を貼り付けないでください。
- アプリのコードを編集する前に、LLDB、ログ、再現手順で仮説を検証します。
ランタイムの証拠との照合
逆アセンブルからは値がどのストアに格納されるかがわかりますが、実際に格納されたかどうかはランタイムでしかわかりません。まず疑似コードを読んで、API が実際にどの ivar/コレクションに書き込むのかを確認し、次に class_copyIvarList + object_getIvar + perform を使って、ランタイムでそのストアを直接読み取ります。誤った(レガシーな)経路を計測するのは、よくある時間の浪費です。最新の AppKit では -[NSWindow _addCursorRect:cursor:forView:] は使われておらず、カーソル矩形は _NSTrackingAreaAKViewHelper の cursorAreas セットに格納されています。
「リグレッション」と結論づける前に、必ず異なる OS バージョンの別のマシンでも同じプローブを実行してください。既知の正常な OS と既知の不具合がある OS で、何もしないプローブの結果が同じに見えることがあります。プローブが何も計測していないとわかるのは、対照実験を実施した場合です。マシン群については $remote-mac を、カーソル計測ハーネスについては codexbar-ui-verification-quirks のメモを参照してください。
障害への対処
- モーダルダイアログやインポートによってトランスポートが停止したままになることがあるため、Hopper の呼び出しは
timeoutで囲んでください。 - インポート中に Hopper MCP リクエストを並行して送信しないでください。
Connection closedは、通常、Hopper が起動していないか、モーダルダイアログが表示されていることを意味します。System Events でウィンドウを確認してから、再試行してください。Document not found.は、ほとんどの場合、ドキュメントの破損ではなく引数の不足を意味します(上記の--argsを参照)。- mcporter が応答しなくなった場合は、広範囲にプロセスを終了させるより、デーモンを再起動してください。
mcporter daemon stop && mcporter daemon start