diff --git a/java/ghidra/Test.java b/java/ghidra/Test.java index 5193d3ea..d7d0077c 100644 --- a/java/ghidra/Test.java +++ b/java/ghidra/Test.java @@ -17,7 +17,11 @@ public class Test extends GhidraScript { try (FunctionDatabase db = new FunctionDatabase(this)) { List entries = db.loadAllEntries(); for (FunctionDatabase.FunctionEntry entry : entries) { - println("entry.name: " + entry.name + " entry.address: " + entry.address + " entry.type: " + entry.type); + println("entry.name: " + entry.name + " entry.address: " + entry.address + " entry.type: " + entry.type + + " calling_convention: " + entry.callingConvention + " return_type: " + entry.returnType); + if (!entry.parameterNames.isEmpty()) { + println(" parameters: " + entry.parameterNames + " | types: " + entry.parameterTypes); + } } } } diff --git a/java/ghidra/re3lib/FunctionDatabase.java b/java/ghidra/re3lib/FunctionDatabase.java index c5d51e08..74abfe75 100644 --- a/java/ghidra/re3lib/FunctionDatabase.java +++ b/java/ghidra/re3lib/FunctionDatabase.java @@ -105,17 +105,51 @@ public class FunctionDatabase implements AutoCloseable { public File file; public Type type; public CallingConvention callingConvention; + public String parameterNames; // Semicolon-separated parameter names + public String parameterTypes; // Semicolon-separated parameter types + public String returnType; // Function return type - public FunctionEntry(Address address, String name, File file, Type type) { - this(address, name, file, type, CallingConvention.Cdecl); - } - - public FunctionEntry(Address address, String name, File file, Type type, CallingConvention callingConvention) { + public FunctionEntry(Address address, String name, File file, Type type, CallingConvention callingConvention, + String parameterNames, String parameterTypes, String returnType) { this.address = address; this.name = name; this.file = file; this.type = type; this.callingConvention = callingConvention; + this.parameterNames = parameterNames != null ? parameterNames : ""; + this.parameterTypes = parameterTypes != null ? parameterTypes : ""; + this.returnType = returnType != null ? returnType : "void"; + } + + // Helper methods to work with semicolon-separated parameter lists + public String[] getParameterNamesArray() { + if (parameterNames == null || parameterNames.trim().isEmpty()) { + return new String[0]; + } + return parameterNames.split(";"); + } + + public String[] getParameterTypesArray() { + if (parameterTypes == null || parameterTypes.trim().isEmpty()) { + return new String[0]; + } + return parameterTypes.split(";"); + } + + public void setParameterNamesArray(String[] names) { + if (names == null || names.length == 0) { + this.parameterNames = ""; + } else { + this.parameterNames = String.join(";", names); + } + } + + public void setParameterTypesArray(String[] types) { + if (types == null || types.length == 0) { + this.parameterTypes = ""; + } else { + this.parameterTypes = String.join(";", types); + } } } @@ -210,21 +244,21 @@ public class FunctionDatabase implements AutoCloseable { private void prepareCachedStatements() throws SQLException { // Find by name findByNameFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Functions WHERE name = ?"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Functions WHERE name = ?"); findByNameImports = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Imports WHERE name = ?"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Imports WHERE name = ?"); // Find by address findByAddressFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Functions WHERE address = ?"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Functions WHERE address = ?"); findByAddressImports = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Imports WHERE address = ?"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Imports WHERE address = ?"); // Insert or replace insertOrReplaceFunctions = connection.prepareStatement( - "INSERT OR REPLACE INTO Functions (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)"); + "INSERT OR REPLACE INTO Functions (filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); insertOrReplaceImports = connection.prepareStatement( - "INSERT OR REPLACE INTO Imports (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)"); + "INSERT OR REPLACE INTO Imports (filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); // Delete by filepath deleteByFilepathFunctions = connection.prepareStatement( @@ -234,9 +268,9 @@ public class FunctionDatabase implements AutoCloseable { // Load all entries loadAllFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Functions"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Functions"); loadAllImports = connection.prepareStatement( - "SELECT filepath, name, address, type, calling_convention FROM Imports"); + "SELECT filepath, name, address, type, calling_convention, parameter_names, parameter_types, return_type FROM Imports"); // Global statements findByNameGlobals = connection.prepareStatement( @@ -314,6 +348,9 @@ public class FunctionDatabase implements AutoCloseable { String addressStr = rs.getString("address"); int typeValue = rs.getInt("type"); int callingConventionValue = rs.getInt("calling_convention"); + String parameterNames = rs.getString("parameter_names"); + String parameterTypes = rs.getString("parameter_types"); + String returnType = rs.getString("return_type"); if (addressStr != null && !addressStr.isEmpty()) { Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr); @@ -321,7 +358,8 @@ public class FunctionDatabase implements AutoCloseable { Type type = Type.fromValue(typeValue); CallingConvention callingConvention = CallingConvention.fromValue(callingConventionValue); - return new FunctionEntry(address, name, file, type, callingConvention); + return new FunctionEntry(address, name, file, type, callingConvention, + parameterNames, parameterTypes, returnType); } return null; } @@ -334,6 +372,9 @@ public class FunctionDatabase implements AutoCloseable { address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, + parameter_names TEXT DEFAULT '', + parameter_types TEXT DEFAULT '', + return_type TEXT DEFAULT '', PRIMARY KEY (name, filepath) )"""; @@ -344,6 +385,9 @@ public class FunctionDatabase implements AutoCloseable { address TEXT, type INTEGER DEFAULT 0, calling_convention INTEGER DEFAULT 0, + parameter_names TEXT DEFAULT '', + parameter_types TEXT DEFAULT '', + return_type TEXT DEFAULT '', PRIMARY KEY (name, filepath) )"""; @@ -444,16 +488,45 @@ public class FunctionDatabase implements AutoCloseable { insertOrReplaceFunctions.setString(3, entry.address.toString()); insertOrReplaceFunctions.setInt(4, entry.type.getValue()); insertOrReplaceFunctions.setInt(5, entry.callingConvention.getValue()); + insertOrReplaceFunctions.setString(6, entry.parameterNames); + insertOrReplaceFunctions.setString(7, entry.parameterTypes); + insertOrReplaceFunctions.setString(8, entry.returnType); insertOrReplaceFunctions.executeUpdate(); script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath - + " (calling convention: " + entry.callingConvention + ")"); + + " (calling convention: " + entry.callingConvention + ", return type: " + entry.returnType + ")"); } catch (SQLException e) { script.println("Error adding entry: " + e.getMessage()); throw new Exception("Failed to add entry", e); } } + // Helper method to add import entry + public void addImportEntry(FunctionEntry entry) throws Exception { + ensureConnection(); + + String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath() + .relativize(entry.file.toPath()).toString().replace('\\', '/'); + + try { + insertOrReplaceImports.setString(1, relativePath); + insertOrReplaceImports.setString(2, entry.name); + insertOrReplaceImports.setString(3, entry.address.toString()); + insertOrReplaceImports.setInt(4, entry.type.getValue()); + insertOrReplaceImports.setInt(5, entry.callingConvention.getValue()); + insertOrReplaceImports.setString(6, entry.parameterNames); + insertOrReplaceImports.setString(7, entry.parameterTypes); + insertOrReplaceImports.setString(8, entry.returnType); + insertOrReplaceImports.executeUpdate(); + + script.println("Added/updated import entry: " + entry.name + " at " + entry.address + " in " + relativePath + + " (calling convention: " + entry.callingConvention + ", return type: " + entry.returnType + ")"); + } catch (SQLException e) { + script.println("Error adding import entry: " + e.getMessage()); + throw new Exception("Failed to add import entry", e); + } + } + // Helper method to remove entry by file path public void removeEntryAt(String filePath) throws Exception { ensureConnection(); @@ -689,7 +762,7 @@ public class FunctionDatabase implements AutoCloseable { } } - public void addGlobal(Address address, String name, String dataType) throws Exception { + public void addGlobal(Address address, String name) throws Exception { ensureConnection(); String filepath = RemanConfig.GLOBAL_H_FILE; // Default filepath for globals @@ -701,7 +774,7 @@ public class FunctionDatabase implements AutoCloseable { insertOrReplaceGlobals.setString(3, addressStr); insertOrReplaceGlobals.executeUpdate(); - script.println("Added/updated global: " + name + " at " + address + " with type " + dataType); + script.println("Added/updated global: " + name + " at " + address); } catch (SQLException e) { script.println("Error adding global: " + e.getMessage()); throw new Exception("Failed to add global", e); diff --git a/java/ghidra/re3lib/FunctionDumper.java b/java/ghidra/re3lib/FunctionDumper.java index fd835cf9..25a3b356 100644 --- a/java/ghidra/re3lib/FunctionDumper.java +++ b/java/ghidra/re3lib/FunctionDumper.java @@ -47,6 +47,44 @@ public class FunctionDumper { initFunctionBlacklist(); } + // Helper method to extract parameter names from Ghidra function + private String extractParameterNames(Function function) { + var parameters = function.getParameters(); + if (parameters.length == 0) { + return ""; + } + + StringBuilder names = new StringBuilder(); + for (int i = 0; i < parameters.length; i++) { + if (i > 0) names.append(";"); + String paramName = parameters[i].getName(); + names.append(paramName != null ? paramName : "param" + i); + } + return names.toString(); + } + + // Helper method to extract parameter types from Ghidra function + private String extractParameterTypes(Function function) { + var parameters = function.getParameters(); + if (parameters.length == 0) { + return ""; + } + + StringBuilder types = new StringBuilder(); + for (int i = 0; i < parameters.length; i++) { + if (i > 0) types.append(";"); + String paramType = parameters[i].getDataType().toString(); + types.append(paramType != null ? paramType : "void*"); + } + return types.toString(); + } + + // Helper method to extract return type from Ghidra function + private String extractReturnType(Function function) { + var returnType = function.getReturnType(); + return returnType != null ? returnType.toString() : "void"; + } + boolean isValidFunction(Function function) { if (functionAddrBlackList.contains(function.getEntryPoint())) return false; @@ -137,6 +175,13 @@ public class FunctionDumper { .fromString(callingConventionName); script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention); + // Extract parameter information and return type + String parameterNames = extractParameterNames(externalFunction); + String parameterTypes = extractParameterTypes(externalFunction); + String returnType = extractReturnType(externalFunction); + + script.println("Parameters: " + parameterNames + " | Types: " + parameterTypes + " | Return: " + returnType); + try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) { writer2.println("// AUTO-GENERATED FILE!!!!"); writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra"); @@ -147,14 +192,13 @@ public class FunctionDumper { writer2.println("#include "); writer2.println(); writer2.println("// " + externalFunction.getEntryPoint()); - writer2.println("// " + externalFunction.getName() + "(" + callingConvention + ")"); + writer2.println("// " + externalFunction.getName()); // Parse function signature to extract calling convention, return type, and // parameters String signature = externalFunction.getSignature().getPrototypeString(false); signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName); script.println("Santized Signature: " + signature); - String returnType = externalFunction.getReturnType().toString(); // Generate function stub using appropriate forwarding function writer2.println("extern \"C\" " + signature + " {"); @@ -196,9 +240,10 @@ public class FunctionDumper { createdFile = true; } - // Add stub function to database with calling convention + // Add stub function to database with calling convention and parameter information FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(), - externalFunction.getName(), f4, FunctionDatabase.Type.Stub, callingConvention); + externalFunction.getName(), f4, FunctionDatabase.Type.Stub, callingConvention, + parameterNames, parameterTypes, returnType); functionDatabase.addEntryAt(newEntry); } @@ -217,6 +262,13 @@ public class FunctionDumper { .fromString(callingConventionName); script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention); + // Extract parameter information and return type + String parameterNames = extractParameterNames(function); + String parameterTypes = extractParameterTypes(function); + String returnType = extractReturnType(function); + + script.println("Parameters: " + parameterNames + " | Types: " + parameterTypes + " | Return: " + returnType); + // Handle forceFixType flag if (forceFixType) { targetType = FunctionDatabase.Type.Fix; @@ -284,9 +336,9 @@ public class FunctionDumper { File f0 = targetFilename; script.println("Processing " + function.getName() + " => " + f0.toString()); - // Update database with calling convention + // Update database with calling convention and parameter information FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0, - targetType, callingConvention); + targetType, callingConvention, parameterNames, parameterTypes, returnType); functionDatabase.addEntryAt(newEntry); List externalFunctionCalls = new ArrayList<>(); @@ -429,13 +481,19 @@ public class FunctionDumper { String proto = externalFunction.getSignature().getPrototypeString(false); String name = externalFunction.getName(); proto = proto.replace(name, Utils.sanitizeIdentifier(name)); - + // Add calling convention information to the comment String callingConv = externalFunction.getCallingConventionName(); FunctionDatabase.CallingConvention conv = FunctionDatabase.CallingConvention.fromString(callingConv); + + // Add parameter information to the comment + String extParamNames = extractParameterNames(externalFunction); + String extParamTypes = extractParameterTypes(externalFunction); + String extReturnType = extractReturnType(externalFunction); + headers.add("" + proto + "; // " + externalFunction.getEntryPoint() + " // " - + name + " // " + conv); + + name + " // " + conv + " // Params: " + extParamNames + " | Types: " + extParamTypes + " | Return: " + extReturnType); } for (String header : headers) { @@ -443,6 +501,8 @@ public class FunctionDumper { } writer2.println(); writer2.print("// " + function.getEntryPoint()); + writer2.println(" // " + function.getName() + "(" + callingConvention + ")"); + writer2.println("// Parameters: " + parameterNames + " | Types: " + parameterTypes + " | Return: " + returnType); String codeString = codeWriter.toString(); for (HashMap.Entry entry : replacementMap.entrySet()) { String oldName = entry.getKey(); diff --git a/java/ghidra/re3lib/GlobalDumper.java b/java/ghidra/re3lib/GlobalDumper.java index 573a93e0..4d95d146 100644 --- a/java/ghidra/re3lib/GlobalDumper.java +++ b/java/ghidra/re3lib/GlobalDumper.java @@ -226,8 +226,7 @@ public class GlobalDumper { // Add all current globals to database for (GlobalRec global : globalAddrs.values()) { - String dataTypeName = global.type.getDisplayName(); - functionDatabase.addGlobal(global.address, global.name, dataTypeName); + functionDatabase.addGlobal(global.address, global.name); } }