feat: Add strings endpoint to list string data in the binary
The new endpoint provides: - GET /strings endpoint with pagination and filtering - Python bridge support via list_strings() function - Searching of string data types across memory blocks - Filtering options for string content
This commit is contained in:
parent
25f353a4f3
commit
f71f4aa43b
@ -1616,6 +1616,34 @@ def rename_data(port: int = DEFAULT_GHIDRA_PORT,
|
||||
return simplify_response(response)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_strings(port: int = DEFAULT_GHIDRA_PORT,
|
||||
offset: int = 0,
|
||||
limit: int = 2000,
|
||||
filter: str = None) -> dict:
|
||||
"""List all defined strings in the binary with their memory addresses
|
||||
|
||||
Args:
|
||||
port: Ghidra instance port (default: 8192)
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum strings to return (default: 2000)
|
||||
filter: Optional string content filter
|
||||
|
||||
Returns:
|
||||
dict: List of string data with addresses, values, and metadata
|
||||
"""
|
||||
params = {
|
||||
"offset": offset,
|
||||
"limit": limit
|
||||
}
|
||||
|
||||
if filter:
|
||||
params["filter"] = filter
|
||||
|
||||
response = safe_get(port, "strings", params)
|
||||
return simplify_response(response)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def update_data(port: int = DEFAULT_GHIDRA_PORT,
|
||||
address: str = "",
|
||||
|
||||
@ -374,6 +374,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
.addLink("functions", "/functions")
|
||||
.addLink("symbols", "/symbols")
|
||||
.addLink("data", "/data")
|
||||
.addLink("strings", "/strings")
|
||||
.addLink("segments", "/segments")
|
||||
.addLink("memory", "/memory")
|
||||
.addLink("xrefs", "/xrefs")
|
||||
|
||||
@ -84,6 +84,18 @@ package eu.starsong.ghidra.endpoints;
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
server.createContext("/strings", exchange -> {
|
||||
try {
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
handleListStrings(exchange);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /strings endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void handleData(HttpExchange exchange) throws IOException {
|
||||
@ -1294,4 +1306,100 @@ package eu.starsong.ghidra.endpoints;
|
||||
}
|
||||
|
||||
// Note: The handleUpdateData method is already defined earlier in this file at line 477
|
||||
|
||||
/**
|
||||
* Handle request to list strings in the binary
|
||||
* @param exchange The HTTP exchange
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void handleListStrings(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
Map<String, String> qparams = parseQueryParams(exchange);
|
||||
int offset = parseIntOrDefault(qparams.get("offset"), 0);
|
||||
int limit = parseIntOrDefault(qparams.get("limit"), 2000);
|
||||
String filter = qparams.get("filter");
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> strings = new ArrayList<>();
|
||||
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!block.isInitialized()) continue;
|
||||
|
||||
DataIterator it = program.getListing().getDefinedData(block.getStart(), true);
|
||||
while (it.hasNext()) {
|
||||
Data data = it.next();
|
||||
if (!block.contains(data.getAddress())) continue;
|
||||
|
||||
// Check if the data type is a string type
|
||||
String dataTypeName = data.getDataType().getName().toLowerCase();
|
||||
boolean isString = dataTypeName.contains("string") ||
|
||||
dataTypeName.contains("unicode") ||
|
||||
(dataTypeName.contains("char") && data.getLength() > 1); // Array of chars
|
||||
|
||||
if (isString) {
|
||||
// Get the string value
|
||||
String value = data.getDefaultValueRepresentation();
|
||||
if (value == null) value = "";
|
||||
|
||||
// Skip if it doesn't match the filter
|
||||
if (filter != null && !filter.isEmpty() && !value.toLowerCase().contains(filter.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> stringInfo = new HashMap<>();
|
||||
stringInfo.put("address", data.getAddress().toString());
|
||||
stringInfo.put("value", value);
|
||||
stringInfo.put("length", data.getLength());
|
||||
stringInfo.put("type", data.getDataType().getName());
|
||||
|
||||
// If the data has a label/name, include it
|
||||
String name = null;
|
||||
Symbol symbol = program.getSymbolTable().getPrimarySymbol(data.getAddress());
|
||||
if (symbol != null) {
|
||||
name = symbol.getName();
|
||||
}
|
||||
stringInfo.put("name", name != null ? name : "");
|
||||
|
||||
// Add HATEOAS links
|
||||
Map<String, Object> links = new HashMap<>();
|
||||
Map<String, String> selfLink = new HashMap<>();
|
||||
selfLink.put("href", "/data/" + data.getAddress().toString());
|
||||
links.put("self", selfLink);
|
||||
|
||||
Map<String, String> memoryLink = new HashMap<>();
|
||||
memoryLink.put("href", "/memory?address=" + data.getAddress().toString());
|
||||
links.put("memory", memoryLink);
|
||||
|
||||
stringInfo.put("_links", links);
|
||||
|
||||
strings.add(stringInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build response with HATEOAS links
|
||||
eu.starsong.ghidra.api.ResponseBuilder builder = new eu.starsong.ghidra.api.ResponseBuilder(exchange, port)
|
||||
.success(true);
|
||||
|
||||
// Apply pagination and get paginated items
|
||||
List<Map<String, Object>> paginated = applyPagination(strings, offset, limit, builder, "/strings");
|
||||
|
||||
// Set the paginated result
|
||||
builder.result(paginated);
|
||||
|
||||
// Add program link
|
||||
builder.addLink("program", "/program");
|
||||
builder.addLink("data", "/data");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error listing strings", e);
|
||||
sendErrorResponse(exchange, 500, "Error listing strings: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user