Upgrade function dumper for cc
This commit is contained in:
parent
ac92d40671
commit
f6d2ec6efb
|
@ -46,17 +46,76 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static enum CallingConvention {
|
||||||
|
Cdecl(0),
|
||||||
|
Stdcall(1),
|
||||||
|
Fastcall(2);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
CallingConvention(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CallingConvention fromValue(int value) {
|
||||||
|
for (CallingConvention conv : CallingConvention.values()) {
|
||||||
|
if (conv.value == value) {
|
||||||
|
return conv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown calling convention value: " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CallingConvention fromString(String convStr) {
|
||||||
|
if (convStr == null) {
|
||||||
|
return Cdecl; // Default
|
||||||
|
}
|
||||||
|
|
||||||
|
String lower = convStr.toLowerCase();
|
||||||
|
if (lower.contains("fastcall")) {
|
||||||
|
return Fastcall;
|
||||||
|
} else if (lower.contains("stdcall")) {
|
||||||
|
return Stdcall;
|
||||||
|
} else {
|
||||||
|
return Cdecl; // Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
switch (this) {
|
||||||
|
case Fastcall:
|
||||||
|
return "fastcall";
|
||||||
|
case Stdcall:
|
||||||
|
return "stdcall";
|
||||||
|
case Cdecl:
|
||||||
|
default:
|
||||||
|
return "cdecl";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class FunctionEntry {
|
public static class FunctionEntry {
|
||||||
public Address address;
|
public Address address;
|
||||||
public String name;
|
public String name;
|
||||||
public File file;
|
public File file;
|
||||||
public Type type;
|
public Type type;
|
||||||
|
public CallingConvention callingConvention;
|
||||||
|
|
||||||
public FunctionEntry(Address address, String name, File file, Type 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) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.callingConvention = callingConvention;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +143,7 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
private PreparedStatement findByAddressFunctions;
|
private PreparedStatement findByAddressFunctions;
|
||||||
private PreparedStatement findByAddressImports;
|
private PreparedStatement findByAddressImports;
|
||||||
private PreparedStatement insertOrReplaceFunctions;
|
private PreparedStatement insertOrReplaceFunctions;
|
||||||
|
private PreparedStatement insertOrReplaceImports;
|
||||||
private PreparedStatement deleteByFilepathFunctions;
|
private PreparedStatement deleteByFilepathFunctions;
|
||||||
private PreparedStatement deleteByFilepathImports;
|
private PreparedStatement deleteByFilepathImports;
|
||||||
private PreparedStatement loadAllFunctions;
|
private PreparedStatement loadAllFunctions;
|
||||||
|
@ -151,19 +211,21 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
private void prepareCachedStatements() throws SQLException {
|
private void prepareCachedStatements() throws SQLException {
|
||||||
// Find by name
|
// Find by name
|
||||||
findByNameFunctions = connection.prepareStatement(
|
findByNameFunctions = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Functions WHERE name = ?");
|
"SELECT filepath, name, address, type, calling_convention FROM Functions WHERE name = ?");
|
||||||
findByNameImports = connection.prepareStatement(
|
findByNameImports = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Imports WHERE name = ?");
|
"SELECT filepath, name, address, type, calling_convention FROM Imports WHERE name = ?");
|
||||||
|
|
||||||
// Find by address
|
// Find by address
|
||||||
findByAddressFunctions = connection.prepareStatement(
|
findByAddressFunctions = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Functions WHERE address = ?");
|
"SELECT filepath, name, address, type, calling_convention FROM Functions WHERE address = ?");
|
||||||
findByAddressImports = connection.prepareStatement(
|
findByAddressImports = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Imports WHERE address = ?");
|
"SELECT filepath, name, address, type, calling_convention FROM Imports WHERE address = ?");
|
||||||
|
|
||||||
// Insert or replace
|
// Insert or replace
|
||||||
insertOrReplaceFunctions = connection.prepareStatement(
|
insertOrReplaceFunctions = connection.prepareStatement(
|
||||||
"INSERT OR REPLACE INTO Functions (filepath, name, address, type) VALUES (?, ?, ?, ?)");
|
"INSERT OR REPLACE INTO Functions (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)");
|
||||||
|
insertOrReplaceImports = connection.prepareStatement(
|
||||||
|
"INSERT OR REPLACE INTO Imports (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
// Delete by filepath
|
// Delete by filepath
|
||||||
deleteByFilepathFunctions = connection.prepareStatement(
|
deleteByFilepathFunctions = connection.prepareStatement(
|
||||||
|
@ -173,9 +235,9 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
|
|
||||||
// Load all entries
|
// Load all entries
|
||||||
loadAllFunctions = connection.prepareStatement(
|
loadAllFunctions = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Functions");
|
"SELECT filepath, name, address, type, calling_convention FROM Functions");
|
||||||
loadAllImports = connection.prepareStatement(
|
loadAllImports = connection.prepareStatement(
|
||||||
"SELECT filepath, name, address, type FROM Imports");
|
"SELECT filepath, name, address, type, calling_convention FROM Imports");
|
||||||
|
|
||||||
// Global statements
|
// Global statements
|
||||||
findByNameGlobals = connection.prepareStatement(
|
findByNameGlobals = connection.prepareStatement(
|
||||||
|
@ -201,6 +263,8 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
findByAddressImports.close();
|
findByAddressImports.close();
|
||||||
if (insertOrReplaceFunctions != null)
|
if (insertOrReplaceFunctions != null)
|
||||||
insertOrReplaceFunctions.close();
|
insertOrReplaceFunctions.close();
|
||||||
|
if (insertOrReplaceImports != null)
|
||||||
|
insertOrReplaceImports.close();
|
||||||
if (deleteByFilepathFunctions != null)
|
if (deleteByFilepathFunctions != null)
|
||||||
deleteByFilepathFunctions.close();
|
deleteByFilepathFunctions.close();
|
||||||
if (deleteByFilepathImports != null)
|
if (deleteByFilepathImports != null)
|
||||||
|
@ -250,13 +314,15 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
String name = rs.getString("name");
|
String name = rs.getString("name");
|
||||||
String addressStr = rs.getString("address");
|
String addressStr = rs.getString("address");
|
||||||
int typeValue = rs.getInt("type");
|
int typeValue = rs.getInt("type");
|
||||||
|
int callingConventionValue = rs.getInt("calling_convention");
|
||||||
|
|
||||||
if (addressStr != null && !addressStr.isEmpty()) {
|
if (addressStr != null && !addressStr.isEmpty()) {
|
||||||
Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr);
|
Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr);
|
||||||
File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
|
File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
|
||||||
Type type = Type.fromValue(typeValue);
|
Type type = Type.fromValue(typeValue);
|
||||||
|
CallingConvention callingConvention = CallingConvention.fromValue(callingConventionValue);
|
||||||
|
|
||||||
return new FunctionEntry(address, name, file, type);
|
return new FunctionEntry(address, name, file, type, callingConvention);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +333,8 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
filepath TEXT,
|
filepath TEXT,
|
||||||
name TEXT,
|
name TEXT,
|
||||||
address TEXT,
|
address TEXT,
|
||||||
type INTEGER,
|
type INTEGER DEFAULT 0,
|
||||||
|
calling_convention INTEGER DEFAULT 0,
|
||||||
PRIMARY KEY (name, filepath)
|
PRIMARY KEY (name, filepath)
|
||||||
)""";
|
)""";
|
||||||
|
|
||||||
|
@ -276,7 +343,8 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
filepath TEXT,
|
filepath TEXT,
|
||||||
name TEXT,
|
name TEXT,
|
||||||
address TEXT,
|
address TEXT,
|
||||||
type INTEGER,
|
type INTEGER DEFAULT 0,
|
||||||
|
calling_convention INTEGER DEFAULT 0,
|
||||||
PRIMARY KEY (name, filepath)
|
PRIMARY KEY (name, filepath)
|
||||||
)""";
|
)""";
|
||||||
|
|
||||||
|
@ -377,15 +445,40 @@ public class FunctionDatabase implements AutoCloseable {
|
||||||
insertOrReplaceFunctions.setString(2, entry.name);
|
insertOrReplaceFunctions.setString(2, entry.name);
|
||||||
insertOrReplaceFunctions.setString(3, entry.address.toString());
|
insertOrReplaceFunctions.setString(3, entry.address.toString());
|
||||||
insertOrReplaceFunctions.setInt(4, entry.type.getValue());
|
insertOrReplaceFunctions.setInt(4, entry.type.getValue());
|
||||||
|
insertOrReplaceFunctions.setInt(5, entry.callingConvention.getValue());
|
||||||
insertOrReplaceFunctions.executeUpdate();
|
insertOrReplaceFunctions.executeUpdate();
|
||||||
|
|
||||||
script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath);
|
script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath
|
||||||
|
+ " (calling convention: " + entry.callingConvention + ")");
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
script.println("Error adding entry: " + e.getMessage());
|
script.println("Error adding entry: " + e.getMessage());
|
||||||
throw new Exception("Failed to add entry", e);
|
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.executeUpdate();
|
||||||
|
|
||||||
|
script.println("Added/updated import entry: " + entry.name + " at " + entry.address + " in " + relativePath
|
||||||
|
+ " (calling convention: " + entry.callingConvention + ")");
|
||||||
|
} 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
|
// Helper method to remove entry by file path
|
||||||
public void removeEntryAt(String filePath) throws Exception {
|
public void removeEntryAt(String filePath) throws Exception {
|
||||||
ensureConnection();
|
ensureConnection();
|
||||||
|
|
|
@ -131,6 +131,11 @@ public class FunctionDumper {
|
||||||
File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName);
|
File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName);
|
||||||
script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString());
|
script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString());
|
||||||
|
|
||||||
|
// Extract calling convention from Ghidra function
|
||||||
|
String callingConventionName = externalFunction.getCallingConventionName();
|
||||||
|
FunctionDatabase.CallingConvention callingConvention = FunctionDatabase.CallingConvention.fromString(callingConventionName);
|
||||||
|
script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention);
|
||||||
|
|
||||||
try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) {
|
try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) {
|
||||||
writer2.println("// AUTO-GENERATED FILE!!!!");
|
writer2.println("// AUTO-GENERATED FILE!!!!");
|
||||||
writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra");
|
writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra");
|
||||||
|
@ -141,14 +146,13 @@ public class FunctionDumper {
|
||||||
writer2.println("#include <gh_global.h>");
|
writer2.println("#include <gh_global.h>");
|
||||||
writer2.println();
|
writer2.println();
|
||||||
writer2.println("// " + externalFunction.getEntryPoint());
|
writer2.println("// " + externalFunction.getEntryPoint());
|
||||||
writer2.println("// " + externalFunction.getName());
|
writer2.println("// " + externalFunction.getName() + "(" + callingConvention + ")");
|
||||||
|
|
||||||
// Parse function signature to extract calling convention, return type, and
|
// Parse function signature to extract calling convention, return type, and
|
||||||
// parameters
|
// parameters
|
||||||
String signature = externalFunction.getSignature().getPrototypeString(false);
|
String signature = externalFunction.getSignature().getPrototypeString(false);
|
||||||
signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName);
|
signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName);
|
||||||
script.println("Santized Signature: " + signature);
|
script.println("Santized Signature: " + signature);
|
||||||
String callingConvention = externalFunction.getCallingConventionName();
|
|
||||||
String returnType = externalFunction.getReturnType().toString();
|
String returnType = externalFunction.getReturnType().toString();
|
||||||
|
|
||||||
// Generate function stub using appropriate forwarding function
|
// Generate function stub using appropriate forwarding function
|
||||||
|
@ -156,8 +160,10 @@ public class FunctionDumper {
|
||||||
|
|
||||||
// Determine which stub function to use based on calling convention
|
// Determine which stub function to use based on calling convention
|
||||||
String stubFunction;
|
String stubFunction;
|
||||||
if (callingConvention != null && callingConvention.equals("__stdcall")) {
|
if (callingConvention == FunctionDatabase.CallingConvention.Stdcall) {
|
||||||
stubFunction = "gh_stub_impl_stdcall";
|
stubFunction = "gh_stub_impl_stdcall";
|
||||||
|
} else if (callingConvention == FunctionDatabase.CallingConvention.Fastcall) {
|
||||||
|
stubFunction = "gh_stub_impl_fastcall";
|
||||||
} else {
|
} else {
|
||||||
// Default to cdecl for most cases
|
// Default to cdecl for most cases
|
||||||
stubFunction = "gh_stub_impl_cdecl";
|
stubFunction = "gh_stub_impl_cdecl";
|
||||||
|
@ -189,9 +195,9 @@ public class FunctionDumper {
|
||||||
createdFile = true;
|
createdFile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add stub function to database
|
// Add stub function to database with calling convention
|
||||||
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(),
|
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(),
|
||||||
externalFunction.getName(), f4, FunctionDatabase.Type.Stub);
|
externalFunction.getName(), f4, FunctionDatabase.Type.Stub, callingConvention);
|
||||||
functionDatabase.addEntryAt(newEntry);
|
functionDatabase.addEntryAt(newEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +210,11 @@ public class FunctionDumper {
|
||||||
List<FunctionDatabase.FunctionEntry> entries = functionDatabase.findEntriesByAddress(entrypoint);
|
List<FunctionDatabase.FunctionEntry> entries = functionDatabase.findEntriesByAddress(entrypoint);
|
||||||
FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto;
|
FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto;
|
||||||
|
|
||||||
|
// Extract calling convention from Ghidra function
|
||||||
|
String callingConventionName = function.getCallingConventionName();
|
||||||
|
FunctionDatabase.CallingConvention callingConvention = FunctionDatabase.CallingConvention.fromString(callingConventionName);
|
||||||
|
script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention);
|
||||||
|
|
||||||
// Handle forceFixType flag
|
// Handle forceFixType flag
|
||||||
if (forceFixType) {
|
if (forceFixType) {
|
||||||
targetType = FunctionDatabase.Type.Fix;
|
targetType = FunctionDatabase.Type.Fix;
|
||||||
|
@ -271,9 +282,9 @@ public class FunctionDumper {
|
||||||
File f0 = targetFilename;
|
File f0 = targetFilename;
|
||||||
script.println("Processing " + function.getName() + " => " + f0.toString());
|
script.println("Processing " + function.getName() + " => " + f0.toString());
|
||||||
|
|
||||||
// Update database
|
// Update database with calling convention
|
||||||
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0,
|
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0,
|
||||||
targetType);
|
targetType, callingConvention);
|
||||||
functionDatabase.addEntryAt(newEntry);
|
functionDatabase.addEntryAt(newEntry);
|
||||||
|
|
||||||
List<Function> externalFunctionCalls = new ArrayList<>();
|
List<Function> externalFunctionCalls = new ArrayList<>();
|
||||||
|
@ -305,8 +316,8 @@ public class FunctionDumper {
|
||||||
// Parse preliminary line tokens
|
// Parse preliminary line tokens
|
||||||
for (int i = 0; i < line.getNumTokens(); i++) {
|
for (int i = 0; i < line.getNumTokens(); i++) {
|
||||||
ClangToken token = line.getToken(i);
|
ClangToken token = line.getToken(i);
|
||||||
if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall")
|
// Remove some of the calling conventions, keep __stdcall/fastcall
|
||||||
|| token.getText().equals("__stdcall")) {
|
if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall")) {
|
||||||
// Remove function declaration
|
// Remove function declaration
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -410,9 +421,13 @@ public class FunctionDumper {
|
||||||
String proto = externalFunction.getSignature().getPrototypeString(false);
|
String proto = externalFunction.getSignature().getPrototypeString(false);
|
||||||
String name = externalFunction.getName();
|
String name = externalFunction.getName();
|
||||||
proto = proto.replace(name, Utils.sanitizeIdentifier(name));
|
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);
|
||||||
headers.add("" + proto
|
headers.add("" + proto
|
||||||
+ "; // " + externalFunction.getEntryPoint() + " // "
|
+ "; // " + externalFunction.getEntryPoint() + " // "
|
||||||
+ name);
|
+ name + " // " + conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String header : headers) {
|
for (String header : headers) {
|
||||||
|
|
Loading…
Reference in New Issue