An MCP (Model Context Protocol) server for IDA Pro focused on malware analysis. This server enables Claude to analyze and rename functions/variables in IDA Pro through a structured interface.
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
│ Windows Host │ │ Windows VM │
│ │ │ │
│ Claude Desktop ◄──► server.py ◄───┼────┼──► ida_plugin.py ◄──► IDA Pro │
│ MCP/stdio HTTP │ │ :13337 │
└────────────────────────────────────┘ └────────────────────────────────────┘
Claude <--MCP/stdio--> server.py <--HTTP/JSON-RPC--> ida_plugin.py <--IDAPython--> IDA Pro
| File | Description |
|---|---|
server.py |
MCP server with stdio transport. Parses ida_plugin.py AST to extract @jsonrpc functions and exposes them as MCP tools. Forwards calls via HTTP to IDA. |
ida_plugin.py |
IDA Pro plugin. Runs HTTP server on 0.0.0.0:13337, handles JSON-RPC requests, executes IDAPython commands. |
rpc.py |
@jsonrpc decorator registry. Functions decorated with @jsonrpc are automatically exposed via JSON-RPC. |
sync.py |
@ida_sync decorator. Ensures IDAPython calls run on IDA's main thread using execute_sync. |
install.py |
Helper script to copy plugin files to IDA's plugins directory. |
- Python 3.11+
- IDA Pro 8.3+ (9.x recommended) with Hex-Rays decompiler
- Windows (for IDA Pro)
mcp>=1.0.0
httpx>=0.25.0
# Clone or download the repository
cd ida-malware-mcp
# Install in development mode
pip install -e .Option A: Automatic Installation
python -m ida_malware_mcp.install "C:\Program Files\IDA Pro 9.0\plugins"Option B: Manual Installation
Copy these files to your IDA plugins directory:
C:\Program Files\IDA Pro 9.0\plugins\
└── ida_malware_mcp\
├── __init__.py
├── ida_plugin.py
├── rpc.py
└── sync.py
Then create a loader file ida_malware_mcp_loader.py in the plugins directory:
"""IDA Malware MCP Plugin Loader"""
import sys
from pathlib import Path
plugin_dir = Path(__file__).parent / "ida_malware_mcp"
if str(plugin_dir) not in sys.path:
sys.path.insert(0, str(plugin_dir))
from ida_malware_mcp.ida_plugin import PLUGIN_ENTRYAdd to claude_desktop_config.json:
{
"mcpServers": {
"ida-malware-mcp": {
"command": "python",
"args": ["-m", "ida_malware_mcp.server"],
"env": {
"IDA_MCP_URL": "http://192.168.x.x:13337/mcp"
}
}
}
}Replace 192.168.x.x with your VM's IP address.
Allow inbound connections on port 13337:
netsh advfirewall firewall add rule name="IDA MCP" dir=in action=allow protocol=tcp localport=13337- Start IDA Pro and load a binary
- Check IDA Output window for:
[IDA MCP] Server started on http://0.0.0.0:13337/mcp - Test from host:
curl http://<VM_IP>:13337/mcp
| Tool | Description |
|---|---|
get_analysis_guidelines() |
Call FIRST! Get mandatory workflow for batch analysis |
complete_batch(batch_num, functions, findings, progress) |
Call after each batch! Logs results |
check_connection() |
Verify IDA plugin is running and responsive |
get_metadata() |
Get IDB info: filename, architecture, bitness, address range |
list_functions(offset, count, filter?) |
List functions with pagination. Use filter="sub_" for unnamed functions |
print_summary(summary) |
Print execution summary to IDA's Output window |
| Tool | Description |
|---|---|
get_entry_points() |
Get all entry points: main, TLS callbacks, exports |
get_segments() |
List segments with permissions, entropy, and suspicious indicators |
get_imports() |
Get imported functions grouped by DLL/module |
get_exports() |
Get exported functions with ordinals |
get_strings(min_length?, max_count?, filter?) |
Get strings with xref counts. Filter by substring |
| Tool | Description |
|---|---|
get_function_pseudocode(address) |
Get Hex-Rays decompiled code (falls back to disassembly if unavailable) |
get_function_disassembly(address) |
Get assembly listing for a function |
get_local_variables(func_address) |
List local variables with names and types |
get_function_calls(address) |
Get all functions/APIs called by a function |
get_function_callers(address) |
Get all functions that call this function |
get_basic_blocks(address) |
Get CFG basic blocks with predecessors/successors |
refresh_decompilation(address) |
Force Hex-Rays refresh after type changes |
analyze_function_complete(address) |
Kitchen sink: decompile + disasm + variables + callees + callers + strings in one call |
get_call_graph(address, depth?, direction?) |
Recursive call graph (callees, callers, or both) up to depth 10 |
| Tool | Description |
|---|---|
get_xrefs_to(address, max_count?) |
Get all references TO an address (callers, data refs) |
get_xrefs_from(address, max_count?) |
Get all references FROM an address/function |
| Tool | Description |
|---|---|
search_bytes(pattern, max_results?) |
Search byte patterns with wildcards. E.g., "FC E8", "E8 ?? ?? ?? ??" |
search_immediate(value, max_results?) |
Find immediate value usage (API hashes, constants) |
find_potential_hashes(func_address?, max_results?) |
Find potential API hash constants (32-bit immediates) with disassembly |
search_functions_regex(pattern, max_results?) |
Search function names with regex (e.g., "crypt|encrypt", "sub_14.*") |
search_strings_regex(pattern, max_results?) |
Search strings with regex (e.g., "https?://", "password|secret") |
| Tool | Description |
|---|---|
get_bytes(address, size, format?) |
Extract raw bytes as hex/base64/array with auto-analysis hints |
apply_struct(address, struct_name, is_pointer?) |
Apply structure type at address |
| Tool | Description |
|---|---|
rename_function(address, new_name) |
Rename a function |
set_name(address, new_name) |
Rename any address (functions, globals, data) |
rename_local_variable(func_addr, old_name, new_name) |
Rename a local variable |
batch_rename_locals(func_addr, renames[]) |
Batch rename multiple local variables |
set_local_variable_type(func_addr, var_name, type_name) |
Set variable type (e.g., IMAGE_DOS_HEADER *) |
set_function_prototype(address, prototype) |
Set full function signature |
apply_callee_type(address, prototype) |
Apply prototype to indirect CALL (runtime-resolved APIs) — auto-strips SAL/MSDN formatting |
get_callee_type(address) |
Read back callee type applied to a CALL instruction |
set_comment(address, comment, is_repeatable?) |
Add disassembly comment at address |
set_decompiler_comment(address, comment) |
Add comment in Hex-Rays pseudocode view |
set_plate_comment(address, comment) |
Add banner comment above a function |
create_function(address, end_address?) |
Create a function (useful for shellcode, missed code) |
delete_function(address) |
Delete (undefine) a function boundary |
| Tool | Description |
|---|---|
batch_rename_functions(renames[]) |
Rename multiple functions in one call |
batch_set_comments(comments[]) |
Set comments at multiple addresses (regular, repeatable, anterior, posterior) |
batch_set_variable_types(func_addr, type_changes[]) |
Set types of multiple local variables in one call |
| Tool | Description |
|---|---|
create_struct(name, fields?) |
Create a new structure with optional initial fields |
add_struct_field(struct_name, field_name, field_type, offset?, size?) |
Add a field to an existing structure |
get_struct_layout(struct_name) |
Get full structure layout with all fields, offsets, sizes |
create_enum(name, members?, is_bitfield?) |
Create a new enum type |
get_enum_members(enum_name) |
Get all members and values of an enum |
| Tool | Description |
|---|---|
list_local_types() |
List all types in Local Types window |
declare_c_type(c_declaration) |
Add/update type from C declaration |
ensure_pe_types() |
Create PE_BASE, PE_BASE64 unions and PE struct types |
| Tool | Description |
|---|---|
find_anti_analysis() |
Detect anti-debugging, anti-VM, and anti-sandbox techniques |
extract_iocs(max_results?) |
Extract IOCs: URLs, IPs, emails, file paths, registry keys, crypto wallets |
analyze_api_chains(address?, max_depth?) |
Analyze API call chains and detect behavioral patterns (download+exec, injection, etc.) |
| Tool | Description |
|---|---|
set_bookmark(address, description, slot?) |
Set a bookmark at an address |
list_bookmarks() |
List all bookmarks with descriptions and context |
delete_bookmark(slot) |
Delete a bookmark by slot number |
| Tool | Description |
|---|---|
run_script(code) |
Execute arbitrary IDAPython code. Set result variable to return data. |
The IDA plugin accepts JSON-RPC 2.0 requests at http://<IP>:13337/mcp:
Request:
{
"jsonrpc": "2.0",
"method": "get_function_pseudocode",
"params": ["0x140001000"],
"id": 1
}Response:
{
"jsonrpc": "2.0",
"result": {
"address": "0x140001000",
"name": "sub_140001000",
"pseudocode": "int __fastcall sub_140001000(...) { ... }"
},
"id": 1
}Add a function with @jsonrpc and @ida_sync decorators in ida_plugin.py:
@jsonrpc
@ida_sync
def my_new_tool(param1: str, param2: int) -> dict:
"""Description shown to Claude as tool description."""
# Implementation using IDAPython
ea = _parse_address(param1)
# ... do something ...
return {"result": "success", "data": "..."}The function is automatically:
- Exposed via JSON-RPC in the IDA plugin
- Discovered by
server.pyvia AST parsing - Available as an MCP tool to Claude
- Use type hints for proper JSON Schema generation
str→"type": "string"int→"type": "integer"bool→"type": "boolean"list→"type": "array"dict→"type": "object"- Optional parameters: use
param: str | None = None
Accept addresses as strings (hex 0x... or decimal), convert using _parse_address():
def my_tool(address: str) -> dict:
ea = _parse_address(address) # Handles "0x401000" or "4198400"Quick assessment of a suspicious binary:
1. get_metadata() → Get file info, architecture
2. get_segments() → Check for packed sections (high entropy, RWX)
3. get_imports() → Review API capabilities
4. get_strings(filter="http") → Find C2 URLs
5. get_entry_points() → Identify execution starting points
Claude analyzes unnamed functions and identifies standard library functions:
1. list_functions(filter="sub_") → Get unnamed functions
2. get_function_pseudocode(addr) → Get decompiled code (or assembly fallback)
3. Claude analyzes the code → Identifies strlen, memcpy, etc.
4. rename_function(addr, "mw_strlen")→ Apply rename with mw_ prefix
5. print_summary(summary) → Display results in IDA
Naming Convention: mw_ + standard function name
mw_strlen,mw_strcpy,mw_memcpy,mw_memsetmw_xor_decrypt,mw_rc4_encrypt,mw_resolve_api_ror13
Identify and resolve API hash functions:
1. search_immediate("0x6A4ABC41") → Find hash constant usage
2. get_function_callers(addr) → Find the hash resolver function
3. get_function_pseudocode(resolver) → Analyze resolver logic
4. ensure_pe_types() → Create PE_BASE unions
5. set_local_variable_type(...) → Apply proper types
6. rename_function(addr, "mw_resolve_api_ror13")
Analyze embedded shellcode or shellcode samples:
1. search_bytes("FC E8") → Find shellcode patterns (cld; call)
2. get_bytes(addr, 256) → Extract shellcode bytes
3. get_function_pseudocode(addr) → Get disassembly (auto-fallback)
4. get_basic_blocks(addr) → Understand control flow
5. get_function_calls(addr) → Find resolved API calls
Find and analyze string decryption routines:
1. get_strings() → Find encrypted/garbled strings
2. get_xrefs_to(string_addr) → Find code referencing string
3. get_function_pseudocode(func) → Analyze decryption logic
4. get_function_calls(func) → Check for crypto APIs
5. rename_function(addr, "mw_decrypt_string")
Understand function relationships:
1. get_call_graph(main_func, depth=3) → Full recursive call tree
2. analyze_function_complete(addr) → Everything about one function in one call
3. get_function_callers(crypto_func) → Who uses crypto?
4. get_xrefs_from(suspicious_func) → All references (calls + data)
Identify evasion techniques in malware:
1. find_anti_analysis() → Detect anti-debug/VM/sandbox APIs
2. extract_iocs() → Pull out URLs, IPs, paths, registry keys
3. analyze_api_chains(addr) → Find download+exec, injection patterns
4. search_functions_regex("crypt|encrypt|decrypt") → Find crypto functions
Define and apply custom structures:
1. create_struct("C2_CONFIG", [{"name": "magic", "type": "DWORD"}, ...])
2. add_struct_field("C2_CONFIG", "url", "BYTE", size=256)
3. apply_struct(addr, "C2_CONFIG")
4. refresh_decompilation(func_addr) → See clean pseudocode
5. get_struct_layout("C2_CONFIG") → Verify layout
Efficiently document many functions:
1. list_functions(filter="sub_") → Get unnamed functions
2. For each batch of 5:
a. analyze_function_complete(addr) → Get everything about the function
b. batch_rename_functions([...]) → Rename multiple functions at once
c. batch_set_comments([...]) → Add comments at multiple addresses
d. batch_set_variable_types(addr, [...]) → Fix types in bulk
Run arbitrary IDAPython when built-in tools aren't enough:
1. run_script("result = [hex(ea) for ea in idautils.Functions() if idc.get_func_attr(ea, idc.FUNCATTR_FLAGS) & 0x4]")
→ Find all library functions
2. run_script("import ida_bytes; result = ida_bytes.get_bytes(0x401000, 16).hex()")
→ Read raw bytes with full IDA API access
- Verify IDA Pro is running with a binary loaded
- Check IDA Output window for server startup message
- Test connectivity:
curl http://<VM_IP>:13337/mcp - Check Windows Firewall allows port 13337
get_function_pseudocode()automatically falls back to disassembly when Hex-Rays fails- Check the
code_typefield in response:"pseudocode"or"disassembly" - For shellcode, assembly output is expected and fully supported
- Check IDA's Output window for error messages
- Verify all plugin files are in the correct directory
- Ensure Python version matches IDA's embedded Python
- Large binaries may take time for string/function enumeration
- Use pagination:
list_functions(offset=0, count=50) - Use filters:
get_strings(filter="http", max_count=100)
| Variable | Default | Description |
|---|---|---|
IDA_MCP_URL |
http://localhost:13337/mcp |
URL of the IDA plugin endpoint |
This project builds upon and was inspired by:
-
mrexodia/ida-pro-mcp - The original IDA Pro MCP server that provided the foundational architecture for Claude-IDA integration via JSON-RPC.
-
bethington/ghidra-mcp - The Ghidra MCP server whose comprehensive toolset (179 tools) inspired many additions including struct/enum management, batch operations, malware analysis tools, bookmarks, and the kitchen-sink
analyze_function_completepattern. -
herrcore's PE Header Union Trick - The PE_BASE/PE_BASE64 union technique for cleaner decompilation of PE parsing code and API hash resolvers.
-
Dump-GUY/ApplyCalleeTypeEx - The callee type application logic and prototype preprocessing pipeline (SAL annotation stripping, calling convention normalization, MSDN format handling) were ported from this modernized fork of Mandiant FLARE's ApplyCalleeType plugin (Apache-2.0).
MIT License