GhidraMCP+
README.md
# GhidraMCP+ — Ghidra to MCP Bridge with Evidence Capture
An upgraded fork of [LaurieWired/ghidraMCP](https://github.com/LaurieWired/ghidraMCP), extended for static malware analysis workflows that require reproducible evidence: annotated screenshots, verifiable findings, and structured reports.
> **Credit:** This project is built on top of LaurieWired/ghidraMCP (Apache 2.0). The original Ghidra plugin and MCP bridge are LaurieWired's work; this fork keeps that foundation and adds the features listed below. Thank you, LaurieWired, for open-sourcing the groundwork.
[](https://www.apache.org/licenses/LICENSE-2.0)

*Automatic red highlight on the exact evidence line — the new `/evidence` endpoint (address-range mode shown).*
## What This Fork Adds
| Feature | Original (LaurieWired) | This fork |
|---|---|---|
| Screenshot of the Ghidra window | no | `/screenshot` |
| Go to an address (like pressing G) | no | `/focus` |
| Red highlight on evidence plus capture in one call | no | `/evidence` (uses official `FieldPanel.getCursorBounds()`) |
| Red caption above the box | no | `label` parameter |
| Range highlight (multiple lines) | no | `to` parameter |
| HTTP server binding | all interfaces | loopback only (`127.0.0.1`) |
| Request body limit | none | 1 MB max |
| Output path validation in the bridge | none | blocks `/etc`, `/proc`, `/sys`, `/dev`, `C:\Windows`, `..` |
| One-command launcher (Linux/macOS) | no | `start-mcp.sh` |
| One-command launcher (Windows) | no | `start-mcp.ps1` |
| Wait for the plugin HTTP to be alive | no | polls up to 30 s, has `--check` mode |
| Build without Maven | requires Maven | `.dsh-build.sh` (plain javac + jar) |
| Low-RAM GUI launcher | no | `ghidra-safe.sh` (heap capped at 1.2 GB) |
## Architecture
```
+-------------+ MCP stdio +----------------------+ HTTP 127.0.0.1:8080 +------------------+
| dsh/Claude | -------------> | bridge_mcp_ghidra.py | ---------------------> | Ghidra GUI |
| (AI client) | 30 tools | (FastMCP) | GhidraMCP+ plugin | + CodeBrowser |
+-------------+ +----------------------+ +------------------+
```
- The **plugin** (Java) runs inside the Ghidra GUI. Its HTTP server is only alive while a CodeBrowser tool has a program open; headless mode does not load it.
- The **bridge** (Python) exposes 30 MCP tools: decompile, rename, cross-references, strings, segments, imports, and three imaging tools: `focus_address`, `screenshot_tool`, `capture_evidence`.
- HTTP listens on loopback only; no other machine on the LAN can drive your Ghidra instance.
## Installation
### Requirements
- [Ghidra](https://ghidra-sre.org) (tested with 11.3.1)
- Python 3.10 or newer
- JDK 21 (only when building from source)
### Step 1 — Build the plugin (no Maven needed)
```bash
./.dsh-build.sh
# Output: target/GhidraMCP.jar and target/GhidraMCP-1.0-SNAPSHOT.zip
```
The script copies the required Ghidra jars into `lib/`, compiles with `javac`, and packages the extension zip in the layout Ghidra expects.
### Step 2 — Install the extension into Ghidra
GUI route: `File -> Install Extensions -> (+) -> select target/GhidraMCP-1.0-SNAPSHOT.zip -> restart Ghidra`
Terminal route (Linux):
```bash
SETTINGS=~/.config/ghidra/ghidra_11.3.1_PUBLIC
mkdir -p $SETTINGS/Extensions/GhidraMCP/lib
cp target/GhidraMCP.jar $SETTINGS/Extensions/GhidraMCP/lib/
cp src/main/resources/{extension.properties,Module.manifest} $SETTINGS/Extensions/GhidraMCP/
```
Enable the plugin: `File -> Configure -> Developer -> GhidraMCPPlugin`.
### Step 3 — Run the full pipeline (one command)
```bash
# Linux / macOS
./start-mcp.sh # waits for the plugin HTTP, then runs the MCP bridge (stdio)
# Windows (PowerShell 5.1+)
.\start-mcp.ps1 # same behavior
```
Options: `--check` (verify connectivity, do not run the bridge) | `--install-deps` (install `requests` + `mcp`) | `--transport sse` (run SSE on port 8081 instead of stdio).
Both launchers pick Python automatically (project `.venv`, then `py -3` / `python3`, then `python`), poll `http://127.0.0.1:8080/` for up to 30 seconds, and print clear guidance when Ghidra or the CodeBrowser tool is not running yet.
### Low-RAM machines
```bash
./ghidra-safe.sh # runs the Ghidra GUI with the heap capped at 1.2 GB (override with _GHIDRA_MAXMEM=800M)
```
## Evidence Capture
The headline feature of this fork: capture a CodeBrowser screenshot with a red highlight box drawn around the exact evidence line. It uses Ghidra's official API (`CodeViewerService -> ListingPanel -> FieldPanel.getCursorBounds()`), so the box lands on the real address line every time, without guessing.
```
GET http://127.0.0.1:8080/evidence?address=0x0044c51e&to=0x0044c534&label=EX-01%20Entry&lines=6
```
| Parameter | Purpose |
|---|---|
| `address` | evidence address to focus (required); the listing scrolls as if G was pressed |
| `to` | end address of a range; the red box then covers the whole range |
| `label` | red caption drawn above the box |
| `lines` | context lines (default 6) |
Returns a PNG of the CodeBrowser window with:
- a 3 px red border plus a translucent red fill over the evidence line(s)
- the red caption above the box when `label` is given
From an MCP client (dsh, Claude, and others), call the tool:
```python
capture_evidence(
address="0x0044c51e",
output_path="reports/exhibits/EX-01.png",
label="Entry stub jmp _CorExeMain",
to="0x0044c534" # optional range
)
```

*Single-line highlight example — the high-entropy IL region (entropy 7.6) from a Lokibot analysis.*
## RE Workflow Training — How to Drive This Toolchain
This section documents the reverse-engineering methodology this fork was built for. It assumes an AI agent (or a disciplined human analyst) drives Ghidra through the MCP tools. The workflow has six phases.
### Phase 0 — Safety first
- Never execute malware samples on a real machine. Static analysis or an isolated sandbox only.
- Hash the sample before anything else (`md5sum`, `sha256sum`) and reference it by hash, not by filename, in every report.
- The HTTP endpoints of this plugin can rename and annotate program data. Keep the loopback binding; do not expose it to a network.
### Phase 1 — Triage before Ghidra
Do not open the binary in Ghidra first. Triage the raw bytes first:
```bash
md5sum sample.exe && sha256sum sample.exe # record hashes before anything else
file sample.exe # PE32? .NET? packed?
strings -n 8 sample.exe | head -50 # quick IOC scan
```
For .NET binaries, parse the metadata before Ghidra: find the `BSJB` metadata root, walk the streams (`#~`, `#Strings`, `#US`, `#GUID`, `#Blob`), and enumerate MethodDef tokens and resources. Ghidra shows managed code as meaningless x86 data, so the metadata is the real entry point for .NET analysis. A large `__StaticArrayInitTypeSize` value combined with reflection names (`GetTypes`, `Invoke`, `InitializeArray`) indicates an obfuscator that moved IL bodies into a static array.
Core rule of the whole workflow: **every claim must trace back to a real byte region with an address. If you cannot point to it, it does not go in the report.**
### Phase 2 — Choose the right approach
Match the target to the right technique before touching tools:
| Target type | Approach |
|---|---|
| Native x86/x64 PE | Ghidra MCP tools: `list_imports`, `list_strings`, `decompile_function`, `get_xrefs_to` |
| .NET assembly | Python metadata parsing first; Ghidra MCP only for the native stub and embedded data |
| Packed / obfuscated | Identify the packer layer by layer; each layer gets its own evidence exhibit |
| APK / mobile | Decompile the managed layer; Ghidra only for native `.so` libraries |
### Phase 3 — Evidence collection
Work exhibit-first. For each finding:
1. Locate the exact address or byte range that proves the claim.
2. Capture it: `capture_evidence(address="0x...", label="F-01 XOR loop", to="0x...")`.
3. Verify the capture programmatically: count red pixels in the PNG. A valid evidence image has roughly 500-900 red pixels plus dark text pixels inside the box. Zero red pixels means the fallback banner fired and the highlight failed; retake the shot.
4. Excerpts from tools go into the report verbatim. Never edit decompiler output; put analyst notes in separate annotation bullets below the code block.
### Phase 4 — Analysis discipline
- Follow the call chain: entry point first, then each hop, collecting one exhibit per meaningful step.
- Name things as you go: `rename_function`, `rename_local_variable`, and `set_decompiler_comment` make later exhibits self-explanatory.
- Record what you tried and failed (for example: XOR scan of 256 single-byte keys, RC4 with candidate keys, zlib inflate). Failed decryption attempts are findings too; they tell the next analyst what not to repeat.
- Time-box static analysis. If a payload cannot be decrypted statically within the box, stop and document the boundary instead of guessing.
### Phase 5 — Report structure
A complete RE report has nine sections:
1. Executive summary — what the sample is, what it does, what remains unknown, plus a capability table with evidence links
2. Sample information — hashes, type, sections, entry point
3. Exhibits — each with a repro command, an address, a verbatim excerpt, an annotated screenshot, and an interpretation
4. Technical analysis — identification, mechanism by phase, crypto/algorithms, dynamic results (or why they are absent)
5. Findings table — each finding mapped to an exhibit and a MITRE ATT&CK technique, with a confidence level
6. IOC table — every indicator traced back to an exhibit
7. Callflow — numbered steps from entry point to final behavior, unverified steps clearly marked
8. Open questions — everything that did not make it into findings, with next-step proposals
9. Appendix — environment, full reproduction commands, optional YARA rule
The two hard rules: **no finding without an exhibit, and no claim without an address.**
## HTTP Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
| `/methods` | GET | list functions (paginated) |
| `/classes` | GET | list namespaces/classes |
| `/segments` `/imports` `/exports` `/strings` | GET | list program data |
| `/xrefs` `/calls` | GET | cross-references |
| `/decompile` | POST | decompile a function by name |
| `/renameFunction` `/renameData` and related | POST | renames |
| `/comment` `/setFunctionPrototype` | POST | annotations |
| `/focus` | GET | scroll the listing to an address (G key) |
| `/screenshot` | GET | PNG of the CodeBrowser window |
| `/evidence` | GET | red-highlight capture (new) |
| `/windows` | GET | debug: list Ghidra windows |
See `src/main/java/com/lauriewired/GhidraMCPPlugin.java` for the complete list.
## MCP Tools (30)
Analysis: `list_methods` | `list_classes` | `list_segments` | `list_imports` | `list_exports` | `list_strings` | `list_data_items` | `list_namespaces` | `decompile_function` | `disassemble_function` | `search_functions_by_name` | `get_xrefs_to` | `get_xrefs_from` | `get_function_xrefs` | `get_callers` | `get_function_by_address` | `get_current_address` | `get_current_function`
Editing: `rename_function` | `rename_data` | `rename_local_variable` | `rename_global_variable` | `rename_function_by_address` | `set_function_prototype` | `set_decompiler_comment` | `set_disassembly_comment` | `set_local_variable_type`
Imaging (new): `focus_address` | `screenshot_tool` | `capture_evidence`
## Mounting an MCP Client
**Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"ghidra": {
"command": "python",
"args": ["/ABSOLUTE_PATH/bridge_mcp_ghidra.py", "--ghidra-server", "http://127.0.0.1:8080/"]
}
}
}
```
**Cline / SSE mode:**
```
python bridge_mcp_ghidra.py --transport sse --mcp-host 127.0.0.1 --mcp-port 8081 --ghidra-server http://127.0.0.1:8080/
```
Then add the remote server `http://127.0.0.1:8081/sse` in Cline.
**dsh (DeepSeek Harness)** — profile row:
```yaml
- id: mcp-ghidra
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: ghidra
transport: stdio
command: '/path/to/ghidraMCP/.venv/bin/python'
args: ['/path/to/ghidraMCP/bridge_mcp_ghidra.py', '--ghidra-server', 'http://127.0.0.1:8080/']
toolCallTimeoutMs: 120000
failOnStartupError: false
```
## Security Notes
- **Loopback only.** The HTTP server binds `127.0.0.1`; other machines on the LAN cannot drive Ghidra. Renames and annotations mutate real program data, so do not relax this binding.
- **Body limit.** POST bodies over 1 MB are rejected to avoid memory pressure on the Ghidra JVM.
- **Path validation.** The bridge refuses to write screenshots to `/etc`, `/proc`, `/sys`, `/dev`, `C:\Windows`, any path containing `..`, and any non-`.png` output.
## Acknowledgments
- **[LaurieWired/ghidraMCP](https://github.com/LaurieWired/ghidraMCP)** — the original plugin and bridge this fork builds on. Apache 2.0.
- The Ghidra project for the public API, in particular `FieldPanel.getCursorBounds()`, which made precise evidence highlighting possible.
## License
Apache 2.0 — inherited from the original project.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues