package com.lauriewired.handlers.set;
import com.lauriewired.handlers.Handler;
import com.sun.net.httpserver.HttpExchange;
import ghidra.framework.plugintool.PluginTool;
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.Program;
import javax.swing.SwingUtilities;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import static com.lauriewired.util.ParseUtils.*;
import ghidra.program.model.data.CategoryPath;
import static ghidra.program.util.GhidraProgramUtilities.getCurrentProgram;
/**
* Handler for undefining a specific member from a structure in Ghidra while preserving struct layout.
* This handler processes requests to clear a named member from a specified structure,
* making it behave like it was never defined. The struct size and placement of other components
* are preserved automatically by Ghidra's clearAtOffset method.
* This is useful for cleaning up incorrect member definitions during reverse engineering workflows.
*
* Behavior:
* - Clears the specified member from the structure definition at its offset
* - Preserves the overall struct size and placement of other components (no shifting)
* - Cleared areas will not appear in get_struct results, consistent with never-defined fields
* - Useful for cleaning up struct definitions while maintaining struct boundaries
*
* Expects a POST request with parameters:
* - struct_name: Name of the structure to modify (required)
* - member_name: Name of the member to undefine (required)
*/
public final class UndefineStructMember extends Handler {
/**
* Constructs a new UndefineStructMember handler.
*
* @param tool the PluginTool instance to use for program operations
*/
public UndefineStructMember(PluginTool tool) {
super(tool, "/undefine_struct_member");
}
/**
* Handles HTTP requests to undefine a member from a structure.
* Expects POST parameters: struct_name (required), member_name (required).
*
* @param exchange the HttpExchange object containing the request
* @throws IOException if an I/O error occurs
*/
@Override
public void handle(HttpExchange exchange) throws IOException {
Map<String, String> params = parsePostParams(exchange);
String structName = params.get("struct_name");
String memberName = params.get("member_name");
if (structName == null || memberName == null) {
sendResponse(exchange, "struct_name and member_name are required");
return;
}
sendResponse(exchange, undefineStructMember(structName, memberName));
}
/**
* Undefines a specific member from a structure by name, preserving struct layout.
* The member will be cleared from its offset without affecting other components.
*
* @param structName the name of the structure to modify
* @param memberName the name of the member to undefine
* @return a message indicating the result of the operation
*/
private String undefineStructMember(String structName, String memberName) {
Program program = getCurrentProgram(tool);
if (program == null)
return "No program loaded";
final AtomicReference<String> result = new AtomicReference<>();
try {
SwingUtilities.invokeAndWait(() -> {
int txId = program.startTransaction("Undefine Struct Member");
boolean success = false;
try {
DataTypeManager dtm = program.getDataTypeManager();
CategoryPath path = new CategoryPath("/");
DataType dt = dtm.getDataType(path, structName);
if (dt == null || !(dt instanceof Structure)) {
result.set("Error: Struct " + structName + " not found");
return;
}
Structure struct = (Structure) dt;
// Find the member by name
DataTypeComponent[] components = struct.getComponents();
int memberIndex = -1;
for (int i = 0; i < components.length; i++) {
if (memberName.equals(components[i].getFieldName())) {
memberIndex = i;
break;
}
}
if (memberIndex == -1) {
result.set("Error: Member '" + memberName + "' not found in struct " + structName);
return;
}
// Get member info before clearing for confirmation message
DataTypeComponent memberToUndefine = components[memberIndex];
int memberOffset = memberToUndefine.getOffset();
int memberSize = memberToUndefine.getLength();
String memberType = memberToUndefine.getDataType().getName();
// Clear the component at the specified offset without shifting other members
// This preserves struct size and placement of other components automatically
struct.clearAtOffset(memberOffset);
result.set("Successfully undefined member '" + memberName + "' (type: " + memberType +
", size: " + memberSize + " bytes, offset: " + memberOffset + ") in struct '" +
structName + "' - cleared component without affecting other members");
success = true;
} catch (Exception e) {
result.set("Error: Failed to undefine member in struct: " + e.getMessage());
} finally {
program.endTransaction(txId, success);
}
});
} catch (InterruptedException | InvocationTargetException e) {
return "Error: Failed to execute undefine struct member on Swing thread: " + e.getMessage();
}
return result.get();
}
}