From bc1e137878c1ae604deeda23274bea9ed4e9272a Mon Sep 17 00:00:00 2001 From: Teal Bauer Date: Tue, 11 Nov 2025 12:54:03 +0100 Subject: [PATCH] chore: prepare v2.0.0 release - Update version to v2.0.0 in ApiConstants.java and bridge_mcp_hydra.py - Create CHANGELOG v2.0.0 section with release date - Fix Ghidra 11.3.2+ compatibility in TransactionHelper (endTransaction signature) - Clarify instances_list vs instances_discover usage in documentation - Remove commented-out code in pom.xml Fixes #7 Closes #5 --- CHANGELOG.md | 5 +- CONTRIBUTING.md | 4 +- README.md | 33 ++------- bridge_mcp_hydra.py | 73 ++++++++++--------- pom.xml | 10 --- .../eu/starsong/ghidra/api/ApiConstants.java | 2 +- .../ghidra/util/TransactionHelper.java | 2 +- 7 files changed, 53 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8be978..b9c325b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [2.0.0] - 2025-11-11 + ### Added - **MCP Integration Refactor:** Refactored the Python bridge for improved MCP integration. (337f89e) - Introduced MCP resources for loading context (e.g., instances, functions, disassembly). @@ -115,7 +117,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Initial project setup - Basic MCP bridge functionality -[unreleased]: https://github.com/teal-bauer/GhydraMCP/compare/v1.4.0...HEAD +[unreleased]: https://github.com/teal-bauer/GhydraMCP/compare/v2.0.0...HEAD +[2.0.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.4.0...v2.0.0 [1.4.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.2...v1.3.0 [1.2]: https://github.com/teal-bauer/GhydraMCP/compare/v1.1...v1.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 581df41..7c83b7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,12 +83,12 @@ When making changes, update version numbers in these locations: 1. **Plugin Version** in `src/main/java/eu/starsong/ghidra/api/ApiConstants.java`: ```java - public static final String PLUGIN_VERSION = "v2.0.0-beta.1"; + public static final String PLUGIN_VERSION = "v2.0.0"; ``` 2. **Bridge Version** in `bridge_mcp_hydra.py`: ```python - BRIDGE_VERSION = "v2.0.0-beta.1" + BRIDGE_VERSION = "v2.0.0" ``` ### API Versions diff --git a/README.md b/README.md index 0a55df2..cbf86cd 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ First, download the latest [release](https://github.com/teal-bauer/GhydraMCP/rel 1. Run Ghidra 2. Select `File` -> `Install Extensions` 3. Click the `+` button -4. Select the `GhydraMCP-2.0.0-beta.1.zip` (or your chosen version) from the downloaded release +4. Select the `GhydraMCP-[version].zip` file from the downloaded release 5. Restart Ghidra 6. Make sure the GhydraMCPPlugin is enabled in `File` -> `Configure` -> `Developer` @@ -177,10 +177,10 @@ Theoretically, any MCP client should work with GhydraMCP. Two examples are given - `update_data`: Update both name and type (params: address, name, data_type) **Instance Management**: -- `list_instances`: List active Ghidra instances (no params) +- `list_instances`: List active Ghidra instances, automatically discovering new ones on default host (no params) - **use this first** +- `discover_instances`: Discover instances on a specific host (params: host [optional]) - **only use for non-default hosts** - `register_instance`: Register new instance (params: port, url) - `unregister_instance`: Remove instance (params: port) -- `discover_instances`: Auto-discover running instances (params: host [optional]) **Example Usage**: ```python @@ -209,13 +209,11 @@ client.use_tool("ghydra", "rename_data", {"address": "0x00401234", "name": "my_v client.use_tool("ghydra", "update_data", {"address": "0x00401238", "name": "ptr_var", "data_type": "char *"}) client.use_tool("ghydra", "delete_data", {"address": "0x0040123C"}) -# Instance management +# Instance management +client.use_tool("ghydra", "list_instances") # Lists all instances (auto-discovers on default host) +client.use_tool("ghydra", "discover_instances", {"host": "192.168.1.10"}) # Only if scanning different host client.use_tool("ghydra", "register_instance", {"port": 8192, "url": "http://localhost:8192/"}) client.use_tool("ghydra", "register_instance", {"port": 8193}) - -# Auto-discover instances -client.use_tool("ghydra", "discover_instances") # Default host -client.use_tool("ghydra", "discover_instances", {"host": "192.168.1.10"}) # Custom host ``` ## Client Setup @@ -256,24 +254,7 @@ Check which Ghidra instances are currently running. **Assistant:** ``` -I'll check which Ghidra instances are currently running by discovering any active instances. - -View result from discover_instances from ghydra (local) -{ - "found": 2, - "instances": [ - { - "port": 8192, - "url": "http://localhost:8192", - "result": "Registered instance on port 8192 at http://localhost:8192" - }, - { - "port": 8193, - "url": "http://localhost:8193", - "result": "Registered instance on port 8193 at http://localhost:8193" - } - ] -} +I'll check which Ghidra instances are currently running. View result from list_instances from ghydra (local) { diff --git a/bridge_mcp_hydra.py b/bridge_mcp_hydra.py index 8086f16..1e33689 100644 --- a/bridge_mcp_hydra.py +++ b/bridge_mcp_hydra.py @@ -31,7 +31,7 @@ DEFAULT_GHIDRA_HOST = "localhost" QUICK_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+10) FULL_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+20) -BRIDGE_VERSION = "v2.0.0-beta.5" +BRIDGE_VERSION = "v2.0.0" REQUIRED_API_VERSION = 2005 current_instance_port = DEFAULT_GHIDRA_PORT @@ -39,7 +39,10 @@ current_instance_port = DEFAULT_GHIDRA_PORT instructions = """ GhydraMCP allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware. -First, run `instances_discover()` to find all available Ghidra instances (both already known and newly discovered). Then use `instances_use(port)` to set your working instance. +First, run `instances_list()` to see all available Ghidra instances (automatically discovers instances on the default host). +Then use `instances_use(port)` to set your working instance. + +Note: Use `instances_discover(host)` only if you need to scan a different host. The API is organized into namespaces for different types of operations: - instances_* : For managing Ghidra instances @@ -1151,7 +1154,19 @@ def reverse_engineer_binary_prompt(port: int = None): # Instance management tools @mcp.tool() def instances_list() -> dict: - """List all active Ghidra instances""" + """List all active Ghidra instances + + This is the primary tool for working with instances. It automatically discovers + new instances on the default host before listing. + + Use instances_discover(host) only if you need to scan a different host. + + Returns: + dict: Contains 'instances' list with all available Ghidra instances + """ + # Auto-discover new instances before listing + _discover_instances(QUICK_DISCOVERY_RANGE, host=None, timeout=0.5) + with instances_lock: return { "instances": [ @@ -1167,45 +1182,33 @@ def instances_list() -> dict: @mcp.tool() def instances_discover(host: str = None) -> dict: - """Discover available Ghidra instances by scanning ports + """Discover Ghidra instances on a specific host + + Use this ONLY when you need to discover instances on a different host. + For normal usage, just use instances_list() which auto-discovers on the default host. Args: - host: Optional host to scan (default: configured ghidra_host) + host: Host to scan for Ghidra instances (default: configured ghidra_host) Returns: - dict: Contains 'found' count, 'new_instances' count, and 'instances' list with all available instances + dict: Contains 'instances' list with all available instances after discovery """ - # Get newly discovered instances - discovery_result = _discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5) - new_instances = discovery_result.get("instances", []) - new_count = len(new_instances) + # Discover instances on the specified host + _discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5) - # Get all currently known instances (including ones that were already registered) - all_instances = [] + # Return all instances (same format as instances_list for consistency) with instances_lock: - for port, info in active_instances.items(): - instance_info = { - "port": port, - "url": info["url"], - "project": info.get("project", ""), - "file": info.get("file", ""), - "plugin_version": info.get("plugin_version", "unknown"), - "api_version": info.get("api_version", "unknown") - } - - # Mark if this was newly discovered in this call - instance_info["newly_discovered"] = any(inst["port"] == port for inst in new_instances) - - all_instances.append(instance_info) - - # Sort by port for consistent ordering - all_instances.sort(key=lambda x: x["port"]) - - return { - "found": len(all_instances), # Total instances available - "new_instances": new_count, # How many were newly discovered - "instances": all_instances # All available instances - } + return { + "instances": [ + { + "port": port, + "url": info["url"], + "project": info.get("project", ""), + "file": info.get("file", "") + } + for port, info in active_instances.items() + ] + } @mcp.tool() def instances_register(port: int, url: str = None) -> str: diff --git a/pom.xml b/pom.xml index 1cf7db5..3e7bead 100644 --- a/pom.xml +++ b/pom.xml @@ -99,16 +99,6 @@ - - - org.apache.maven.plugins maven-compiler-plugin diff --git a/src/main/java/eu/starsong/ghidra/api/ApiConstants.java b/src/main/java/eu/starsong/ghidra/api/ApiConstants.java index 4dedbc8..98941c4 100644 --- a/src/main/java/eu/starsong/ghidra/api/ApiConstants.java +++ b/src/main/java/eu/starsong/ghidra/api/ApiConstants.java @@ -1,7 +1,7 @@ package eu.starsong.ghidra.api; public class ApiConstants { - public static final String PLUGIN_VERSION = "v2.0.0-beta.5"; + public static final String PLUGIN_VERSION = "v2.0.0"; public static final int API_VERSION = 2005; public static final int DEFAULT_PORT = 8192; public static final int MAX_PORT_ATTEMPTS = 10; diff --git a/src/main/java/eu/starsong/ghidra/util/TransactionHelper.java b/src/main/java/eu/starsong/ghidra/util/TransactionHelper.java index c9aed85..27898bc 100644 --- a/src/main/java/eu/starsong/ghidra/util/TransactionHelper.java +++ b/src/main/java/eu/starsong/ghidra/util/TransactionHelper.java @@ -38,7 +38,7 @@ public class TransactionHelper { Msg.error(TransactionHelper.class, "Transaction failed: " + transactionName, e); } finally { if (txId >= 0) { - program.endTransaction(txId, success); + success = program.endTransaction(txId, success); } } });