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
This commit is contained in:
parent
4379bea14f
commit
bc1e137878
@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.0.0] - 2025-11-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **MCP Integration Refactor:** Refactored the Python bridge for improved MCP integration. (337f89e)
|
- **MCP Integration Refactor:** Refactored the Python bridge for improved MCP integration. (337f89e)
|
||||||
- Introduced MCP resources for loading context (e.g., instances, functions, disassembly).
|
- 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
|
- Initial project setup
|
||||||
- Basic MCP bridge functionality
|
- 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.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.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
|
[1.2]: https://github.com/teal-bauer/GhydraMCP/compare/v1.1...v1.2
|
||||||
|
|||||||
@ -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`:
|
1. **Plugin Version** in `src/main/java/eu/starsong/ghidra/api/ApiConstants.java`:
|
||||||
```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`:
|
2. **Bridge Version** in `bridge_mcp_hydra.py`:
|
||||||
```python
|
```python
|
||||||
BRIDGE_VERSION = "v2.0.0-beta.1"
|
BRIDGE_VERSION = "v2.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### API Versions
|
### API Versions
|
||||||
|
|||||||
31
README.md
31
README.md
@ -121,7 +121,7 @@ First, download the latest [release](https://github.com/teal-bauer/GhydraMCP/rel
|
|||||||
1. Run Ghidra
|
1. Run Ghidra
|
||||||
2. Select `File` -> `Install Extensions`
|
2. Select `File` -> `Install Extensions`
|
||||||
3. Click the `+` button
|
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
|
5. Restart Ghidra
|
||||||
6. Make sure the GhydraMCPPlugin is enabled in `File` -> `Configure` -> `Developer`
|
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)
|
- `update_data`: Update both name and type (params: address, name, data_type)
|
||||||
|
|
||||||
**Instance Management**:
|
**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)
|
- `register_instance`: Register new instance (params: port, url)
|
||||||
- `unregister_instance`: Remove instance (params: port)
|
- `unregister_instance`: Remove instance (params: port)
|
||||||
- `discover_instances`: Auto-discover running instances (params: host [optional])
|
|
||||||
|
|
||||||
**Example Usage**:
|
**Example Usage**:
|
||||||
```python
|
```python
|
||||||
@ -210,12 +210,10 @@ client.use_tool("ghydra", "update_data", {"address": "0x00401238", "name": "ptr_
|
|||||||
client.use_tool("ghydra", "delete_data", {"address": "0x0040123C"})
|
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": 8192, "url": "http://localhost:8192/"})
|
||||||
client.use_tool("ghydra", "register_instance", {"port": 8193})
|
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
|
## Client Setup
|
||||||
@ -256,24 +254,7 @@ Check which Ghidra instances are currently running.
|
|||||||
|
|
||||||
**Assistant:**
|
**Assistant:**
|
||||||
```
|
```
|
||||||
I'll check which Ghidra instances are currently running by discovering any active instances.
|
I'll check which Ghidra instances are currently running.
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
View result from list_instances from ghydra (local)
|
View result from list_instances from ghydra (local)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -31,7 +31,7 @@ DEFAULT_GHIDRA_HOST = "localhost"
|
|||||||
QUICK_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+10)
|
QUICK_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+10)
|
||||||
FULL_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+20)
|
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
|
REQUIRED_API_VERSION = 2005
|
||||||
|
|
||||||
current_instance_port = DEFAULT_GHIDRA_PORT
|
current_instance_port = DEFAULT_GHIDRA_PORT
|
||||||
@ -39,7 +39,10 @@ current_instance_port = DEFAULT_GHIDRA_PORT
|
|||||||
instructions = """
|
instructions = """
|
||||||
GhydraMCP allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware.
|
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:
|
The API is organized into namespaces for different types of operations:
|
||||||
- instances_* : For managing Ghidra instances
|
- instances_* : For managing Ghidra instances
|
||||||
@ -1151,7 +1154,19 @@ def reverse_engineer_binary_prompt(port: int = None):
|
|||||||
# Instance management tools
|
# Instance management tools
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def instances_list() -> dict:
|
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:
|
with instances_lock:
|
||||||
return {
|
return {
|
||||||
"instances": [
|
"instances": [
|
||||||
@ -1167,45 +1182,33 @@ def instances_list() -> dict:
|
|||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def instances_discover(host: str = None) -> dict:
|
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:
|
Args:
|
||||||
host: Optional host to scan (default: configured ghidra_host)
|
host: Host to scan for Ghidra instances (default: configured ghidra_host)
|
||||||
|
|
||||||
Returns:
|
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
|
# Discover instances on the specified host
|
||||||
discovery_result = _discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5)
|
_discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5)
|
||||||
new_instances = discovery_result.get("instances", [])
|
|
||||||
new_count = len(new_instances)
|
|
||||||
|
|
||||||
# Get all currently known instances (including ones that were already registered)
|
# Return all instances (same format as instances_list for consistency)
|
||||||
all_instances = []
|
|
||||||
with instances_lock:
|
with instances_lock:
|
||||||
for port, info in active_instances.items():
|
return {
|
||||||
instance_info = {
|
"instances": [
|
||||||
"port": port,
|
{
|
||||||
"url": info["url"],
|
"port": port,
|
||||||
"project": info.get("project", ""),
|
"url": info["url"],
|
||||||
"file": info.get("file", ""),
|
"project": info.get("project", ""),
|
||||||
"plugin_version": info.get("plugin_version", "unknown"),
|
"file": info.get("file", "")
|
||||||
"api_version": info.get("api_version", "unknown")
|
}
|
||||||
}
|
for port, info in active_instances.items()
|
||||||
|
]
|
||||||
# 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
|
|
||||||
}
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def instances_register(port: int, url: str = None) -> str:
|
def instances_register(port: int, url: str = None) -> str:
|
||||||
|
|||||||
10
pom.xml
10
pom.xml
@ -99,16 +99,6 @@
|
|||||||
</resources>
|
</resources>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- Set Java version -->
|
<!-- Set Java version -->
|
||||||
<!-- Resources plugin to handle filtering -->
|
|
||||||
<!-- <plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>3.3.1</version>
|
|
||||||
<configuration>
|
|
||||||
<encoding>UTF-8</encoding>
|
|
||||||
</configuration>
|
|
||||||
</plugin> -->
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package eu.starsong.ghidra.api;
|
package eu.starsong.ghidra.api;
|
||||||
|
|
||||||
public class ApiConstants {
|
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 API_VERSION = 2005;
|
||||||
public static final int DEFAULT_PORT = 8192;
|
public static final int DEFAULT_PORT = 8192;
|
||||||
public static final int MAX_PORT_ATTEMPTS = 10;
|
public static final int MAX_PORT_ATTEMPTS = 10;
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public class TransactionHelper {
|
|||||||
Msg.error(TransactionHelper.class, "Transaction failed: " + transactionName, e);
|
Msg.error(TransactionHelper.class, "Transaction failed: " + transactionName, e);
|
||||||
} finally {
|
} finally {
|
||||||
if (txId >= 0) {
|
if (txId >= 0) {
|
||||||
program.endTransaction(txId, success);
|
success = program.endTransaction(txId, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user