- Change from 'except Exception' to bare 'except' to catch Java exceptions from Ghidra that don't inherit from Python Exception - Use sys.exc_info() to safely extract error messages when str(e) might fail on certain Java exception types - Add null checks after getAddress() since it can return None instead of throwing for invalid addresses - Add last-resort response handling to prevent silent connection drops when exception handling itself fails These endpoints now return proper JSON error responses instead of causing "Empty reply from server" errors.
MCGhidra Docker Setup
This directory contains Docker configuration for running MCGhidra in headless mode.
Quick Start
# Build the image
docker build -t mcghidra:latest -f docker/Dockerfile .
# Analyze a binary
docker run -p 8192:8192 -v /path/to/binaries:/binaries mcghidra /binaries/sample.exe
# Check API health
curl http://localhost:8192/
Architecture
The Docker container includes:
- Ghidra 11.4.2 - Full headless installation
- MCGhidra Extension - The Java plugin (installed in Extensions/)
- MCGhidraServer.py - Headless HTTP server (Jython, full API parity)
Why Two HTTP Servers?
The MCGhidra plugin (MCGhidraPlugin.java) is a full Ghidra GUI plugin that requires:
- Ghidra's
PluginToolframework ProgramManagerservice for program access- GUI event handling
These GUI services don't exist in headless mode. Instead, the container uses MCGhidraServer.py, a Jython script that:
- Runs via
analyzeHeadless -postScript - Has direct access to
currentProgramfrom the script context - Provides full API parity with the GUI plugin (45 routes)
- Supports all read and write operations
Available Endpoints (Headless Mode)
The headless server implements the complete MCGhidra HTTP API:
| Category | Endpoints | Description |
|---|---|---|
| Info | GET /, /info, /program |
API info, program metadata |
| Functions | GET /functions, /functions/{addr}, /functions/by-name/{name} |
List and detail |
| Decompile | GET /functions/{addr}/decompile, /functions/by-name/{name}/decompile |
C pseudocode |
| Disassembly | GET /functions/{addr}/disassembly, /functions/by-name/{name}/disassembly |
Assembly listing |
| Data | GET /data, /strings |
Defined data and strings |
| Memory | GET /memory, /memory/blocks |
Read bytes, list segments |
| Xrefs | GET /xrefs |
Cross-references (to/from) |
| Structs | GET /structs |
Data type structures |
| Symbols | GET /symbols, /imports, /exports |
Symbol tables |
| Analysis | GET /analysis/callgraph, /analysis/dataflow |
Static analysis |
| Write Ops | PATCH /functions/*, POST /data, POST /structs/* |
Rename, annotate, create |
See GHIDRA_HTTP_API.md for the complete API specification.
Container Modes
Headless Mode (Default)
Imports a binary, analyzes it, and starts the HTTP API server:
docker run -p 8192:8192 \
-v ./samples:/binaries \
mcghidra /binaries/sample.exe
Server Mode
Opens an existing project and program:
docker run -p 8192:8192 \
-e MCGHIDRA_MODE=server \
-v ./projects:/projects \
mcghidra program_name
Analyze Mode
Imports and analyzes without starting HTTP server:
docker run \
-e MCGHIDRA_MODE=analyze \
-v ./samples:/binaries \
-v ./projects:/projects \
mcghidra /binaries/sample.exe
Shell Mode
Interactive debugging:
docker run -it \
-e MCGHIDRA_MODE=shell \
mcghidra
Environment Variables
| Variable | Default | Description |
|---|---|---|
MCGHIDRA_MODE |
headless |
Container mode (headless, server, analyze, shell) |
MCGHIDRA_PORT |
8192 |
HTTP API port |
MCGHIDRA_MAXMEM |
2G |
JVM heap memory |
PROJECT_DIR |
/projects |
Ghidra project directory |
PROJECT_NAME |
MCGhidra |
Ghidra project name |
Docker Compose
Use docker-compose for easier management:
# Development mode (hot-reload scripts)
docker compose --profile dev up mcghidra-dev
# Production mode
docker compose --profile prod up mcghidra
# Interactive shell
docker compose --profile debug run --rm mcghidra-shell
MCP Integration
The MCGhidra Python server includes Docker management tools:
# Check Docker status
await docker_status()
# Start container for a binary
await docker_start(binary_path="/path/to/binary.exe", port=8192)
# Wait for container to be ready
await docker_wait(port=8192, timeout=300)
# Automatic mode - starts container if no Ghidra available
await docker_auto_start(binary_path="/path/to/binary.exe")
# Get container logs
await docker_logs("mcghidra-server")
# Stop container
await docker_stop("mcghidra-server")
Building
# Using Make
make build
# Using Docker directly
docker build -t mcghidra:latest -f docker/Dockerfile .
# Build with specific Ghidra version
docker build -t mcghidra:latest \
--build-arg GHIDRA_VERSION=11.4.2 \
--build-arg GHIDRA_DATE=20250826 \
-f docker/Dockerfile .
Troubleshooting
Container starts but API doesn't respond
Analysis takes time. Monitor progress with:
docker logs -f mcghidra-server
Port already in use
Stop existing containers:
docker stop $(docker ps -q --filter "name=mcghidra")
Memory issues with large binaries
Increase JVM heap:
docker run -e MCGHIDRA_MAXMEM=4G -p 8192:8192 mcghidra /binaries/large.exe
Permission denied on volumes
The container runs as user ghidra (UID 1001). Ensure volume permissions:
sudo chown -R 1001:1001 /path/to/binaries