127 lines
6.3 KiB
Markdown
127 lines
6.3 KiB
Markdown
# ida-mcp-server
|
|
|
|
An MCP server that exposes a running IDA Pro session to Claude (or any other MCP
|
|
client) over HTTP. Ships with an IDA plugin that turns a database into an
|
|
HTTP API, and a small server that turns that API into MCP tools.
|
|
|
|
## Why this exists
|
|
|
|
The usual way to hook IDA up to an AI assistant is a fixed pair of ports:
|
|
"IDA A" on 7777, "IDA B" on 7778, hardcoded into a script. That falls apart the
|
|
moment you want a third binary open, or you're running IDA on a different
|
|
machine over SSH and have to juggle port forwards by hand.
|
|
|
|
This version drops the hardcoding. Each IDA instance finds a free port on its
|
|
own, starts its own HTTP server, and registers itself with the MCP server over
|
|
a heartbeat. Open as many IDA windows as you want, on the same machine or a
|
|
remote one -- they show up automatically, and the MCP tools let you address a
|
|
specific one by id or just let it pick the only one that's open.
|
|
|
|
## How it's put together
|
|
|
|
```
|
|
ida_mcp_plugin.py runs inside IDA, one instance per open database
|
|
-> exposes decompilation, xrefs, renaming, etc. over HTTP
|
|
ida-mcp-server.py a small Starlette/uvicorn server
|
|
-> turns that HTTP API into MCP tools over JSON-RPC (/sse)
|
|
-> keeps a live registry of which IDA instances are open
|
|
```
|
|
|
|
The plugin and the server talk to each other in both directions: the plugin
|
|
pushes heartbeats to the server so it knows what's alive, and the server pulls
|
|
data from the plugin when a tool is called.
|
|
|
|
## Setup
|
|
|
|
1. Copy `ida_mcp_plugin.py` into IDA's plugins directory (on macOS, something
|
|
like `IDA Professional 9.1.app/Contents/MacOS/plugins/`).
|
|
2. Install the server's dependencies and start it:
|
|
|
|
```bash
|
|
pip install httpx starlette uvicorn
|
|
python ida-mcp-server.py
|
|
```
|
|
|
|
3. Open IDA. The plugin starts automatically, picks a port, and registers
|
|
itself with the server at `http://127.0.0.1:8888` by default.
|
|
4. Point your MCP client at `http://<server-host>:8888/sse`.
|
|
|
|
That's it for a single machine. No ports to configure, no restart dance when
|
|
you open a second binary.
|
|
|
|
### Multiple IDA instances
|
|
|
|
Just open more IDA windows. Each one registers under its own id (derived from
|
|
the input file name and process id) and shows up in `ida_list_instances`. Most
|
|
tools take an optional `instance` argument -- skip it if only one IDA is open,
|
|
or pass an id from `ida_list_instances` to target a specific one.
|
|
|
|
### Running IDA on a different machine
|
|
|
|
Set these before launching IDA on the remote box:
|
|
|
|
- `IDA_MCP_HOST=0.0.0.0` -- so the plugin accepts connections from outside localhost
|
|
- `IDA_MCP_ADVERTISE_HOST=<ip reachable from the server>` -- what the plugin tells the server to call it back on
|
|
- `IDA_MCP_REGISTRY_URL=http://<server-ip>:8888` -- where to send heartbeats
|
|
|
|
The server still has to be able to reach that IP and port -- this handles the
|
|
port bookkeeping, not your network topology. If there's a firewall in the way
|
|
you'll still need a tunnel, but you won't be assigning ports by hand anymore.
|
|
|
|
### Reloading the plugin without restarting IDA
|
|
|
|
If you edit `ida_mcp_plugin.py`, call the `ida_reload_plugin` tool instead of
|
|
closing and reopening IDA. It re-reads the file from disk and swaps in the new
|
|
code for anything that handles a request. The one thing that doesn't
|
|
hot-reload is the heartbeat/registration thread, which keeps running with
|
|
whatever code it started with until IDA actually restarts.
|
|
|
|
## Security
|
|
|
|
By default the server and plugin trust anything that can reach them, which is
|
|
fine if everything stays on localhost. If you're exposing the server publicly
|
|
(for example through ngrok, to use it from a hosted MCP client), set
|
|
`IDA_MCP_TOKEN` to the same value on both the plugin's environment and the
|
|
server's environment. That gates:
|
|
|
|
- MCP tool calls against the server
|
|
- instance registration from the plugin to the server
|
|
- the OAuth handshake used by clients that require one (the token has to be
|
|
typed in during authorization; it isn't handed out automatically)
|
|
|
|
Without a token, anyone who has the URL has full read/write access to whatever
|
|
IDA session is open -- decompiling, renaming, patching, all of it. Don't skip
|
|
the token if the server is reachable from outside your own machine.
|
|
|
|
## What the tools can do
|
|
|
|
Around 40 tools, roughly grouped as:
|
|
|
|
- **Discovery**: list/search functions and strings, imports, segments, entry points, `ida_list_instances`
|
|
- **Reading code**: decompile (single or batched), disassembly with honest pagination (it tells you if the output was truncated), xrefs, call graphs, a triage tool that ranks functions by how referenced or how large they are
|
|
- **Types**: read/set a function's prototype, set a local variable's type, define structs/enums/unions in Local Types
|
|
- **Writing to the database**: rename functions and locals, set comments (function-level or at a specific address), a persistent per-address note store that survives across sessions
|
|
- **Binary-level**: raw byte reads, hex pattern search, immediate-value search, a heuristic vtable finder, byte patching, defining data items
|
|
- **Objective-C**: class and method listing for Mach-O binaries, read from `_OBJC_CLASS_$_` symbols and `+[Class sel]`/`-[Class sel]` naming
|
|
- **Comparing binaries**: decompile the same or different targets across two IDA instances side by side -- useful for diffing a patched build against the original, or a symboled build against a stripped one
|
|
|
|
A few of these are marked experimental in their descriptions -- mostly around
|
|
byte-pattern search and struct/type APIs that have moved between IDA versions
|
|
in the past (IDA 9.x renamed or relocated a handful of functions this project
|
|
already ran into and worked around). If one of them fails, the error message
|
|
says what actually broke rather than a generic failure.
|
|
|
|
## Known limitations
|
|
|
|
- The Objective-C method scan relies on standard naming conventions
|
|
(`+[Class sel]`); it won't find much in binaries where those names have been
|
|
stripped or where the code is mostly Swift.
|
|
- The vtable finder is a heuristic (runs of pointers into real functions in
|
|
non-executable segments) and can both miss real vtables and flag things that
|
|
aren't. Narrow it to a specific segment on large binaries or it can be slow.
|
|
- Byte patching and struct/data creation write directly to the IDA database.
|
|
There's no undo built in beyond what IDA itself offers.
|
|
|
|
## License
|
|
|
|
MIT, see [LICENSE](LICENSE).
|