ghidra-retro-mcp

ghidra-retro-mcp

Enables AI assistants to perform headless reverse engineering of retro game ROMs using Ghidra, with automated platform detection and triage.

Category
访问服务器

README

Ghidra Retro MCP

MCP (Model Context Protocol) server that exposes Ghidra's headless analysis capabilities to AI assistants via pyhidra.

GBA ROMs: If analyzing Game Boy Advance ROMs, install 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.

Security Model

This server communicates exclusively over standard process stdio — there is no HTTP socket, no TCP listener, and no network interface exposed. It is inherently immune to LAN/WAN exposure, SSRF, and unauthenticated API attacks. The only way to interact with it is for an MCP client to launch it as a subprocess and communicate via stdin/stdout.

Hardware & Retro Ecosystem Integration

ghidra-retro-mcp includes native out-of-the-box support for retro-reversing automation pipelines. The server container bundles pre-compiled execution dependencies for:

  • Nintendo Entertainment System (NES) via GhidraNes
  • Super Nintendo Entertainment System (SNES) via native 65816 memory maps
  • Game Boy Advance (GBA) via gba-ghidra-loader
  • Nintendo DS (NDS) via NTRGhidra
  • Nintendo Switch via ghidra-switch-loader
  • PlayStation 1 (PSX) via ghidra_psx_ldr
  • Sega Genesis / Mega Drive via native 68000 memory maps
  • Sega Master System / Game Gear via Ghidra-SegaMasterSystem-Loader
  • Sega Dreamcast via native SuperH4 memory maps

Zero-Input Triage — Worked Example (GBA)

The primary entry point is triage_and_load_retro_rom. Call it with any ROM path and the server handles the rest:

# Auto-detect platform, map language, provision session
triage_and_load_retro_rom(rom_path="/data/game.gba")
# → platform: "Game Boy Advance (GBA)"
# → loader:   "GBA ROM Loader"
# → arch:     "ARM:LE:32:v4t"

# Decompile the main entry point on the same session
decompile_function(address="0x00001c2c")
# → decompiled C code for the GBA ROM entry routine

# Search for a known pattern (e.g. 32-bit ARM store-multiple)
search_bytes(pattern="09 08 00 01")
# → matching addresses labelled "gba_ram_start"

Execution Chaining Flow

Instead of forcing your AI agent to spend cycles manually identifying architecture maps, register layouts, or memory segments, chain the automated ingestion pipeline:

  1. Invoke triage_and_load_retro_rom with a target file path.
  2. 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.
  3. 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.

Triage Tool

Tool Description
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.

Quick Start

Local

pip install -e .
set GHIDRA_INSTALL_DIR=C:\path\to\ghidra   # Windows
ghidra-retro-mcp

Docker

docker build -t ghidra-retro-mcp .
docker run -i --rm -v /path/to/binaries:/data ghidra-retro-mcp

The container bundles JDK 17, Ghidra 11.2, and the server — no host dependencies beyond Docker.

Claude Desktop config

{
  "mcpServers": {
    "ghidra-headless": {
      "command": "ghidra-retro-mcp",
      "args": ["--ghidra-dir", "C:\\path\\to\\ghidra"],
      "env": {}
    }
  }
}

Tools

Session management

Tool Description
analyze_binary Import + analyze a binary, returns a session_id. Reuses the ID if provided, otherwise auto-generates.
list_sessions List all active workspaces with their session IDs, binary paths, and load times.
close_session Close a session and free its Ghidra project resources.

Most tools accept an optional session_id parameter — omit it to use the most recently loaded session.

Read / Analysis

Tool Description
decompile_function Decompile a function by name or address.
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.
get_data_types List all data types defined in the program.
get_cross_references Cross-references to/from an address.
get_call_graph Recursive call graph + callers for a function.
analyze_and_decompile_entrypoints Composite — bulk decompile all entry points (program entry, exports, main, _start, etc.) in one call.
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.

Write / Mutation

Tool Description
rename_symbol Rename a function or label. Stored in the Ghidra project DB.
add_comment Attach a comment (plate, pre, post, eol, repeatable).
create_struct Create a custom structured data type from a JSON member layout [{offset, name, type}, ...]. Offsets are optional.
retype_variable Re-type a local variable or function parameter (e.g. undefined4*MyStruct*).

Assembly-level

Tool Description
disassemble_range Disassemble N raw instructions at an address — returns mnemonic, operands, hex bytes, and length for precise lower-level inspection.
get_listing_range Raw hex + ASCII dump for a byte range, equivalent to Ghidra's Listing panel. Complements disassemble_range for data regions.

Byte-sequence search

Tool Description
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.

Binary diffing

Tool Description
diff_binaries Compare two loaded sessions by function name and body size. Returns functions unique to each side and changed functions.

Workspace Sessions

Each analyze_binary call creates a named session. Sessions keep their Ghidra project open independently, so multiple binaries can be loaded concurrently:

# Load two binaries into separate sessions
s1 = analyze_binary(binary_path="/bin/a.out")        # auto session_id
s2 = analyze_binary(binary_path="/bin/b.out", session_id="my_session")

# Operate on a specific session
decompile_function(function_name="main", session_id=s1.session_id)

# Diff them
diff_binaries(session_a=s1.session_id, session_b="my_session")

Deployment

Docker (multi-user / CI)

