package com.lauriewired.handlers.get;
import com.lauriewired.handlers.Handler;
import com.sun.net.httpserver.HttpExchange;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.model.listing.Program;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.util.task.TaskMonitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import static com.lauriewired.util.ParseUtils.*;
import ghidra.program.model.data.CategoryPath;
import static ghidra.program.util.GhidraProgramUtilities.getCurrentProgram;
/**
* Handler to find all uses of a specific member of a structure by analyzing decompiled code.
* This comprehensive approach searches through all functions to find member accesses in
* function parameters, local variables, dynamic allocations, and other contexts that
* wouldn't be found by only examining global typed data.
*
* The handler decompiles each function and uses regex patterns to find:
* - Direct member access: variable.member_name
* - Pointer member access: variable->member_name
* - Dereference access: (*variable).member_name
*
* Expects query parameters: struct_name, member_name, offset, limit
*/
public final class FindUsesOfStructMember extends Handler {
public FindUsesOfStructMember(PluginTool tool) {
super(tool, "/find_uses_of_struct_member");
}
/**
* Handles the HTTP request to find uses of a struct member.
* Expects query parameters: struct_name, member_name, offset, limit
*
* @param exchange the HTTP exchange containing the request
* @throws Exception if an error occurs while processing the request
*/
@Override
public void handle(HttpExchange exchange) throws Exception {
Map<String, String> qparams = parseQueryParams(exchange);
String structName = qparams.get("struct_name");
String memberName = qparams.get("member_name");
int offset = parseIntOrDefault(qparams.get("offset"), 0);
int limit = parseIntOrDefault(qparams.get("limit"), 100);
sendResponse(exchange, findUsesOfStructMember(structName, memberName, offset, limit));
}
/**
* Finds all uses of a specific member of a structure by searching through decompiled code.
* This approach finds member accesses in function parameters, local variables, and other contexts
* that wouldn't be found by just looking at global typed data.
*
* @param structName the name of the structure
* @param memberName the name of the member within the structure
* @param offset the starting index for pagination
* @param limit the maximum number of results to return
* @return a string containing the member uses or an error message
*/
private String findUsesOfStructMember(String structName, String memberName, int offset, int limit) {
Program program = getCurrentProgram(tool);
if (program == null)
return "No program loaded";
if (structName == null || structName.isEmpty())
return "Struct name is required";
if (memberName == null || memberName.isEmpty())
return "Member name is required";
try {
// Verify the structure exists
DataTypeManager dtm = program.getDataTypeManager();
CategoryPath path = new CategoryPath("/");
DataType dt = dtm.getDataType(path, structName);
if (dt == null || !(dt instanceof Structure)) {
return "Error: Struct " + structName + " not found";
}
Structure struct = (Structure) dt;
// Verify the member exists in the structure
boolean memberFound = false;
DataTypeComponent[] components = struct.getDefinedComponents();
for (DataTypeComponent component : components) {
if (memberName.equals(component.getFieldName())) {
memberFound = true;
break;
}
}
if (!memberFound) {
return "Error: Member '" + memberName + "' not found in struct " + structName;
}
List<String> refs = new ArrayList<>();
// Initialize decompiler
DecompInterface decompiler = new DecompInterface();
decompiler.openProgram(program);
// Create regex patterns to find struct member accesses
// Pattern 1: variable.member_name
Pattern dotPattern = Pattern.compile("\\b\\w+\\." + Pattern.quote(memberName) + "\\b");
// Pattern 2: variable->member_name
Pattern arrowPattern = Pattern.compile("\\b\\w+\\->" + Pattern.quote(memberName) + "\\b");
// Pattern 3: (*variable).member_name
Pattern derefPattern = Pattern.compile("\\(\\*\\w+\\)\\." + Pattern.quote(memberName) + "\\b");
// Search through all functions
FunctionIterator functionIterator = program.getFunctionManager().getFunctions(true);
while (functionIterator.hasNext()) {
Function function = functionIterator.next();
try {
// Decompile the function
DecompileResults results = decompiler.decompileFunction(function, 30, TaskMonitor.DUMMY);
if (results != null && results.decompileCompleted()) {
String decompiledCode = results.getDecompiledFunction().getC();
// Search for member access patterns in the decompiled code
refs.addAll(findMemberAccesses(decompiledCode, structName, memberName,
function.getName(), function.getEntryPoint(),
dotPattern, arrowPattern, derefPattern));
}
} catch (Exception e) {
// Continue with next function if decompilation fails
continue;
}
}
decompiler.dispose();
if (refs.isEmpty()) {
return "No uses found of " + structName + "." + memberName + " in decompiled code";
}
return paginateList(refs, offset, limit);
} catch (Exception e) {
return "Error finding struct member uses: " + e.getMessage();
}
}
/**
* Searches for member access patterns in decompiled C code.
*
* @param decompiledCode the decompiled C code to search
* @param structName the name of the structure
* @param memberName the name of the member
* @param functionName the name of the function being searched
* @param functionAddress the entry point address of the function
* @param dotPattern regex pattern for dot notation (var.member)
* @param arrowPattern regex pattern for arrow notation (var->member)
* @param derefPattern regex pattern for dereference notation ((*var).member)
* @return list of formatted reference strings
*/
private List<String> findMemberAccesses(String decompiledCode, String structName, String memberName,
String functionName, Address functionAddress, Pattern dotPattern, Pattern arrowPattern, Pattern derefPattern) {
List<String> accesses = new ArrayList<>();
if (decompiledCode == null || decompiledCode.isEmpty()) {
return accesses;
}
// Split code into lines for better context reporting
String[] lines = decompiledCode.split("\n");
for (int lineNum = 0; lineNum < lines.length; lineNum++) {
String line = lines[lineNum].trim();
// Skip empty lines and comments
if (line.isEmpty() || line.startsWith("//") || line.startsWith("/*")) {
continue;
}
// Check for dot notation (var.member)
Matcher dotMatcher = dotPattern.matcher(line);
while (dotMatcher.find()) {
accesses.add(String.format("Access to %s.%s in %s() at %s (line %d): %s",
structName, memberName, functionName, functionAddress, lineNum + 1, line));
}
// Check for arrow notation (var->member)
Matcher arrowMatcher = arrowPattern.matcher(line);
while (arrowMatcher.find()) {
accesses.add(String.format("Access to %s->%s in %s() at %s (line %d): %s",
structName, memberName, functionName, functionAddress, lineNum + 1, line));
}
// Check for dereference notation ((*var).member)
Matcher derefMatcher = derefPattern.matcher(line);
while (derefMatcher.find()) {
accesses.add(String.format("Access to (*%s).%s in %s() at %s (line %d): %s",
structName, memberName, functionName, functionAddress, lineNum + 1, line));
}
}
return accesses;
}
}