{
  "markdown": "# Ghidra BizHawk MCP\n\nA unified MCP (Model Context Protocol) server bridging Ghidra's headless static analysis with BizHawk's live emulation — switch between decompiling a ROM and running it on real hardware in the same session.\n\n> **GBA ROMs**: If analyzing Game Boy Advance ROMs, install [pudii/gba-ghidra-loader](https://github.com/pudii/gba-ghidra-loader) in your Ghidra installation for proper ROM header parsing, mirrored memory regions, and I/O register maps. The loader repository has pre-built `.gpa` files for Ghidra 11.x.\n\n## Prerequisites\n\n| Dependency | Version | Required | Notes |\n|---|---|---|---|\n| Python | >= 3.10 | Yes | Runtime for the MCP server |\n| Ghidra | 11.x or 12.x | Yes | Headless or GUI install; `GHIDRA_INSTALL_DIR` must point here |\n| Java (JDK) | >= 17 | Yes | Bundled with Ghidra; needed for JVM bridge |\n| pyghidra | >= 3.0 | Yes | Python-to-Ghidra bridge; installed automatically |\n| BizHawk (EmuHawk) | Latest stable | No | Only needed for live emulation tools; `BIZHAWK_EXE_PATH` optional |\n| Docker | Latest | No | Only needed for containerized deployment |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        MCP Client (Claude / Cursor)              │\n│  sends JSON-RPC over stdin/stdout                                │\n└─────────────────────────────┬───────────────────────────────────┘\n                              │\n                              ▼\n┌──────────────────────────────────────────────────────────────────┐\n│                    ghidra-bizhawk-mcp                            │\n│  ┌─────────────────────────────────────────────────────────────┐│\n│  │   MCP Server (server.py) — tool registry, stdio dispatch    ││\n│  └──────────────────────────┬──────────────────────────────────┘│\n│                             │                                   │\n│              ┌──────────────┴──────────────┐                    │\n│              ▼                              ▼                   │\n│  ┌────────────────────┐    ┌──────────────────────────────┐    │\n│  │   GhidraSession    │    │   BizhawkBridge              │    │\n│  │   pyghidra → JVM   │    │   TCP client → localhost:8766│    │\n│  │   decompile, etc.  │    └──────────────┬───────────────┘    │\n│  └────────────────────┘                   │                     │\n└───────────────────────────────────────────┼─────────────────────┘\n                                            │ TCP (newline-delimited JSON)\n                                            ▼\n                              ┌──────────────────────────────┐\n                              │   BizHawk (EmuHawk.exe)       │\n                              │   built-in socket server      │\n                              │   ┌────────────────────────┐ │\n                              │   │  bridge.lua            │ │\n                              │   │  memory read/write     │ │\n                              │   │  joypad, savestate     │ │\n                              │   │  frame advance         │ │\n                              │   └────────────────────────┘ │\n                              └──────────────────────────────┘\n```\n\n## Security Model\n\nThe MCP server communicates with the MCP client **exclusively over stdin/stdout** — no HTTP or network listener. The only local TCP socket is a **loopback-only** connection (`127.0.0.1:8766`) between the server and BizHawk's built-in Lua socket server. This is used solely for live-emulation features and is not exposed to the network.\n\n## Hardware & Retro Ecosystem Integration\n\n`ghidra-bizhawk-mcp` includes native out-of-the-box support for retro-reversing automation pipelines via Ghidra's static analysis, plus live emulation via BizHawk's multi-system emulator. The server bundles:\n\n- **Nintendo Entertainment System (NES)** via `GhidraNes`\n- **Super Nintendo Entertainment System (SNES)** via native 65816 memory maps\n- **Game Boy Advance (GBA)** via `gba-ghidra-loader`\n- **Nintendo DS (NDS)** via `NTRGhidra`\n- **Nintendo Switch** via `ghidra-switch-loader`\n- **PlayStation 1 (PSX)** via `ghidra_psx_ldr`\n- **Sega Genesis / Mega Drive** via native 68000 memory maps\n- **Sega Master System / Game Gear** via `Ghidra-SegaMasterSystem-Loader`\n- **Sega Dreamcast** via native SuperH4 memory maps\n\n### Zero-Input Triage — Worked Example (GBA)\n\nThe primary entry point is `triage_and_load_retro_rom`. Call it with any ROM path and the server handles the rest:\n\n```python\n# Auto-detect platform, map language, provision session\ntriage_and_load_retro_rom(rom_path=\"/data/game.gba\")\n# → platform: \"Game Boy Advance (GBA)\"\n# → loader:   \"GBA ROM Loader\"\n# → arch:     \"ARM:LE:32:v4t\"\n\n# Decompile the main entry point on the same session\ndecompile_function(address=\"0x00001c2c\")\n# → decompiled C code for the GBA ROM entry routine\n\n# Search for a known pattern (e.g. 32-bit ARM store-multiple)\nsearch_bytes(pattern=\"09 08 00 01\")\n# → matching addresses labelled \"gba_ram_start\"\n```\n\n### Execution Chaining Flow\n\nInstead of forcing your AI agent to spend cycles manually identifying architecture maps, register layouts, or memory segments, chain the automated ingestion pipeline:\n\n1. Invoke `triage_and_load_retro_rom` with a target file path.\n2. The server headlessly parses the binary file structure (`NES\\x1a`, `NTR`, `NSO0`, `GBA`, SNES title vectors, `PS-X EXE`, `SEGA`, `TMR SEGA`, `SEGA ENTERPRISES`), binds the matching Ghidra language module (`6502:LE:16`, `ARM:LE:32:v4t`, `AARCH64:LE:64`, `65816:LE:24`, `MIPS:LE:32`, `68000:BE:32`, `Z80:16`, `SuperH4:LE:32`), loads standard address memory blocks, and links automated signature cache arrays.\n3. Use the integrated `emulate_slice` or `emulate_slice_with_taint` tools to analyze localized console loops — no physical console hardware or open GDB networking ports needed.\n\n### Triage Tool\n\n| Tool | Description |\n|---|---|\n| `triage_and_load_retro_rom` | Reads raw file magic bytes to detect NES, SNES, GBA, NDS, Switch, PSX, Genesis, SMS, or Dreamcast ROMs. Provisions a correctly-language-mapped Ghidra session and auto-restores cached function signatures. Returns platform, loader, architecture tag, and mapped memory blocks. |\n\n## Quick Start\n\n### 1. Install\n\n```bash\npip install ghidra-bizhawk-mcp\n```\n\nOr from source:\n\n```bash\ngit clone https://github.com/getanirao/ghidra-bizhawk-mcp.git\ncd ghidra-bizhawk-mcp\npip install -e .\n```\n\n### 2. Set environment\n\n```bash\n# Required: point to your Ghidra installation\nexport GHIDRA_INSTALL_DIR=/opt/ghidra_11.2    # Linux / macOS\nset GHIDRA_INSTALL_DIR=C:\\Program Files\\ghidra_11.2   # Windows\n\n# Optional: enable live BizHawk emulation\nexport BIZHAWK_EXE_PATH=/path/to/EmuHawk.exe\n\n# Optional: run in mock mode (no Ghidra/BizHawk needed)\nexport MOCK_MODE=1\n```\n\n### 3. Run\n\n```bash\nghidra-bizhawk-mcp\n```\n\nThe server listens on **stdin/stdout** — pipe it to any MCP-compatible client.\n\n### Docker\n\n```bash\ndocker build -t ghidra-bizhawk-mcp .\ndocker run -i --rm -v /path/to/binaries:/data ghidra-bizhawk-mcp\n```\n\nThe container bundles JDK 17, Ghidra 11.2, and the server — no host dependencies beyond Docker.\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `GHIDRA_INSTALL_DIR` | Yes | — | Path to Ghidra installation (e.g. `/opt/ghidra_11.2`) |\n| `BIZHAWK_EXE_PATH` | No | — | Path to EmuHawk.exe for live emulation features |\n| `MOCK_MODE` | No | `0` | Set to `1` to run without Ghidra/BizHawk (for testing/CI) |\n\n### Claude Desktop\n\nAdd to your `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"ghidra-bizhawk\": {\n      \"command\": \"ghidra-bizhawk-mcp\",\n      \"env\": {\n        \"GHIDRA_INSTALL_DIR\": \"/opt/ghidra_11.2\"\n      }\n    }\n  }\n}\n```\n\n### Cursor\n\nAdd to your Cursor MCP configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"ghidra-bizhawk\": {\n      \"command\": \"ghidra-bizhawk-mcp\",\n      \"env\": {\n        \"GHIDRA_INSTALL_DIR\": \"/opt/ghidra_11.2\"\n      }\n    }\n  }\n}\n```\n\n## Tools\n\n### Session management\n\n| Tool | Description |\n|---|---|\n| `analyze_binary` | Import + analyze a binary, returns a `session_id`. Reuses the ID if provided, otherwise auto-generates. |\n| `list_sessions` | List all active workspaces with their session IDs, binary paths, and load times. |\n| `close_session` | Close a session and free its Ghidra project resources. |\n\nMost tools accept an optional `session_id` parameter — omit it to use the most recently loaded session.\n\n### Read / Analysis\n\n| Tool | Description |\n|---|---|\n| `decompile_function` | Decompile a function by name or address. |\n| `decompile_function_paginated` | Decompile with `line_start`, `line_end`, `max_tokens` (token-budget truncation), and `summarize` (strips boilerplate locals + collapsing blank lines). Prevents context-window exhaustion. |\n| `get_data_types` | List all data types defined in the program. |\n| `get_cross_references` | Cross-references to/from an address. |\n| `get_call_graph` | Recursive call graph + callers for a function. |\n| `analyze_and_decompile_entrypoints` | Composite — bulk decompile all entry points (program entry, exports, `main`, `_start`, etc.) in one call. |\n| `generate_workspace_report` | Produce a Markdown summary of the active workspace — entry points, function count, custom symbols, recovered structures, renamed functions, comments. Replaces a GUI CodeBrowser window. |\n\n### Write / Mutation\n\n| Tool | Description |\n|---|---|\n| `rename_symbol` | Rename a function or label. Stored in the Ghidra project DB. |\n| `add_comment` | Attach a comment (`plate`, `pre`, `post`, `eol`, `repeatable`). |\n| `create_struct` | Create a custom structured data type from a JSON member layout `[{offset, name, type}, ...]`. Offsets are optional. |\n| `retype_variable` | Re-type a local variable or function parameter (e.g. `undefined4*` → `MyStruct*`). |\n\n### Assembly-level\n\n| Tool | Description |\n|---|---|\n| `disassemble_range` | Disassemble N raw instructions at an address — returns mnemonic, operands, hex bytes, and length for precise lower-level inspection. |\n| `get_listing_range` | Raw hex + ASCII dump for a byte range, equivalent to Ghidra's Listing panel. Complements `disassemble_range` for data regions. |\n\n### Byte-sequence search\n\n| Tool | Description |\n|---|---|\n| `search_bytes` | Search the entire binary for a hex byte pattern (e.g. `09 08 00 01` or `F86D0003`). Returns matching addresses with context bytes and any string label at the hit. |\n\n### Binary diffing\n\n| Tool | Description |\n|---|---|\n| `diff_binaries` | Compare two loaded sessions by function name and body size. Returns functions unique to each side and changed functions. |\n\n## Workspace Sessions\n\nEach `analyze_binary` call creates a named session. Sessions keep their Ghidra project open independently, so multiple binaries can be loaded concurrently:\n\n```python\n# Load two binaries into separate sessions\ns1 = analyze_binary(binary_path=\"/bin/a.out\")        # auto session_id\ns2 = analyze_binary(binary_path=\"/bin/b.out\", session_id=\"my_session\")\n\n# Operate on a specific session\ndecompile_function(function_name=\"main\", session_id=s1.session_id)\n\n# Diff them\ndiff_binaries(session_a=s1.session_id, session_b=\"my_session\")\n```\n\n## Deployment\n\n### Docker (multi-user / CI)\n\n```bash\ndocker build -t ghidra-bizhawk-mcp .\n\n# Run as an MCP subprocess\ndocker run -i --rm \\\n  -v /data/binaries:/data \\\n  ghidra-bizhawk-mcp \\\n  --ghidra-dir /opt/ghidra\n```\n\nThe `Dockerfile` bundles Ghidra 11.2 and JDK 17 in a slim Python 3.11 image. Bind-mount your binaries directory at runtime.\n\n### MCP Bundle (MCPB — Claude Desktop / Smithery)\n\nPackage as a portable `.mcpb` bundle for one-click install in Claude Desktop or publishing on [Smithery](https://smithery.ai).\n\n**Prerequisites:** Install the MCPB CLI:\n\n```bash\nnpm install -g @anthropic-ai/mcpb\n```\n\n**Build the bundle:**\n\n```bash\n# From the repo root\nscripts/build-mcpb.ps1\n```\n\nOr manually with `mcpb`:\n\n```bash\nmcpb pack\n```\n\nThe output `ghidra-bizhawk-mcp.mcpb` wraps the server with a `manifest.json` that prompts for `GHIDRA_INSTALL_DIR` (required) and optionally `BIZHAWK_EXE_PATH` at install time — no manual JSON editing.\n\n**Publishing to Smithery:**\n\n```bash\nsmithery mcp publish ./dist/ghidra-bizhawk-mcp.mcpb -n getanirao/ghidra-bizhawk-mcp\n```\n\n### P-code micro-emulation\n\n| Tool | Description |\n|---|---|\n| `emulate_slice` | Headlessly execute N instructions. Seed register state and get a step-by-step trace of register mutations. |\n| `emulate_slice_with_taint` | Same as `emulate_slice` but with automated taint tracking — specify a taint register (e.g. `r0`) and the tool flags exactly when its value is modified or propagates to other registers. |\n| `emulate_slice_with_breakpoints` | Execute until a condition is met or the count expires. Condition syntax: `R0==0`, `R1>0xFF`, `R2!=R3`, `PC==0x1234`. Stops before or after the matching instruction. |\n\nAll run inside the pyhidra process via Ghidra's `EmulatorHelper` — no GDB/LLDB, no network ports, no debugger stubs. Works on ARM, x86, MIPS, and any Ghidra-supported architecture.\n\n#### Worked example — breaking on a register condition\n\nSuppose you're reversing a GBA ROM and want to find the first time `r0` becomes zero inside a loop at `0x08000100`:\n\n```python\n# Step until r0 == 0, stop before the matching instruction\nresult = emulate_slice_with_breakpoints(\n    session_id=\"gba_v1\",\n    start_address=\"0x08000100\",\n    max_instructions=5000,\n    stop_condition=\"R0==0\",\n    stop_mode=\"before\"\n)\n# result.exit_reason → \"R0==0\"\n# result.instructions_executed → 312\n# result.trace → [step 311: r0 goes 4→2, step 312: r0 goes 2→0]\n\n# Check if a specific address was reached after a branch\nresult = emulate_slice_with_breakpoints(\n    session_id=\"gba_v1\",\n    start_address=\"0x08000100\",\n    max_instructions=5000,\n    stop_condition=\"PC==0x08001234\"\n)\n# result.exit_reason → \"PC==0x08001234\"\n\n# Use inequalities to catch bounds checks\nresult = emulate_slice_with_breakpoints(\n    session_id=\"gba_v1\",\n    start_address=\"0x08000100\",\n    max_instructions=5000,\n    stop_condition=\"R1>0xFF\"\n)\n# result.exit_reason → \"R1>0xFF\"\n# result.last_step[\"r1\"] → 0x100\n```\n\nThis is especially powerful for identifying copy-loop bounds (`R3 >= R4`), null-pointer paths (`R0==0`), or switch-table targets (`PC==0x`).\n\n### Function fingerprinting / signature transfer\n\n| Tool | Description |\n|---|---|\n| `calculate_function_fingerprint` | Generate a structural hash for a function (vars, params, body size, branches, called funcs, embedded strings, numeric constants). Survives compiler reordering. |\n| `export_signature_map` | Build a complete `{hash → name}` map for every function in the current binary. Save this JSON to reuse across versions. |\n| `apply_signature_map` | Pass a previously exported signature map; the server sweeps the binary and renames every matching function automatically. |\n\n### Persistent signature stash (server-side cache)\n\n| Tool | Description |\n|---|---|\n| `save_active_binary_signature` | Fingerprint all functions and stash the map under a `lineage_group_id` (e.g. `\"my_firmware_v1\"`). Stored in `~/.ghidra_bizhawk_mcp/signatures/` — no JSON files to manage. |\n| `auto_restore_signatures_from_stash` | Load a stashed map by `lineage_group_id` and auto-rename every matching function. |\n| `auto_stash_current_binary` | **Zero-input auto-stash** — hashes the binary's first 4 KB, saves a map under that hash. Just analyze and call. |\n| `auto_restore_current_binary` | **Zero-input auto-restore** — hashes the binary, looks up a previous stash, renames matches. No group ID needed. |\n| `list_stashed_signature_groups` | List all stashed groups currently in the local cache. |\n\n**Workflow — fully automated persistence:**\n\n```python\n# Analyze v1 — stashes automatically under binary content hash\ns1 = analyze_binary(binary_path=\"/bin/v1.bin\")\nauto_stash_current_binary(session_id=s1.session_id)\n\n# Later, analyze v2 — restores automatically\ns2 = analyze_binary(binary_path=\"/bin/v2.bin\")\nauto_restore_current_binary(session_id=s2.session_id)\n# → 142 functions renamed, zero manual JSON handling\n```\n\n\n\n### Try these prompts\n\nAfter configuring your MCP client (see [Configuration](#configuration)), ask your AI agent:\n- *\"Load this GBA ROM and decompile the entry point.\"*\n- *\"What functions call 0x8001234 in this NDS binary?\"*\n- *\"Triage this PSX EXE and trace r0 through the first 20 instructions.\"*\n- *\"Diff the two sessions I have open and show me changed functions.\"*\n\n## Project Structure\n\n```\nghidra-bizhawk-mcp/\n├── Dockerfile\n├── pyproject.toml\n├── README.md\n└── src/ghidra_bizhawk_mcp/\n    ├── __init__.py\n    ├── server.py          # MCP server, tool registry, stdio transport\n    ├── ghidra_bridge.py   # GhidraSession — pyghidra wrapper, all Ghidra logic\n    ├── lua/\n    │   └── bridge.lua     # BizHawk-side Lua bridge for live emulation\n    └── tools/\n        ├── __init__.py\n        ├── bizhawk_bridge.py  # TCP client connecting MCP ↔ BizHawk\n        └── ...\n```\n\n## How it works\n\n1. `pyhidra.start()` boots Ghidra's JVM once at server startup\n2. Each `analyze_binary` call opens a new Ghidra project in its own named session\n3. Read/write tools route to the requested session via `session_id` (or the active default)\n4. Write tools apply changes directly to the Ghidra program database\n5. Sessions persist until explicitly closed — enabling multi-binary workflows and diffing\n\n<!-- mcp-name: io.github.getanirao/ghidra-bizhawk-mcp -->\n",
  "bytes": 17433,
  "sha": "278f98661784fae3e2e1c47bc9aeeab1fa2fbe1b7086fb1b615cd086b9a6433d",
  "repo_slug": "getanirao/ghidra-retro-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_getanirao_ghidra_retro_mcp_d7a11524/readme"
}