Some checks are pending
Build Ghidra Plugin / build (push) Waiting to run
- Add ghydramcp Python package with FastMCP server implementation - Add docker-compose.yml for easy container management - Add Makefile with build/run targets - Add QUICKSTART.md for getting started - Add uv.lock for reproducible dependencies
130 lines
3.6 KiB
Python
130 lines
3.6 KiB
Python
"""Memory mixin for GhydraMCP.
|
|
|
|
Provides tools for memory read/write operations.
|
|
"""
|
|
|
|
from typing import Any, Dict, Optional
|
|
|
|
from fastmcp.contrib.mcp_mixin import mcp_tool
|
|
|
|
from .base import GhydraMixinBase
|
|
|
|
|
|
class MemoryMixin(GhydraMixinBase):
|
|
"""Mixin for memory operations.
|
|
|
|
Provides tools for:
|
|
- Reading memory bytes
|
|
- Writing memory bytes (use with caution)
|
|
"""
|
|
|
|
@mcp_tool()
|
|
def memory_read(
|
|
self,
|
|
address: str,
|
|
length: int = 16,
|
|
format: str = "hex",
|
|
port: Optional[int] = None,
|
|
) -> Dict[str, Any]:
|
|
"""Read bytes from memory.
|
|
|
|
Args:
|
|
address: Memory address in hex format
|
|
length: Number of bytes to read (default: 16)
|
|
format: Output format - "hex", "base64", or "string" (default: "hex")
|
|
port: Ghidra instance port (optional)
|
|
|
|
Returns:
|
|
Memory contents in the requested format
|
|
"""
|
|
if not address:
|
|
return {
|
|
"success": False,
|
|
"error": {
|
|
"code": "MISSING_PARAMETER",
|
|
"message": "Address parameter is required",
|
|
},
|
|
}
|
|
|
|
try:
|
|
port = self.get_instance_port(port)
|
|
except ValueError as e:
|
|
return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}
|
|
|
|
params = {
|
|
"address": address,
|
|
"length": length,
|
|
"format": format,
|
|
}
|
|
|
|
response = self.safe_get(port, "memory", params)
|
|
simplified = self.simplify_response(response)
|
|
|
|
if "result" in simplified and isinstance(simplified["result"], dict):
|
|
result = simplified["result"]
|
|
memory_info = {
|
|
"success": True,
|
|
"address": result.get("address", address),
|
|
"length": result.get("bytesRead", length),
|
|
"format": format,
|
|
}
|
|
|
|
if "hexBytes" in result:
|
|
memory_info["hexBytes"] = result["hexBytes"]
|
|
if "rawBytes" in result:
|
|
memory_info["rawBytes"] = result["rawBytes"]
|
|
|
|
return memory_info
|
|
|
|
return simplified
|
|
|
|
@mcp_tool()
|
|
def memory_write(
|
|
self,
|
|
address: str,
|
|
bytes_data: str,
|
|
format: str = "hex",
|
|
port: Optional[int] = None,
|
|
) -> Dict[str, Any]:
|
|
"""Write bytes to memory (use with caution).
|
|
|
|
Args:
|
|
address: Memory address in hex format
|
|
bytes_data: Data to write (format depends on 'format' parameter)
|
|
format: Input format - "hex", "base64", or "string" (default: "hex")
|
|
port: Ghidra instance port (optional)
|
|
|
|
Returns:
|
|
Operation result
|
|
"""
|
|
if not address:
|
|
return {
|
|
"success": False,
|
|
"error": {
|
|
"code": "MISSING_PARAMETER",
|
|
"message": "Address parameter is required",
|
|
},
|
|
}
|
|
|
|
if not bytes_data:
|
|
return {
|
|
"success": False,
|
|
"error": {
|
|
"code": "MISSING_PARAMETER",
|
|
"message": "Bytes parameter is required",
|
|
},
|
|
}
|
|
|
|
try:
|
|
port = self.get_instance_port(port)
|
|
except ValueError as e:
|
|
return {"success": False, "error": {"code": "NO_INSTANCE", "message": str(e)}}
|
|
|
|
payload = {
|
|
"bytes": bytes_data,
|
|
"format": format,
|
|
}
|
|
|
|
response = self.safe_patch(port, f"programs/current/memory/{address}", payload)
|
|
return self.simplify_response(response)
|