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()
|
||||
def list_functions(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List functions in the current program with pagination
|
||||
def list_functions(port: int = DEFAULT_GHIDRA_PORT,
|
||||
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:
|
||||
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)
|
||||
name_matches_regex: Regex name filter
|
||||
|
||||
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()
|
||||
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})
|
||||
|
||||
@mcp.tool()
|
||||
def list_segments(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List memory segments with pagination
|
||||
def list_segments(port: int = DEFAULT_GHIDRA_PORT,
|
||||
offset: int = 0,
|
||||
limit: int = 100,
|
||||
name: str = None) -> dict:
|
||||
"""List memory segments with filtering and pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum items to return (default: 100)
|
||||
name: Filter by segment name (case-sensitive substring match)
|
||||
|
||||
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()
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
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})
|
||||
|
||||
@mcp.tool()
|
||||
def list_data_items(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List data items with pagination
|
||||
def list_data_items(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 defined data items 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 data type (e.g. "string", "dword")
|
||||
|
||||
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()
|
||||
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 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()
|
||||
def get_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> dict:
|
||||
"""Get function details by memory address
|
||||
@ -516,6 +782,113 @@ def get_current_address(port: int = DEFAULT_GHIDRA_PORT) -> dict:
|
||||
"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()
|
||||
def get_current_function(port: int = DEFAULT_GHIDRA_PORT) -> dict:
|
||||
"""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