WIP update APIs
This commit is contained in:
parent
6b2e572bd4
commit
57584581bc
399
JAVA_PLUGIN_API.md
Normal file
399
JAVA_PLUGIN_API.md
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
# GhydraMCP Java Plugin REST API Documentation
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
`http://localhost:8192` (default port, may vary)
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
### 1. Instance Information
|
||||||
|
- `GET /info`
|
||||||
|
- `GET /` (root path)
|
||||||
|
|
||||||
|
Returns basic instance information including:
|
||||||
|
- Port number
|
||||||
|
- Whether this is the base instance
|
||||||
|
- Current project name (if available)
|
||||||
|
- Current program name (if available)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"port": 8192,
|
||||||
|
"isBaseInstance": true,
|
||||||
|
"project": "MyProject",
|
||||||
|
"file": "program.exe"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Function Operations
|
||||||
|
|
||||||
|
#### List Functions
|
||||||
|
- `GET /functions`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
- `query` (optional): Search term to filter functions
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"name": "init_peripherals",
|
||||||
|
"address": "08000200"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "uart_rx_valid_command",
|
||||||
|
"address": "0800029c"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1743778219516,
|
||||||
|
"port": 8192,
|
||||||
|
"instanceType": "base"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Function Details
|
||||||
|
- `GET /functions/{name}`
|
||||||
|
|
||||||
|
Returns decompiled code for the specified function.
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "int main() {\n // Decompiled code here\n}",
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rename Function
|
||||||
|
- `POST /functions/{name}`
|
||||||
|
|
||||||
|
Body Parameters:
|
||||||
|
- `newName`: New name for the function
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "Renamed successfully",
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Function Variables
|
||||||
|
- `GET /functions/{name}/variables`
|
||||||
|
|
||||||
|
Lists all variables (parameters and locals) in a function.
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": {
|
||||||
|
"function": "myFunction",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "param1",
|
||||||
|
"type": "int",
|
||||||
|
"kind": "parameter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "param2",
|
||||||
|
"type": "char*",
|
||||||
|
"kind": "parameter"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"localVariables": [
|
||||||
|
{
|
||||||
|
"name": "var1",
|
||||||
|
"type": "int",
|
||||||
|
"address": "08000234"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "var2",
|
||||||
|
"type": "float",
|
||||||
|
"address": "08000238"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rename/Retype Variable
|
||||||
|
- `POST /functions/{name}/variables/{varName}`
|
||||||
|
|
||||||
|
Body Parameters (one of):
|
||||||
|
- `newName`: New name for variable
|
||||||
|
- `dataType`: New data type for variable
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "Variable renamed",
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Class Operations
|
||||||
|
- `GET /classes`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
"MyClass1",
|
||||||
|
"MyClass2"
|
||||||
|
],
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Memory Segments
|
||||||
|
- `GET /segments`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"name": ".text",
|
||||||
|
"start": "08000000",
|
||||||
|
"end": "08001000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".data",
|
||||||
|
"start": "08001000",
|
||||||
|
"end": "08002000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Symbol Operations
|
||||||
|
|
||||||
|
#### Imports
|
||||||
|
- `GET /symbols/imports`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"name": "printf",
|
||||||
|
"address": "EXTERNAL:00000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "malloc",
|
||||||
|
"address": "EXTERNAL:00000004"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exports
|
||||||
|
- `GET /symbols/exports`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"name": "main",
|
||||||
|
"address": "08000200"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_start",
|
||||||
|
"address": "08000100"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Namespace Operations
|
||||||
|
- `GET /namespaces`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
"std",
|
||||||
|
"MyNamespace"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Data Operations
|
||||||
|
|
||||||
|
#### List Defined Data
|
||||||
|
- `GET /data`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"address": "08001000",
|
||||||
|
"name": "myVar",
|
||||||
|
"value": "42"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "08001004",
|
||||||
|
"name": "myString",
|
||||||
|
"value": "\"Hello\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rename Data
|
||||||
|
- `POST /data`
|
||||||
|
|
||||||
|
Body Parameters:
|
||||||
|
- `address`: Address of data to rename (hex string)
|
||||||
|
- `newName`: New name for data
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": {
|
||||||
|
"name": "main",
|
||||||
|
"decompiled": "int main() {\n // Decompiled code here\n}",
|
||||||
|
"metadata": {
|
||||||
|
"size": 256,
|
||||||
|
"entryPoint": "08000200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Variable Operations
|
||||||
|
|
||||||
|
#### Global Variables
|
||||||
|
- `GET /variables`
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `offset` (optional): Pagination offset (default: 0)
|
||||||
|
- `limit` (optional): Maximum results (default: 100)
|
||||||
|
- `search` (optional): Search term to filter variables
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"name": "globalVar1",
|
||||||
|
"address": "08001000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "globalVar2",
|
||||||
|
"address": "08001004"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Instance Management
|
||||||
|
|
||||||
|
#### List Active Instances
|
||||||
|
- `GET /instances`
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"port": 8192,
|
||||||
|
"type": "base"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"port": 8193,
|
||||||
|
"type": "secondary"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Register Instance
|
||||||
|
- `POST /registerInstance`
|
||||||
|
|
||||||
|
Body Parameters:
|
||||||
|
- `port`: Port number to register
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "Instance registered on port 8193",
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Unregister Instance
|
||||||
|
- `POST /unregisterInstance`
|
||||||
|
|
||||||
|
Body Parameters:
|
||||||
|
- `port`: Port number to unregister
|
||||||
|
|
||||||
|
Example Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"result": "Unregistered instance on port 8193",
|
||||||
|
"timestamp": 1743778219516
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
All endpoints return JSON with success=false on errors:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Error message",
|
||||||
|
"status": 500
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Common status codes:
|
||||||
|
- 400: Bad request (invalid parameters)
|
||||||
|
- 404: Not found (invalid endpoint or resource)
|
||||||
|
- 405: Method not allowed
|
||||||
|
- 500: Internal server error
|
||||||
147
MCP_BRIDGE_API.md
Normal file
147
MCP_BRIDGE_API.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# GhydraMCP Bridge API Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This document describes the MCP tools and resources exposed by the GhydraMCP bridge that connects to Ghidra's HTTP API. The bridge provides a higher-level interface optimized for AI agent usage.
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
- Each Ghidra instance runs its own HTTP server (default port 8192)
|
||||||
|
- The bridge discovers and manages multiple Ghidra instances
|
||||||
|
- Tools are organized by resource type (programs, functions, data, etc.)
|
||||||
|
- Consistent response format with success/error indicators
|
||||||
|
|
||||||
|
## Instance Management Tools
|
||||||
|
|
||||||
|
### `list_instances`
|
||||||
|
List all active Ghidra instances with their ports and project info.
|
||||||
|
|
||||||
|
### `discover_instances`
|
||||||
|
Scan for available Ghidra instances by port range.
|
||||||
|
|
||||||
|
### `register_instance`
|
||||||
|
Manually register a Ghidra instance by port/URL.
|
||||||
|
|
||||||
|
## Program Analysis Tools
|
||||||
|
|
||||||
|
### `list_functions`
|
||||||
|
List functions in current program with pagination.
|
||||||
|
|
||||||
|
### `get_function`
|
||||||
|
Get details and decompilation for a function by name.
|
||||||
|
|
||||||
|
### `get_function_by_address`
|
||||||
|
Get function details by memory address.
|
||||||
|
|
||||||
|
### `decompile_function_by_address`
|
||||||
|
Decompile function at specific address.
|
||||||
|
|
||||||
|
### `list_segments`
|
||||||
|
List memory segments/sections in program.
|
||||||
|
|
||||||
|
### `list_data_items`
|
||||||
|
List defined data items in program.
|
||||||
|
|
||||||
|
### `read_memory`
|
||||||
|
Read bytes from memory at address. Parameters:
|
||||||
|
- `address`: Hex address
|
||||||
|
- `length`: Bytes to read
|
||||||
|
- `format`: "hex", "base64" or "string" output format
|
||||||
|
|
||||||
|
### `write_memory`
|
||||||
|
Write bytes to memory at address (use with caution). Parameters:
|
||||||
|
- `address`: Hex address
|
||||||
|
- `bytes`: Data to write
|
||||||
|
- `format`: "hex", "base64" or "string" input format
|
||||||
|
|
||||||
|
### `list_variables`
|
||||||
|
List global variables with search/filter.
|
||||||
|
|
||||||
|
## Modification Tools
|
||||||
|
|
||||||
|
### `update_function`
|
||||||
|
Rename a function.
|
||||||
|
|
||||||
|
### `update_data`
|
||||||
|
Rename data at memory address.
|
||||||
|
|
||||||
|
### `set_function_prototype`
|
||||||
|
Change a function's signature.
|
||||||
|
|
||||||
|
### `rename_local_variable`
|
||||||
|
Rename variable within function.
|
||||||
|
|
||||||
|
### `set_local_variable_type`
|
||||||
|
Change variable's data type.
|
||||||
|
|
||||||
|
## Response Format
|
||||||
|
All tools return responses in this format:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "request-id",
|
||||||
|
"instance": "http://host:port",
|
||||||
|
"success": true/false,
|
||||||
|
"result": {...}, // Tool-specific data
|
||||||
|
"error": { // Only on failure
|
||||||
|
"code": "...",
|
||||||
|
"message": "..."
|
||||||
|
},
|
||||||
|
"_links": { // HATEOAS links
|
||||||
|
"self": {"href": "/path"},
|
||||||
|
"related": {"href": "/other"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
1. Discover available instances:
|
||||||
|
```python
|
||||||
|
discover_instances()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. List functions in first instance:
|
||||||
|
```python
|
||||||
|
list_functions(port=8192, limit=10)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Decompile main function:
|
||||||
|
```python
|
||||||
|
get_function(port=8192, name="main")
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Rename a function:
|
||||||
|
```python
|
||||||
|
update_function(port=8192, name="FUN_1234", new_name="parse_data")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
- Check `success` field first
|
||||||
|
- On failure, `error` contains details
|
||||||
|
- Common error codes:
|
||||||
|
- `INSTANCE_NOT_FOUND`
|
||||||
|
- `RESOURCE_NOT_FOUND`
|
||||||
|
- `INVALID_PARAMETER`
|
||||||
|
- `TRANSACTION_FAILED`
|
||||||
|
|
||||||
|
## Advanced Analysis Tools
|
||||||
|
|
||||||
|
### `list_xrefs`
|
||||||
|
List cross-references between code/data. Parameters:
|
||||||
|
- `to_addr`: Filter refs to this address
|
||||||
|
- `from_addr`: Filter refs from this address
|
||||||
|
- `type`: Filter by ref type ("CALL", "READ", etc)
|
||||||
|
- Basic pagination via `offset`/`limit`
|
||||||
|
|
||||||
|
### `analyze_program`
|
||||||
|
Run Ghidra analysis with optional settings:
|
||||||
|
- `analysis_options`: Dict of analysis passes to enable
|
||||||
|
|
||||||
|
### `get_callgraph`
|
||||||
|
Get function call graph visualization data:
|
||||||
|
- `function`: Starting function (defaults to entry point)
|
||||||
|
- `max_depth`: Maximum call depth (default: 3)
|
||||||
|
|
||||||
|
### `get_dataflow`
|
||||||
|
Perform data flow analysis from address:
|
||||||
|
- `address`: Starting point in hex
|
||||||
|
- `direction`: "forward" or "backward"
|
||||||
|
- `max_steps`: Max analysis steps
|
||||||
@ -317,18 +317,58 @@ def _discover_instances(port_range, host=None, timeout=0.5) -> dict:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_functions(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_functions(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
"""List functions in the current program with pagination
|
offset: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
addr: str = None,
|
||||||
|
name: str = None,
|
||||||
|
name_contains: str = None,
|
||||||
|
name_matches_regex: str = None) -> dict:
|
||||||
|
"""List functions in the current program with filtering and pagination
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
port: Ghidra instance port (default: 8192)
|
port: Ghidra instance port (default: 8192)
|
||||||
offset: Pagination offset (default: 0)
|
offset: Pagination offset (default: 0)
|
||||||
limit: Maximum items to return (default: 100)
|
limit: Maximum items to return (default: 100)
|
||||||
|
addr: Filter by address (hexadecimal)
|
||||||
|
name: Exact name match filter (case-sensitive)
|
||||||
|
name_contains: Substring name filter (case-insensitive)
|
||||||
|
name_matches_regex: Regex name filter
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Function names and addresses
|
dict: {
|
||||||
|
"result": list of function info objects,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
return safe_get(port, "functions", {"offset": offset, "limit": limit})
|
params = {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
if addr:
|
||||||
|
params["addr"] = addr
|
||||||
|
if name:
|
||||||
|
params["name"] = name
|
||||||
|
if name_contains:
|
||||||
|
params["name_contains"] = name_contains
|
||||||
|
if name_matches_regex:
|
||||||
|
params["name_matches_regex"] = name_matches_regex
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/functions", params)
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Transform to expected format if needed
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_classes(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_classes(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||||
@ -393,21 +433,101 @@ def update_data(port: int = DEFAULT_GHIDRA_PORT, address: str = "", new_name: st
|
|||||||
return safe_post(port, "data", {"address": address, "newName": new_name})
|
return safe_post(port, "data", {"address": address, "newName": new_name})
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_segments(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_segments(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
"""List memory segments with pagination
|
offset: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
name: str = None) -> dict:
|
||||||
|
"""List memory segments with filtering and pagination
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
port: Ghidra instance port (default: 8192)
|
port: Ghidra instance port (default: 8192)
|
||||||
offset: Pagination offset (default: 0)
|
offset: Pagination offset (default: 0)
|
||||||
limit: Maximum items to return (default: 100)
|
limit: Maximum items to return (default: 100)
|
||||||
|
name: Filter by segment name (case-sensitive substring match)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Segment information strings
|
dict: {
|
||||||
|
"result": list of segment objects,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
return safe_get(port, "segments", {"offset": offset, "limit": limit})
|
params = {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
if name:
|
||||||
|
params["name"] = name
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/segments", params)
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_imports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_symbols(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
offset: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
addr: str = None,
|
||||||
|
name: str = None,
|
||||||
|
name_contains: str = None,
|
||||||
|
type: str = None) -> dict:
|
||||||
|
"""List symbols with filtering and pagination
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
offset: Pagination offset (default: 0)
|
||||||
|
limit: Maximum items to return (default: 100)
|
||||||
|
addr: Filter by address (hexadecimal)
|
||||||
|
name: Exact name match filter (case-sensitive)
|
||||||
|
name_contains: Substring name filter (case-insensitive)
|
||||||
|
type: Filter by symbol type (e.g. "function", "data", "label")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {
|
||||||
|
"result": list of symbol objects,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
params = {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
if addr:
|
||||||
|
params["addr"] = addr
|
||||||
|
if name:
|
||||||
|
params["name"] = name
|
||||||
|
if name_contains:
|
||||||
|
params["name_contains"] = name_contains
|
||||||
|
if type:
|
||||||
|
params["type"] = type
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/symbols", params)
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def list_imports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> dict:
|
||||||
"""List imported symbols with pagination
|
"""List imported symbols with pagination
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -416,12 +536,28 @@ def list_imports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int =
|
|||||||
limit: Maximum items to return (default: 100)
|
limit: Maximum items to return (default: 100)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Imported symbol information
|
dict: {
|
||||||
|
"result": list of imported symbols,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
return safe_get(port, "symbols/imports", {"offset": offset, "limit": limit})
|
response = safe_get(port, "programs/current/symbols/imports", {"offset": offset, "limit": limit})
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_exports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_exports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> dict:
|
||||||
"""List exported symbols with pagination
|
"""List exported symbols with pagination
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -430,9 +566,25 @@ def list_exports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int =
|
|||||||
limit: Maximum items to return (default: 100)
|
limit: Maximum items to return (default: 100)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Exported symbol information
|
dict: {
|
||||||
|
"result": list of exported symbols,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
return safe_get(port, "symbols/exports", {"offset": offset, "limit": limit})
|
response = safe_get(port, "programs/current/symbols/exports", {"offset": offset, "limit": limit})
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_namespaces(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_namespaces(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||||
@ -449,18 +601,57 @@ def list_namespaces(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int
|
|||||||
return safe_get(port, "namespaces", {"offset": offset, "limit": limit})
|
return safe_get(port, "namespaces", {"offset": offset, "limit": limit})
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def list_data_items(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
def list_data_items(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
"""List data items with pagination
|
offset: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
addr: str = None,
|
||||||
|
name: str = None,
|
||||||
|
name_contains: str = None,
|
||||||
|
type: str = None) -> dict:
|
||||||
|
"""List defined data items with filtering and pagination
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
port: Ghidra instance port (default: 8192)
|
port: Ghidra instance port (default: 8192)
|
||||||
offset: Pagination offset (default: 0)
|
offset: Pagination offset (default: 0)
|
||||||
limit: Maximum items to return (default: 100)
|
limit: Maximum items to return (default: 100)
|
||||||
|
addr: Filter by address (hexadecimal)
|
||||||
|
name: Exact name match filter (case-sensitive)
|
||||||
|
name_contains: Substring name filter (case-insensitive)
|
||||||
|
type: Filter by data type (e.g. "string", "dword")
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Data item information strings
|
dict: {
|
||||||
|
"result": list of data item objects,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
return safe_get(port, "data", {"offset": offset, "limit": limit})
|
params = {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
if addr:
|
||||||
|
params["addr"] = addr
|
||||||
|
if name:
|
||||||
|
params["name"] = name
|
||||||
|
if name_contains:
|
||||||
|
params["name_contains"] = name_contains
|
||||||
|
if type:
|
||||||
|
params["type"] = type
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/data", params)
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def search_functions_by_name(port: int = DEFAULT_GHIDRA_PORT, query: str = "", offset: int = 0, limit: int = 100) -> list:
|
def search_functions_by_name(port: int = DEFAULT_GHIDRA_PORT, query: str = "", offset: int = 0, limit: int = 100) -> list:
|
||||||
@ -479,6 +670,81 @@ def search_functions_by_name(port: int = DEFAULT_GHIDRA_PORT, query: str = "", o
|
|||||||
return ["Error: query string is required"]
|
return ["Error: query string is required"]
|
||||||
return safe_get(port, "functions", {"query": query, "offset": offset, "limit": limit})
|
return safe_get(port, "functions", {"query": query, "offset": offset, "limit": limit})
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def read_memory(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
address: str = "",
|
||||||
|
length: int = 16,
|
||||||
|
format: str = "hex") -> dict:
|
||||||
|
"""Read bytes from memory
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
address: Memory address in hex format
|
||||||
|
length: Number of bytes to read (default: 16)
|
||||||
|
format: Output format - "hex", "base64", or "string" (default: "hex")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {
|
||||||
|
"address": original address,
|
||||||
|
"length": bytes read,
|
||||||
|
"format": output format,
|
||||||
|
"bytes": the memory contents,
|
||||||
|
"timestamp": response timestamp
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
if not address:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": "Address parameter is required",
|
||||||
|
"timestamp": int(time.time() * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/memory", {
|
||||||
|
"address": address,
|
||||||
|
"length": length,
|
||||||
|
"format": format
|
||||||
|
})
|
||||||
|
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"address": address,
|
||||||
|
"length": length,
|
||||||
|
"format": format,
|
||||||
|
"bytes": response.get("result", ""),
|
||||||
|
"timestamp": response.get("timestamp", int(time.time() * 1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def write_memory(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
address: str = "",
|
||||||
|
bytes: str = "",
|
||||||
|
format: str = "hex") -> dict:
|
||||||
|
"""Write bytes to memory (use with caution)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
address: Memory address in hex format
|
||||||
|
bytes: Data to write (format depends on 'format' parameter)
|
||||||
|
format: Input format - "hex", "base64", or "string" (default: "hex")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Operation result with success status
|
||||||
|
"""
|
||||||
|
if not address or not bytes:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": "Address and bytes parameters are required",
|
||||||
|
"timestamp": int(time.time() * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return safe_post(port, "programs/current/memory", {
|
||||||
|
"address": address,
|
||||||
|
"bytes": bytes,
|
||||||
|
"format": format
|
||||||
|
})
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def get_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> dict:
|
def get_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> dict:
|
||||||
"""Get function details by memory address
|
"""Get function details by memory address
|
||||||
@ -516,6 +782,113 @@ def get_current_address(port: int = DEFAULT_GHIDRA_PORT) -> dict:
|
|||||||
"port": port
|
"port": port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def list_xrefs(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
to_addr: str = None,
|
||||||
|
from_addr: str = None,
|
||||||
|
type: str = None,
|
||||||
|
offset: int = 0,
|
||||||
|
limit: int = 100) -> dict:
|
||||||
|
"""List cross-references with filtering and pagination
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
to_addr: Filter references to this address (hexadecimal)
|
||||||
|
from_addr: Filter references from this address (hexadecimal)
|
||||||
|
type: Filter by reference type (e.g. "CALL", "READ", "WRITE")
|
||||||
|
offset: Pagination offset (default: 0)
|
||||||
|
limit: Maximum items to return (default: 100)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {
|
||||||
|
"result": list of xref objects,
|
||||||
|
"size": total count,
|
||||||
|
"offset": current offset,
|
||||||
|
"limit": current limit,
|
||||||
|
"_links": pagination links
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
params = {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
if to_addr:
|
||||||
|
params["to_addr"] = to_addr
|
||||||
|
if from_addr:
|
||||||
|
params["from_addr"] = from_addr
|
||||||
|
if type:
|
||||||
|
params["type"] = type
|
||||||
|
|
||||||
|
response = safe_get(port, "programs/current/xrefs", params)
|
||||||
|
if isinstance(response, dict) and "error" in response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": response.get("result", []),
|
||||||
|
"size": response.get("size", len(response.get("result", []))),
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"_links": response.get("_links", {})
|
||||||
|
}
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def analyze_program(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
analysis_options: dict = None) -> dict:
|
||||||
|
"""Run analysis on the current program
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
analysis_options: Dictionary of analysis options to enable/disable
|
||||||
|
(e.g. {"functionRecovery": True, "dataRefs": False})
|
||||||
|
None means use default analysis options
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Analysis operation result with status
|
||||||
|
"""
|
||||||
|
return safe_post(port, "programs/current/analysis", analysis_options or {})
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_callgraph(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
function: str = None,
|
||||||
|
max_depth: int = 3) -> dict:
|
||||||
|
"""Get function call graph visualization data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
function: Starting function name (None starts from entry point)
|
||||||
|
max_depth: Maximum call depth to analyze (default: 3)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Graph data in DOT format with nodes and edges
|
||||||
|
"""
|
||||||
|
params = {"max_depth": max_depth}
|
||||||
|
if function:
|
||||||
|
params["function"] = function
|
||||||
|
|
||||||
|
return safe_get(port, "programs/current/analysis/callgraph", params)
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_dataflow(port: int = DEFAULT_GHIDRA_PORT,
|
||||||
|
address: str = "",
|
||||||
|
direction: str = "forward",
|
||||||
|
max_steps: int = 50) -> dict:
|
||||||
|
"""Perform data flow analysis from an address
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port: Ghidra instance port (default: 8192)
|
||||||
|
address: Starting address in hex format
|
||||||
|
direction: "forward" or "backward" (default: "forward")
|
||||||
|
max_steps: Maximum analysis steps (default: 50)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Data flow analysis results
|
||||||
|
"""
|
||||||
|
return safe_get(port, "programs/current/analysis/dataflow", {
|
||||||
|
"address": address,
|
||||||
|
"direction": direction,
|
||||||
|
"max_steps": max_steps
|
||||||
|
})
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def get_current_function(port: int = DEFAULT_GHIDRA_PORT) -> dict:
|
def get_current_function(port: int = DEFAULT_GHIDRA_PORT) -> dict:
|
||||||
"""Get the function currently selected in Ghidra's UI
|
"""Get the function currently selected in Ghidra's UI
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user