docker build -t ghidra-retro-mcp .

# Run as an MCP subprocess
docker run -i --rm \
  -v /data/binaries:/data \
  ghidra-retro-mcp \
  --ghidra-dir /opt/ghidra

The Dockerfile bundles Ghidra 11.2 and JDK 17 in a slim Python 3.11 image. Bind-mount your binaries directory at runtime.

P-code micro-emulation

Tool Description
emulate_slice Headlessly execute N instructions. Seed register state and get a step-by-step trace of register mutations.
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.
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.

All 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.

Worked example — breaking on a register condition

Suppose you're reversing a GBA ROM and want to find the first time r0 becomes zero inside a loop at 0x08000100:

# Step until r0 == 0, stop before the matching instruction
result = emulate_slice_with_breakpoints(
    session_id="gba_v1",
    start_address="0x08000100",
    max_instructions=5000,
    stop_condition="R0==0",
    stop_mode="before"
)
# result.exit_reason → "R0==0"
# result.instructions_executed → 312
# result.trace → [step 311: r0 goes 4→2, step 312: r0 goes 2→0]

# Check if a specific address was reached after a branch
result = emulate_slice_with_breakpoints(
    session_id="gba_v1",
    start_address="0x08000100",
    max_instructions=5000,
    stop_condition="PC==0x08001234"
)
# result.exit_reason → "PC==0x08001234"

# Use inequalities to catch bounds checks
result = emulate_slice_with_breakpoints(
    session_id="gba_v1",
    start_address="0x08000100",
    max_instructions=5000,
    stop_condition="R1>0xFF"
)
# result.exit_reason → "R1>0xFF"
# result.last_step["r1"] → 0x100

This is especially powerful for identifying copy-loop bounds (R3 >= R4), null-pointer paths (R0==0), or switch-table targets (PC==0x).

Function fingerprinting / signature transfer

Tool Description
calculate_function_fingerprint Generate a structural hash for a function (vars, params, body size, branches, called funcs, embedded strings, numeric constants). Survives compiler reordering.
export_signature_map Build a complete {hash → name} map for every function in the current binary. Save this JSON to reuse across versions.
apply_signature_map Pass a previously exported signature map; the server sweeps the binary and renames every matching function automatically.

Persistent signature stash (server-side cache)

Tool Description
save_active_binary_signature Fingerprint all functions and stash the map under a lineage_group_id (e.g. "my_firmware_v1"). Stored in ~/.ghidra_retro_mcp/signatures/ — no JSON files to manage.
auto_restore_signatures_from_stash Load a stashed map by lineage_group_id and auto-rename every matching function.
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.
auto_restore_current_binary Zero-input auto-restore — hashes the binary, looks up a previous stash, renames matches. No group ID needed.
list_stashed_signature_groups List all stashed groups currently in the local cache.

Workflow — fully automated persistence:

# Analyze v1 — stashes automatically under binary content hash
s1 = analyze_binary(binary_path="/bin/v1.bin")
auto_stash_current_binary(session_id=s1.session_id)

# Later, analyze v2 — restores automatically
s2 = analyze_binary(binary_path="/bin/v2.bin")
auto_restore_current_binary(session_id=s2.session_id)
# → 142 functions renamed, zero manual JSON handling

Demo

Claude Desktop requesting a GBA ROM triage and getting a decompiled function back

Claude Desktop: "Decompile the entry point of this GBA ROM and trace r0 propagation" — the server auto-detects the ARMv4t language, provisions a session, and returns decompiled C + taint trace.

Terminal output showing triage_and_load_retro_rom detecting a PSX EXE

Console output from triage_and_load_retro_rom detecting a PlayStation 1 executable (PS-X EXE magic), mapping MIPS:LE:32, and auto-restoring cached signatures.

Quick test

# Install
pip install ghidra-retro-mcp
# Requires Ghidra 11.2 + pyhidra; see Quick Start above.

# Start the server (stdio — pipe to an MCP client)
ghidra-retro-mcp

Configure Claude Desktop:

{
  "mcpServers": {
    "ghidra-retro": {
      "command": "ghidra-retro-mcp",
      "args": ["--ghidra-dir", "C:\\path\\to\\ghidra"],
      "env": {}
    }
  }
}

Then ask Claude:

  • "Load this GBA ROM and decompile the entry point."
  • "What functions call 0x8001234 in this NDS binary?"
  • "Triage this PSX EXE and trace r0 through the first 20 instructions."
  • "Diff the two sessions I have open and show me changed functions."

Project Structure

ghidra-retro-mcp/
├── Dockerfile
├── pyproject.toml
├── README.md
└── src/ghidra_retro_mcp/
    ├── __init__.py
    ├── server.py          # MCP server, tool registry, stdio transport
    ├── ghidra_bridge.py   # GhidraSession — pyhidra wrapper, all tool logic
    └── tools/
        └── __init__.py

How it works

  1. pyhidra.start() boots Ghidra's JVM once at server startup
  2. Each analyze_binary call opens a new Ghidra project in its own named session
  3. Read/write tools route to the requested session via session_id (or the active default)
  4. Write tools apply changes directly to the Ghidra program database
  5. Sessions persist until explicitly closed — enabling multi-binary workflows and diffing

<!-- mcp-name: io.github.getanirao/ghidra-retro-mcp -->

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选