Expand functionality
Find variables, rename and retype them Additionally merge changes from https://github.com/LaurieWired/GhidraMCP/pull/16 and https://github.com/LaurieWired/GhidraMCP/pull/18
This commit is contained in:
parent
1bfdf74554
commit
399c76b29a
52
CHANGELOG.md
Normal file
52
CHANGELOG.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Added docstrings for all @mcp.tool functions
|
||||
- Variable manipulation tools (rename/retype variables)
|
||||
- New endpoints for function variable management
|
||||
- Dynamic version output in API responses
|
||||
- Enhanced function analysis capabilities
|
||||
- Support for searching variables by name
|
||||
- New tools for working with function variables:
|
||||
- get_function_by_address
|
||||
- get_current_address
|
||||
- get_current_function
|
||||
- decompile_function_by_address
|
||||
- disassemble_function
|
||||
- set_decompiler_comment
|
||||
- set_disassembly_comment
|
||||
- rename_local_variable
|
||||
- rename_function_by_address
|
||||
- set_function_prototype
|
||||
- set_local_variable_type
|
||||
|
||||
### Changed
|
||||
- Improved version handling in build system
|
||||
- Reorganized imports in bridge_mcp_hydra.py
|
||||
- Updated MANIFEST.MF with more detailed description
|
||||
|
||||
## [1.2.0] - 2024-06-15
|
||||
|
||||
### Added
|
||||
- Enhanced function analysis capabilities
|
||||
- Additional variable manipulation tools
|
||||
- Support for multiple Ghidra instances
|
||||
|
||||
### Changed
|
||||
- Improved error handling in API calls
|
||||
- Optimized performance for large binaries
|
||||
|
||||
## [1.0.0] - 2024-03-15
|
||||
|
||||
### Added
|
||||
- Initial release of GhydraMCP bridge
|
||||
- Basic Ghidra instance management tools
|
||||
- Function analysis tools
|
||||
- Variable manipulation tools
|
||||
@ -6,12 +6,14 @@
|
||||
# ]
|
||||
# ///
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import threading
|
||||
from typing import Dict
|
||||
import time
|
||||
from threading import Lock
|
||||
from typing import Dict
|
||||
|
||||
import requests
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
# Track active Ghidra instances (port -> info dict)
|
||||
@ -251,6 +253,7 @@ def _discover_instances(port_range, host=None, timeout=0.5) -> dict:
|
||||
# Updated tool implementations with port parameter
|
||||
from urllib.parse import quote
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_functions(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List all functions with pagination"""
|
||||
@ -278,30 +281,175 @@ def update_data(port: int = DEFAULT_GHIDRA_PORT, address: str = "", new_name: st
|
||||
|
||||
@mcp.tool()
|
||||
def list_segments(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List all memory segments in the current program with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of segments to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of segment information strings
|
||||
"""
|
||||
return safe_get(port, "segments", {"offset": offset, "limit": limit})
|
||||
|
||||
@mcp.tool()
|
||||
def list_imports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List all imported symbols with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of imports to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of import information strings
|
||||
"""
|
||||
return safe_get(port, "symbols/imports", {"offset": offset, "limit": limit})
|
||||
|
||||
@mcp.tool()
|
||||
def list_exports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List all exported symbols with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of exports to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of export information strings
|
||||
"""
|
||||
return safe_get(port, "symbols/exports", {"offset": offset, "limit": limit})
|
||||
|
||||
@mcp.tool()
|
||||
def list_namespaces(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
|
||||
"""List all namespaces in the current program with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of namespaces to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of namespace information strings
|
||||
"""
|
||||
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 all defined data items with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of data items to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of data item information strings
|
||||
"""
|
||||
return safe_get(port, "data", {"offset": offset, "limit": limit})
|
||||
|
||||
@mcp.tool()
|
||||
def search_functions_by_name(port: int = DEFAULT_GHIDRA_PORT, query: str = "", offset: int = 0, limit: int = 100) -> list:
|
||||
"""Search for functions by name with pagination
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
query: Search string to match against function names
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum number of functions to return (default: 100)
|
||||
|
||||
Returns:
|
||||
List of matching function information strings or error message if query is empty
|
||||
"""
|
||||
if not query:
|
||||
return ["Error: query string is required"]
|
||||
return safe_get(port, "functions", {"query": query, "offset": offset, "limit": limit})
|
||||
|
||||
@mcp.tool()
|
||||
def get_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> str:
|
||||
"""
|
||||
Get a function by its address.
|
||||
"""
|
||||
return "\n".join(safe_get(port, "get_function_by_address", {"address": address}))
|
||||
|
||||
@mcp.tool()
|
||||
def get_current_address(port: int = DEFAULT_GHIDRA_PORT) -> str:
|
||||
"""
|
||||
Get the address currently selected by the user.
|
||||
"""
|
||||
return "\n".join(safe_get(port, "get_current_address"))
|
||||
|
||||
@mcp.tool()
|
||||
def get_current_function(port: int = DEFAULT_GHIDRA_PORT) -> str:
|
||||
"""
|
||||
Get the function currently selected by the user.
|
||||
"""
|
||||
return "\n".join(safe_get(port, "get_current_function"))
|
||||
|
||||
@mcp.tool()
|
||||
def list_functions(port: int = DEFAULT_GHIDRA_PORT) -> list:
|
||||
"""
|
||||
List all functions in the database.
|
||||
"""
|
||||
return safe_get(port, "list_functions")
|
||||
|
||||
@mcp.tool()
|
||||
def decompile_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> str:
|
||||
"""
|
||||
Decompile a function at the given address.
|
||||
"""
|
||||
return "\n".join(safe_get(port, "decompile_function", {"address": address}))
|
||||
|
||||
@mcp.tool()
|
||||
def disassemble_function(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> list:
|
||||
"""
|
||||
Get assembly code (address: instruction; comment) for a function.
|
||||
"""
|
||||
return safe_get(port, "disassemble_function", {"address": address})
|
||||
|
||||
@mcp.tool()
|
||||
def set_decompiler_comment(port: int = DEFAULT_GHIDRA_PORT, address: str = "", comment: str = "") -> str:
|
||||
"""
|
||||
Set a comment for a given address in the function pseudocode.
|
||||
"""
|
||||
return safe_post(port, "set_decompiler_comment", {"address": address, "comment": comment})
|
||||
|
||||
@mcp.tool()
|
||||
def set_disassembly_comment(port: int = DEFAULT_GHIDRA_PORT, address: str = "", comment: str = "") -> str:
|
||||
"""
|
||||
Set a comment for a given address in the function disassembly.
|
||||
"""
|
||||
return safe_post(port, "set_disassembly_comment", {"address": address, "comment": comment})
|
||||
|
||||
@mcp.tool()
|
||||
def rename_local_variable(port: int = DEFAULT_GHIDRA_PORT, function_address: str = "", old_name: str = "", new_name: str = "") -> str:
|
||||
"""
|
||||
Rename a local variable in a function.
|
||||
"""
|
||||
return safe_post(port, "rename_local_variable", {"function_address": function_address, "old_name": old_name, "new_name": new_name})
|
||||
|
||||
@mcp.tool()
|
||||
def rename_function_by_address(port: int = DEFAULT_GHIDRA_PORT, function_address: str = "", new_name: str = "") -> str:
|
||||
"""
|
||||
Rename a function by its address.
|
||||
"""
|
||||
return safe_post(port, "rename_function_by_address", {"function_address": function_address, "new_name": new_name})
|
||||
|
||||
@mcp.tool()
|
||||
def set_function_prototype(port: int = DEFAULT_GHIDRA_PORT, function_address: str = "", prototype: str = "") -> str:
|
||||
"""
|
||||
Set a function's prototype.
|
||||
"""
|
||||
return safe_post(port, "set_function_prototype", {"function_address": function_address, "prototype": prototype})
|
||||
|
||||
@mcp.tool()
|
||||
def set_local_variable_type(port: int = DEFAULT_GHIDRA_PORT, function_address: str = "", variable_name: str = "", new_type: str = "") -> str:
|
||||
"""
|
||||
Set a local variable's type.
|
||||
"""
|
||||
return safe_post(port, "set_local_variable_type", {"function_address": function_address, "variable_name": variable_name, "new_type": new_type})
|
||||
|
||||
@mcp.tool()
|
||||
def list_variables(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100, search: str = "") -> list:
|
||||
"""List global variables with optional search"""
|
||||
@ -339,10 +487,6 @@ def retype_variable(port: int = DEFAULT_GHIDRA_PORT, function: str = "", name: s
|
||||
encoded_var = quote(name)
|
||||
return safe_put(port, f"functions/{encoded_function}/variables/{encoded_var}", {"dataType": data_type})
|
||||
|
||||
# Handle graceful shutdown
|
||||
import signal
|
||||
import os
|
||||
|
||||
def handle_sigint(signum, frame):
|
||||
os._exit(0)
|
||||
|
||||
|
||||
23
pom.xml
23
pom.xml
@ -21,7 +21,14 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Ghidra JARs as system-scoped dependencies for runtime -->
|
||||
<!-- JSON handling -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Ghidra JARs as system-scoped dependencies -->
|
||||
<dependency>
|
||||
<groupId>ghidra</groupId>
|
||||
<artifactId>Generic</artifactId>
|
||||
@ -72,13 +79,6 @@
|
||||
<systemPath>${ghidra.jar.location}/Base.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON Simple for JSON handling -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@ -265,7 +265,14 @@
|
||||
<configuration>
|
||||
<failOnWarning>false</failOnWarning>
|
||||
<ignoredUnusedDeclaredDependencies>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Generic</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:SoftwareModeling</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Project</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Docking</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Decompiler</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Utility</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>ghidra:Base</ignoredUnusedDeclaredDependency>
|
||||
<ignoredUnusedDeclaredDependency>junit:junit</ignoredUnusedDeclaredDependency>
|
||||
</ignoredUnusedDeclaredDependencies>
|
||||
<ignoredSystemDependencies>
|
||||
<ignoredSystemDependency>ghidra:*</ignoredSystemDependency>
|
||||
|
||||
@ -13,6 +13,8 @@ import ghidra.program.model.pcode.HighSymbol;
|
||||
import ghidra.program.model.pcode.VarnodeAST;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||
import ghidra.program.model.pcode.LocalSymbolMap;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
@ -46,6 +48,23 @@ import java.util.concurrent.atomic.*;
|
||||
// For JSON response handling
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.util.PseudoDisassembler;
|
||||
import ghidra.app.cmd.function.SetVariableNameCmd;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.listing.LocalVariableImpl;
|
||||
import ghidra.program.model.listing.ParameterImpl;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.data.Undefined1DataType;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = ghidra.app.DeveloperPluginPackage.NAME,
|
||||
@ -421,7 +440,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
for (Function f : program.getFunctionManager().getFunctions(true)) {
|
||||
names.add(f.getName());
|
||||
names.add(f.getName() + " @ " + f.getEntryPoint());
|
||||
}
|
||||
return paginateList(names, offset, limit);
|
||||
}
|
||||
@ -723,75 +742,128 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
}
|
||||
|
||||
private String renameVariable(String functionName, String oldName, String newName) {
|
||||
if (oldName == null || oldName.isEmpty() || newName == null || newName.isEmpty()) {
|
||||
return "Both old and new variable names are required";
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) return "No program loaded";
|
||||
|
||||
AtomicReference<String> result = new AtomicReference<>("Variable rename failed");
|
||||
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
int tx = program.startTransaction("Rename variable via HTTP");
|
||||
try {
|
||||
Function function = findFunctionByName(program, functionName);
|
||||
if (function == null) {
|
||||
result.set("Function not found: " + functionName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize decompiler
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
decomp.openProgram(program);
|
||||
DecompileResults decompRes = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
|
||||
if (decompRes == null || !decompRes.decompileCompleted()) {
|
||||
result.set("Failed to decompile function: " + functionName);
|
||||
return;
|
||||
}
|
||||
|
||||
HighFunction highFunction = decompRes.getHighFunction();
|
||||
if (highFunction == null) {
|
||||
result.set("Failed to get high function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the variable by name
|
||||
HighSymbol targetSymbol = null;
|
||||
Iterator<HighSymbol> symbolIter = highFunction.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.getName().equals(oldName)) {
|
||||
targetSymbol = symbol;
|
||||
Function func = null;
|
||||
for (Function f : program.getFunctionManager().getFunctions(true)) {
|
||||
if (f.getName().equals(functionName)) {
|
||||
func = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetSymbol == null) {
|
||||
result.set("Variable not found: " + oldName);
|
||||
return;
|
||||
if (func == null) {
|
||||
return "Function not found";
|
||||
}
|
||||
|
||||
// Rename the variable
|
||||
HighFunctionDBUtil.updateDBVariable(targetSymbol, newName, targetSymbol.getDataType(),
|
||||
SourceType.USER_DEFINED);
|
||||
DecompileResults result = decomp.decompileFunction(func, 30, new ConsoleTaskMonitor());
|
||||
if (result == null || !result.decompileCompleted()) {
|
||||
return "Decompilation failed";
|
||||
}
|
||||
|
||||
result.set("Variable renamed from '" + oldName + "' to '" + newName + "'");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error renaming variable", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
} finally {
|
||||
HighFunction highFunction = result.getHighFunction();
|
||||
if (highFunction == null) {
|
||||
return "Decompilation failed (no high function)";
|
||||
}
|
||||
|
||||
LocalSymbolMap localSymbolMap = highFunction.getLocalSymbolMap();
|
||||
if (localSymbolMap == null) {
|
||||
return "Decompilation failed (no local symbol map)";
|
||||
}
|
||||
|
||||
HighSymbol highSymbol = null;
|
||||
Iterator<HighSymbol> symbols = localSymbolMap.getSymbols();
|
||||
while (symbols.hasNext()) {
|
||||
HighSymbol symbol = symbols.next();
|
||||
String symbolName = symbol.getName();
|
||||
|
||||
if (symbolName.equals(oldName)) {
|
||||
highSymbol = symbol;
|
||||
}
|
||||
if (symbolName.equals(newName)) {
|
||||
return "Error: A variable with name '" + newName + "' already exists in this function";
|
||||
}
|
||||
}
|
||||
|
||||
if (highSymbol == null) {
|
||||
return "Variable not found";
|
||||
}
|
||||
|
||||
boolean commitRequired = checkFullCommit(highSymbol, highFunction);
|
||||
|
||||
final HighSymbol finalHighSymbol = highSymbol;
|
||||
final Function finalFunction = func;
|
||||
AtomicBoolean successFlag = new AtomicBoolean(false);
|
||||
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
int tx = program.startTransaction("Rename variable");
|
||||
try {
|
||||
if (commitRequired) {
|
||||
HighFunctionDBUtil.commitParamsToDatabase(highFunction, false,
|
||||
ReturnCommitOption.NO_COMMIT, finalFunction.getSignatureSource());
|
||||
}
|
||||
HighFunctionDBUtil.updateDBVariable(
|
||||
finalHighSymbol,
|
||||
newName,
|
||||
null,
|
||||
SourceType.USER_DEFINED
|
||||
);
|
||||
successFlag.set(true);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Failed to rename variable", e);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(tx, true);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException e) {
|
||||
Msg.error(this, "Failed to execute on Swing thread", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
String errorMsg = "Failed to execute rename on Swing thread: " + e.getMessage();
|
||||
Msg.error(this, errorMsg, e);
|
||||
return errorMsg;
|
||||
}
|
||||
return successFlag.get() ? "Variable renamed" : "Failed to rename variable";
|
||||
}
|
||||
|
||||
return result.get();
|
||||
/**
|
||||
* Copied from AbstractDecompilerAction.checkFullCommit, it's protected.
|
||||
* Compare the given HighFunction's idea of the prototype with the Function's idea.
|
||||
* Return true if there is a difference. If a specific symbol is being changed,
|
||||
* it can be passed in to check whether or not the prototype is being affected.
|
||||
* @param highSymbol (if not null) is the symbol being modified
|
||||
* @param hfunction is the given HighFunction
|
||||
* @return true if there is a difference (and a full commit is required)
|
||||
*/
|
||||
protected static boolean checkFullCommit(HighSymbol highSymbol, HighFunction hfunction) {
|
||||
if (highSymbol != null && !highSymbol.isParameter()) {
|
||||
return false;
|
||||
}
|
||||
Function function = hfunction.getFunction();
|
||||
Parameter[] parameters = function.getParameters();
|
||||
LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap();
|
||||
int numParams = localSymbolMap.getNumParams();
|
||||
if (numParams != parameters.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numParams; i++) {
|
||||
HighSymbol param = localSymbolMap.getParamSymbol(i);
|
||||
if (param.getCategoryIndex() != i) {
|
||||
return true;
|
||||
}
|
||||
VariableStorage storage = param.getStorage();
|
||||
// Don't compare using the equals method so that DynamicVariableStorage can match
|
||||
if (0 != storage.compareTo(parameters[i].getVariableStorage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String retypeVariable(String functionName, String varName, String dataTypeName) {
|
||||
@ -830,12 +902,13 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the variable by name
|
||||
// Find the variable by name - must match exactly and be in current scope
|
||||
HighSymbol targetSymbol = null;
|
||||
Iterator<HighSymbol> symbolIter = highFunction.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.getName().equals(varName)) {
|
||||
if (symbol.getName().equals(varName) &&
|
||||
symbol.getPCAddress().equals(function.getEntryPoint())) {
|
||||
targetSymbol = symbol;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
GHIDRA_MODULE_NAME=GhydraMCP
|
||||
GHIDRA_MODULE_DESC=A multi-headed REST interface for Ghidra for use with MCP agents.
|
||||
Manifest-Version: 1.0
|
||||
GHIDRA_MODULE_NAME: GhydraMCP
|
||||
GHIDRA_MODULE_DESC: A multi-headed REST interface for Ghidra for use with MCP agents.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user