Allow renaming and retyping variables
This commit is contained in:
parent
dce6aa101c
commit
be08f0f2ea
@ -302,6 +302,43 @@ 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 list_variables(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100, search: str = "") -> list:
|
||||
"""List global variables with optional search"""
|
||||
params = {"offset": offset, "limit": limit}
|
||||
if search:
|
||||
params["search"] = search
|
||||
return safe_get(port, "variables", params)
|
||||
|
||||
@mcp.tool()
|
||||
def list_function_variables(port: int = DEFAULT_GHIDRA_PORT, function: str = "") -> str:
|
||||
"""List variables in a specific function"""
|
||||
if not function:
|
||||
return "Error: function name is required"
|
||||
|
||||
encoded_name = quote(function)
|
||||
return safe_get(port, f"functions/{encoded_name}/variables", {})
|
||||
|
||||
@mcp.tool()
|
||||
def rename_variable(port: int = DEFAULT_GHIDRA_PORT, function: str = "", name: str = "", new_name: str = "") -> str:
|
||||
"""Rename a variable in a function"""
|
||||
if not function or not name or not new_name:
|
||||
return "Error: function, name, and new_name parameters are required"
|
||||
|
||||
encoded_function = quote(function)
|
||||
encoded_var = quote(name)
|
||||
return safe_put(port, f"functions/{encoded_function}/variables/{encoded_var}", {"newName": new_name})
|
||||
|
||||
@mcp.tool()
|
||||
def retype_variable(port: int = DEFAULT_GHIDRA_PORT, function: str = "", name: str = "", data_type: str = "") -> str:
|
||||
"""Change the data type of a variable in a function"""
|
||||
if not function or not name or not data_type:
|
||||
return "Error: function, name, and data_type parameters are required"
|
||||
|
||||
encoded_function = quote(function)
|
||||
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
|
||||
|
||||
59
pom.xml
59
pom.xml
@ -6,7 +6,7 @@
|
||||
<groupId>eu.starsong.ghidra</groupId>
|
||||
<artifactId>GhydraMCP</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.1</version>
|
||||
<version>${revision}</version>
|
||||
<name>GhydraMCP</name>
|
||||
<url>https://github.com/teal-bauer/GhydraMCP</url>
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
<ghidra.jar.location>${project.basedir}/lib</ghidra.jar.location>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
<maven.install.skip>true</maven.install.skip>
|
||||
<revision>dev-SNAPSHOT</revision>
|
||||
<maven.build.timestamp.format>yyyyMMdd-HHmmss</maven.build.timestamp.format>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -101,6 +103,56 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Git Commit ID plugin to generate version from git -->
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>get-git-info</id>
|
||||
<phase>initialize</phase>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<generateGitPropertiesFile>true</generateGitPropertiesFile>
|
||||
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
|
||||
<includeOnlyProperties>
|
||||
<includeOnlyProperty>git.commit.id.abbrev</includeOnlyProperty>
|
||||
<includeOnlyProperty>git.commit.time</includeOnlyProperty>
|
||||
<includeOnlyProperty>git.closest.tag.name</includeOnlyProperty>
|
||||
<includeOnlyProperty>git.build.version</includeOnlyProperty>
|
||||
</includeOnlyProperties>
|
||||
<commitIdGenerationMode>full</commitIdGenerationMode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Set revision property from git info -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>set-revision-from-git</id>
|
||||
<phase>initialize</phase>
|
||||
<goals>
|
||||
<goal>regex-property</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<name>revision</name>
|
||||
<value>${git.commit.id.abbrev}-${maven.build.timestamp}</value>
|
||||
<regex>.*</regex>
|
||||
<replacement>$0</replacement>
|
||||
<failIfNoMatch>false</failIfNoMatch>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Use custom MANIFEST.MF -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@ -108,6 +160,9 @@
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
|
||||
<manifestEntries>
|
||||
<Plugin-Version>${revision}</Plugin-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<finalName>GhydraMCP</finalName>
|
||||
<excludes>
|
||||
@ -239,4 +294,4 @@
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
</project>
|
||||
@ -4,13 +4,24 @@ import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.main.ApplicationLevelPlugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.GlobalNamespace;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.pcode.HighVariable;
|
||||
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.symbol.*;
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.decompiler.ClangNode;
|
||||
import ghidra.app.decompiler.ClangTokenGroup;
|
||||
import ghidra.app.decompiler.ClangVariableToken;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.demangler.DemanglerUtil;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
@ -105,25 +116,69 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
|
||||
server.createContext("/functions/", exchange -> {
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
String name = path.substring(path.lastIndexOf('/') + 1);
|
||||
|
||||
// Handle sub-paths: /functions/{name}
|
||||
// or /functions/{name}/variables
|
||||
String[] pathParts = path.split("/");
|
||||
|
||||
if (pathParts.length < 3) {
|
||||
exchange.sendResponseHeaders(400, -1); // Bad Request
|
||||
return;
|
||||
}
|
||||
|
||||
String functionName = pathParts[2];
|
||||
try {
|
||||
name = java.net.URLDecoder.decode(name, StandardCharsets.UTF_8.name());
|
||||
functionName = java.net.URLDecoder.decode(functionName, StandardCharsets.UTF_8.name());
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Failed to decode function name", e);
|
||||
exchange.sendResponseHeaders(400, -1); // Bad Request
|
||||
return;
|
||||
}
|
||||
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
sendResponse(exchange, decompileFunctionByName(name));
|
||||
} else if ("PUT".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parsePostParams(exchange);
|
||||
String newName = params.get("newName");
|
||||
String response = renameFunction(name, newName)
|
||||
? "Renamed successfully" : "Rename failed";
|
||||
sendResponse(exchange, response);
|
||||
// Check if we're dealing with a variables request
|
||||
if (pathParts.length > 3 && "variables".equals(pathParts[3])) {
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
// List all variables in function
|
||||
sendResponse(exchange, listVariablesInFunction(functionName));
|
||||
} else if ("PUT".equals(exchange.getRequestMethod()) && pathParts.length > 4) {
|
||||
// Handle operations on a specific variable
|
||||
String variableName = pathParts[4];
|
||||
try {
|
||||
variableName = java.net.URLDecoder.decode(variableName, StandardCharsets.UTF_8.name());
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Failed to decode variable name", e);
|
||||
exchange.sendResponseHeaders(400, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> params = parsePostParams(exchange);
|
||||
if (params.containsKey("newName")) {
|
||||
// Rename variable
|
||||
String result = renameVariable(functionName, variableName, params.get("newName"));
|
||||
sendResponse(exchange, result);
|
||||
} else if (params.containsKey("dataType")) {
|
||||
// Retype variable
|
||||
String result = retypeVariable(functionName, variableName, params.get("dataType"));
|
||||
sendResponse(exchange, result);
|
||||
} else {
|
||||
sendResponse(exchange, "Missing required parameter: newName or dataType");
|
||||
}
|
||||
} else {
|
||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
||||
}
|
||||
} else {
|
||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
||||
// Simple function operations
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
sendResponse(exchange, decompileFunctionByName(functionName));
|
||||
} else if ("PUT".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parsePostParams(exchange);
|
||||
String newName = params.get("newName");
|
||||
String response = renameFunction(functionName, newName)
|
||||
? "Renamed successfully" : "Rename failed";
|
||||
sendResponse(exchange, response);
|
||||
} else {
|
||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -201,6 +256,24 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
||||
}
|
||||
});
|
||||
|
||||
// Global variables endpoint
|
||||
server.createContext("/variables", exchange -> {
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> qparams = parseQueryParams(exchange);
|
||||
int offset = parseIntOrDefault(qparams.get("offset"), 0);
|
||||
int limit = parseIntOrDefault(qparams.get("limit"), 100);
|
||||
String search = qparams.get("search");
|
||||
|
||||
if (search != null && !search.isEmpty()) {
|
||||
sendResponse(exchange, searchVariables(search, offset, limit));
|
||||
} else {
|
||||
sendResponse(exchange, listGlobalVariables(offset, limit));
|
||||
}
|
||||
} else {
|
||||
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
|
||||
}
|
||||
});
|
||||
|
||||
// Instance management endpoints
|
||||
server.createContext("/instances", exchange -> {
|
||||
@ -563,6 +636,373 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
Msg.error(this, "Failed to execute rename data on Swing thread", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// New variable handling methods
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
private String listVariablesInFunction(String functionName) {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) return "No program loaded";
|
||||
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
try {
|
||||
if (!decomp.openProgram(program)) {
|
||||
return "Failed to initialize decompiler";
|
||||
}
|
||||
|
||||
Function function = findFunctionByName(program, functionName);
|
||||
if (function == null) {
|
||||
return "Function not found: " + functionName;
|
||||
}
|
||||
|
||||
DecompileResults results = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
if (results == null || !results.decompileCompleted()) {
|
||||
return "Failed to decompile function: " + functionName;
|
||||
}
|
||||
|
||||
// Get high-level pcode representation for the function
|
||||
HighFunction highFunction = results.getHighFunction();
|
||||
if (highFunction == null) {
|
||||
return "Failed to get high function for: " + functionName;
|
||||
}
|
||||
|
||||
// Get local variables
|
||||
List<String> variables = new ArrayList<>();
|
||||
Iterator<HighSymbol> symbolIter = highFunction.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.getHighVariable() != null) {
|
||||
DataType dt = symbol.getDataType();
|
||||
String dtName = dt != null ? dt.getName() : "unknown";
|
||||
variables.add(String.format("%s: %s @ %s",
|
||||
symbol.getName(), dtName, symbol.getPCAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get parameters
|
||||
List<String> parameters = new ArrayList<>();
|
||||
// In older Ghidra versions, we need to filter symbols to find parameters
|
||||
symbolIter = highFunction.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.isParameter()) {
|
||||
DataType dt = symbol.getDataType();
|
||||
String dtName = dt != null ? dt.getName() : "unknown";
|
||||
parameters.add(String.format("%s: %s (parameter)",
|
||||
symbol.getName(), dtName));
|
||||
}
|
||||
}
|
||||
|
||||
// Format the response
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Function: ").append(functionName).append("\n\n");
|
||||
|
||||
sb.append("Parameters:\n");
|
||||
if (parameters.isEmpty()) {
|
||||
sb.append(" none\n");
|
||||
} else {
|
||||
for (String param : parameters) {
|
||||
sb.append(" ").append(param).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\nLocal Variables:\n");
|
||||
if (variables.isEmpty()) {
|
||||
sb.append(" none\n");
|
||||
} else {
|
||||
for (String var : variables) {
|
||||
sb.append(" ").append(var).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} finally {
|
||||
decomp.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetSymbol == null) {
|
||||
result.set("Variable not found: " + oldName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rename the variable
|
||||
HighFunctionDBUtil.updateDBVariable(targetSymbol, newName, targetSymbol.getDataType(),
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
result.set("Variable renamed from '" + oldName + "' to '" + newName + "'");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error renaming variable", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
} finally {
|
||||
program.endTransaction(tx, true);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException e) {
|
||||
Msg.error(this, "Failed to execute on Swing thread", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return result.get();
|
||||
}
|
||||
|
||||
private String retypeVariable(String functionName, String varName, String dataTypeName) {
|
||||
if (varName == null || varName.isEmpty() || dataTypeName == null || dataTypeName.isEmpty()) {
|
||||
return "Both variable name and data type are required";
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) return "No program loaded";
|
||||
|
||||
AtomicReference<String> result = new AtomicReference<>("Variable retype failed");
|
||||
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
int tx = program.startTransaction("Retype 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(varName)) {
|
||||
targetSymbol = symbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetSymbol == null) {
|
||||
result.set("Variable not found: " + varName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the data type by name
|
||||
DataType dataType = findDataType(program, dataTypeName);
|
||||
if (dataType == null) {
|
||||
result.set("Data type not found: " + dataTypeName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retype the variable
|
||||
HighFunctionDBUtil.updateDBVariable(targetSymbol, targetSymbol.getName(), dataType,
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
result.set("Variable '" + varName + "' retyped to '" + dataTypeName + "'");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error retyping variable", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
} finally {
|
||||
program.endTransaction(tx, true);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException e) {
|
||||
Msg.error(this, "Failed to execute on Swing thread", e);
|
||||
result.set("Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return result.get();
|
||||
}
|
||||
|
||||
private String listGlobalVariables(int offset, int limit) {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) return "No program loaded";
|
||||
|
||||
List<String> globalVars = new ArrayList<>();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
SymbolIterator it = symbolTable.getSymbolIterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Symbol symbol = it.next();
|
||||
// Check for globals - look for symbols that are in global space and not functions
|
||||
if (symbol.isGlobal() &&
|
||||
symbol.getSymbolType() != SymbolType.FUNCTION &&
|
||||
symbol.getSymbolType() != SymbolType.LABEL) {
|
||||
globalVars.add(String.format("%s @ %s",
|
||||
symbol.getName(), symbol.getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(globalVars);
|
||||
return paginateList(globalVars, offset, limit);
|
||||
}
|
||||
|
||||
private String searchVariables(String searchTerm, int offset, int limit) {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) return "No program loaded";
|
||||
if (searchTerm == null || searchTerm.isEmpty()) return "Search term is required";
|
||||
|
||||
List<String> matchedVars = new ArrayList<>();
|
||||
|
||||
// Search global variables
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
SymbolIterator it = symbolTable.getSymbolIterator();
|
||||
while (it.hasNext()) {
|
||||
Symbol symbol = it.next();
|
||||
if (symbol.isGlobal() &&
|
||||
symbol.getSymbolType() != SymbolType.FUNCTION &&
|
||||
symbol.getSymbolType() != SymbolType.LABEL &&
|
||||
symbol.getName().toLowerCase().contains(searchTerm.toLowerCase())) {
|
||||
matchedVars.add(String.format("%s @ %s (global)",
|
||||
symbol.getName(), symbol.getAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
// Search local variables in functions
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
try {
|
||||
if (decomp.openProgram(program)) {
|
||||
for (Function function : program.getFunctionManager().getFunctions(true)) {
|
||||
DecompileResults results = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
if (results != null && results.decompileCompleted()) {
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (highFunc != null) {
|
||||
// Check each local variable and parameter
|
||||
Iterator<HighSymbol> symbolIter = highFunc.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.getName().toLowerCase().contains(searchTerm.toLowerCase())) {
|
||||
if (symbol.isParameter()) {
|
||||
matchedVars.add(String.format("%s in %s (parameter)",
|
||||
symbol.getName(), function.getName()));
|
||||
} else {
|
||||
matchedVars.add(String.format("%s in %s @ %s (local)",
|
||||
symbol.getName(), function.getName(), symbol.getPCAddress()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
decomp.dispose();
|
||||
}
|
||||
|
||||
Collections.sort(matchedVars);
|
||||
|
||||
if (matchedVars.isEmpty()) {
|
||||
return "No variables matching '" + searchTerm + "'";
|
||||
}
|
||||
return paginateList(matchedVars, offset, limit);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
private Function findFunctionByName(Program program, String name) {
|
||||
if (program == null || name == null || name.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Function function : program.getFunctionManager().getFunctions(true)) {
|
||||
if (function.getName().equals(name)) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private DataType findDataType(Program program, String name) {
|
||||
if (program == null || name == null || name.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// First try direct lookup
|
||||
DataType dt = dtm.getDataType("/" + name);
|
||||
if (dt != null) {
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Try built-in types by simple name
|
||||
dt = dtm.findDataType(name);
|
||||
if (dt != null) {
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Try to find a matching type by name only
|
||||
Iterator<DataType> dtIter = dtm.getAllDataTypes();
|
||||
while (dtIter.hasNext()) {
|
||||
DataType type = dtIter.next();
|
||||
if (type.getName().equals(name)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Utility: parse query params, parse post params, pagination, etc.
|
||||
@ -711,4 +1151,4 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
activeInstances.remove(port);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user