This commit is contained in:
Guus Waals 2025-05-29 18:03:37 +08:00
parent 16e5456079
commit e19a123b94
6 changed files with 439 additions and 236 deletions

View File

@ -9,6 +9,7 @@ import java.util.List;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.pcodeCPort.address.Address; import ghidra.pcodeCPort.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import re3lib.FunctionDatabase;
import re3lib.FunctionDumper; import re3lib.FunctionDumper;
import re3lib.GlobalDumper; import re3lib.GlobalDumper;
import re3lib.PCallTracer; import re3lib.PCallTracer;
@ -19,26 +20,26 @@ public class DumpCurrentFunctionN extends GhidraScript {
final int NumFunctions = 8; final int NumFunctions = 8;
// class Entry { // class Entry {
// Function function; // Function function;
// } // }
// class QueueEntry { // class QueueEntry {
// Function function; // Function function;
// List<Function> callees; // List<Function> callees;
// } // }
// HashSet<Address> visited = new HashSet<>(); // HashSet<Address> visited = new HashSet<>();
// QueueEntry enter(Function function) { // QueueEntry enter(Function function) {
// if (visited.contains(function.getEntryPoint())) // if (visited.contains(function.getEntryPoint()))
// return null; // return null;
// visited.add(function.getEntryPoint()); // visited.add(function.getEntryPoint());
// QueueEntry entry = new QueueEntry(); // QueueEntry entry = new QueueEntry();
// entry.function = function; // entry.function = function;
// function.getCalledFunctions(monitor);
// function.getCalledFunctions(monitor);
// } // }
@Override @Override
@ -46,62 +47,66 @@ public class DumpCurrentFunctionN extends GhidraScript {
RemanConfig.INSTANCE = new RemanConfig(this); RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories(); RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this); try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) {
globalDumper.loadGlobalManifest();
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper); GlobalDumper globalDumper = new GlobalDumper(this, functionDatabase);
globalDumper.loadGlobalManifest();
PCallTracer tracer = new PCallTracer(); FunctionDumper functionDumper = new FunctionDumper(this, functionDatabase, globalDumper);
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
List<Address> queue = new ArrayList<>(); PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
// List<Function> functionsToDump = new ArrayList<>(); List<Address> queue = new ArrayList<>();
// List<Function> functionsToDumpNew = new ArrayList<>();
// for (Function func : tracer.out) {
// if (FunctionDumper.isDumpedFix(func))
// continue;
// println("Dump: " + func.getName()); // List<Function> functionsToDump = new ArrayList<>();
// functionsToDump.add(func); // List<Function> functionsToDumpNew = new ArrayList<>();
// for (Function func : tracer.out) {
// if (FunctionDumper.isDumpedFix(func))
// continue;
// if (!FunctionDumper.isDumpedAuto(func)) // println("Dump: " + func.getName());
// functionsToDumpNew.add(func); // functionsToDump.add(func);
// }
// if (!functionsToDump.isEmpty()) { // if (!FunctionDumper.isDumpedAuto(func))
// String newOpt = "Only new (" + functionsToDumpNew.size() + ")"; // functionsToDumpNew.add(func);
// String okOpt = "Yes (" + functionsToDump.size() + ")"; // }
// String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions ("
// + functionsToDumpNew.size() + " new), continue?",
// new ArrayList<String>() {
// {
// add(okOpt);
// add(newOpt);
// add("No");
// }
// }, okOpt);
// if (choice == okOpt) {
// } else if (choice == newOpt) {
// functionsToDump = functionsToDumpNew;
// } else {
// return;
// }
// for (Function func : functionsToDump) { // if (!functionsToDump.isEmpty()) {
// functionDumper.dump(func); // String newOpt = "Only new (" + functionsToDumpNew.size() + ")";
// } // String okOpt = "Yes (" + functionsToDump.size() + ")";
// String choice = askChoice("Confirmation", "About to generate " +
// functionsToDump.size() + " functions ("
// + functionsToDumpNew.size() + " new), continue?",
// new ArrayList<String>() {
// {
// add(okOpt);
// add(newOpt);
// add("No");
// }
// }, okOpt);
// if (choice == okOpt) {
// } else if (choice == newOpt) {
// functionsToDump = functionsToDumpNew;
// } else {
// return;
// }
// if (functionDumper.createdFile) // for (Function func : functionsToDump) {
// RecompileConfig.INSTANCE.touchCMakeTimestamp(); // functionDumper.dump(func);
// }
// globalDumper.dumpGlobals(); // if (functionDumper.createdFile)
// globalDumper.saveGlobalManifest(); // RecompileConfig.INSTANCE.touchCMakeTimestamp();
// }
// // Dump types // globalDumper.dumpGlobals();
// TypeDumper dumper = new TypeDumper(this); // globalDumper.saveGlobalManifest();
// dumper.run(); // }
// // Dump types
// TypeDumper dumper = new TypeDumper(this);
// dumper.run();
}
} }
} }

View File

@ -7,6 +7,7 @@ import java.util.List;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import re3lib.FunctionDatabase;
import re3lib.FunctionDumper; import re3lib.FunctionDumper;
import re3lib.GlobalDumper; import re3lib.GlobalDumper;
import re3lib.PCallTracer; import re3lib.PCallTracer;
@ -19,60 +20,62 @@ public class DumpCurrentFunctionRecursive extends GhidraScript {
RemanConfig.INSTANCE = new RemanConfig(this); RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories(); RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this); try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) {
globalDumper.loadGlobalManifest(); GlobalDumper globalDumper = new GlobalDumper(this, functionDatabase);
globalDumper.loadGlobalManifest();
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper); FunctionDumper functionDumper = new FunctionDumper(this, functionDatabase, globalDumper);
PCallTracer tracer = new PCallTracer(); PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList); tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress)); tracer.traceCalls(getFunctionContaining(currentAddress));
List<Function> functionsToDump = new ArrayList<>(); List<Function> functionsToDump = new ArrayList<>();
List<Function> functionsToDumpNew = new ArrayList<>(); List<Function> functionsToDumpNew = new ArrayList<>();
for (Function func : tracer.out) { for (Function func : tracer.out) {
if (FunctionDumper.isDumpedFix(func)) if (FunctionDumper.isDumpedFix(func))
continue; continue;
println("Dump: " + func.getName()); println("Dump: " + func.getName());
functionsToDump.add(func); functionsToDump.add(func);
if (!FunctionDumper.isDumpedAuto(func)) if (!FunctionDumper.isDumpedAuto(func))
functionsToDumpNew.add(func); functionsToDumpNew.add(func);
}
if (!functionsToDump.isEmpty()) {
String newOpt = "Only new (" + functionsToDumpNew.size() + ")";
String okOpt = "Yes (" + functionsToDump.size() + ")";
String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions ("
+ functionsToDumpNew.size() + " new), continue?",
new ArrayList<String>() {
{
add(okOpt);
add(newOpt);
add("No");
}
}, okOpt);
if (choice == okOpt) {
} else if (choice == newOpt) {
functionsToDump = functionsToDumpNew;
} else {
return;
} }
for (Function func : functionsToDump) { if (!functionsToDump.isEmpty()) {
functionDumper.dump(func); String newOpt = "Only new (" + functionsToDumpNew.size() + ")";
String okOpt = "Yes (" + functionsToDump.size() + ")";
String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions ("
+ functionsToDumpNew.size() + " new), continue?",
new ArrayList<String>() {
{
add(okOpt);
add(newOpt);
add("No");
}
}, okOpt);
if (choice == okOpt) {
} else if (choice == newOpt) {
functionsToDump = functionsToDumpNew;
} else {
return;
}
for (Function func : functionsToDump) {
functionDumper.dump(func);
}
if (functionDumper.createdFile)
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
} }
if (functionDumper.createdFile) // Dump types
RemanConfig.INSTANCE.touchCMakeTimestamp(); TypeDumper dumper = new TypeDumper(this);
dumper.run();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
} }
// Dump types
TypeDumper dumper = new TypeDumper(this);
dumper.run();
} }
} }

View File

@ -15,8 +15,8 @@ public class Test extends GhidraScript {
// Example SQLite usage // Example SQLite usage
try (FunctionDatabase db = new FunctionDatabase(this)) { try (FunctionDatabase db = new FunctionDatabase(this)) {
List<FunctionDatabase.Entry> entries = db.loadAllEntries(); List<FunctionDatabase.FunctionEntry> entries = db.loadAllEntries();
for (FunctionDatabase.Entry entry : entries) { 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);
} }
} }

View File

@ -20,7 +20,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
public class FunctionDatabase implements AutoCloseable { public class FunctionDatabase implements AutoCloseable {
public enum Type { public static enum Type {
Auto(0), Auto(0),
Fix(1), Fix(1),
Stub(2), Stub(2),
@ -46,13 +46,13 @@ public class FunctionDatabase implements AutoCloseable {
} }
} }
public class Entry { 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 Entry(Address address, String name, File file, Type type) { public FunctionEntry(Address address, String name, File file, Type type) {
this.address = address; this.address = address;
this.name = name; this.name = name;
this.file = file; this.file = file;
@ -60,6 +60,20 @@ public class FunctionDatabase implements AutoCloseable {
} }
} }
public static class GlobalEntry {
public Address address;
public String name;
public String dataType;
public File file;
public GlobalEntry(Address address, String name, String dataType, File file) {
this.address = address;
this.name = name;
this.dataType = dataType;
this.file = file;
}
}
private File dbFile; private File dbFile;
private transient GhidraScript script; private transient GhidraScript script;
private Connection connection; private Connection connection;
@ -75,6 +89,13 @@ public class FunctionDatabase implements AutoCloseable {
private PreparedStatement loadAllFunctions; private PreparedStatement loadAllFunctions;
private PreparedStatement loadAllImports; private PreparedStatement loadAllImports;
// Add these prepared statements after the existing ones
private PreparedStatement findByNameGlobals;
private PreparedStatement findByAddressGlobals;
private PreparedStatement insertOrReplaceGlobals;
private PreparedStatement deleteByFilepathGlobals;
private PreparedStatement loadAllGlobals;
public FunctionDatabase(GhidraScript script) { public FunctionDatabase(GhidraScript script) {
this.script = script; this.script = script;
dbFile = RemanConfig.INSTANCE.databasePath; dbFile = RemanConfig.INSTANCE.databasePath;
@ -155,6 +176,18 @@ public class FunctionDatabase implements AutoCloseable {
"SELECT filepath, name, address, type FROM Functions"); "SELECT filepath, name, address, type FROM Functions");
loadAllImports = connection.prepareStatement( loadAllImports = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Imports"); "SELECT filepath, name, address, type FROM Imports");
// Global statements
findByNameGlobals = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Globals WHERE name = ?");
findByAddressGlobals = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Globals WHERE address = ?");
insertOrReplaceGlobals = connection.prepareStatement(
"INSERT OR REPLACE INTO Globals (filepath, name, address, type) VALUES (?, ?, ?, ?)");
deleteByFilepathGlobals = connection.prepareStatement(
"DELETE FROM Globals WHERE filepath = ?");
loadAllGlobals = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Globals");
} }
private void closePreparedStatements() throws SQLException { private void closePreparedStatements() throws SQLException {
@ -176,17 +209,28 @@ public class FunctionDatabase implements AutoCloseable {
loadAllFunctions.close(); loadAllFunctions.close();
if (loadAllImports != null) if (loadAllImports != null)
loadAllImports.close(); loadAllImports.close();
if (findByNameGlobals != null)
findByNameGlobals.close();
if (findByAddressGlobals != null)
findByAddressGlobals.close();
if (insertOrReplaceGlobals != null)
insertOrReplaceGlobals.close();
if (deleteByFilepathGlobals != null)
deleteByFilepathGlobals.close();
if (loadAllGlobals != null)
loadAllGlobals.close();
} }
public List<Entry> loadAllEntries() throws Exception { public List<FunctionEntry> loadAllEntries() throws Exception {
ensureConnection(); ensureConnection();
List<Entry> entries = new ArrayList<>(); List<FunctionEntry> entries = new ArrayList<>();
try { try {
// Load from Functions table // Load from Functions table
try (ResultSet rs = loadAllFunctions.executeQuery()) { try (ResultSet rs = loadAllFunctions.executeQuery()) {
while (rs.next()) { while (rs.next()) {
Entry entry = createEntryFromResultSet(rs); FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) { if (entry != null) {
entries.add(entry); entries.add(entry);
} }
@ -201,7 +245,7 @@ public class FunctionDatabase implements AutoCloseable {
} }
} }
private Entry createEntryFromResultSet(ResultSet rs) throws SQLException { private FunctionEntry createEntryFromResultSet(ResultSet rs) throws SQLException {
String filepath = rs.getString("filepath"); String filepath = rs.getString("filepath");
String name = rs.getString("name"); String name = rs.getString("name");
String addressStr = rs.getString("address"); String addressStr = rs.getString("address");
@ -212,7 +256,7 @@ public class FunctionDatabase implements AutoCloseable {
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);
return new Entry(address, name, file, type); return new FunctionEntry(address, name, file, type);
} }
return null; return null;
} }
@ -236,21 +280,31 @@ public class FunctionDatabase implements AutoCloseable {
PRIMARY KEY (name, filepath) PRIMARY KEY (name, filepath)
)"""; )""";
String createGlobals = """
CREATE TABLE IF NOT EXISTS Globals (
filepath TEXT,
name TEXT,
address TEXT,
type TEXT,
PRIMARY KEY (name, filepath)
)""";
connection.prepareStatement(createFunctions).executeUpdate(); connection.prepareStatement(createFunctions).executeUpdate();
connection.prepareStatement(createImports).executeUpdate(); connection.prepareStatement(createImports).executeUpdate();
connection.prepareStatement(createGlobals).executeUpdate();
} }
// Helper method to find entries by name // Helper method to find entries by name
public List<Entry> findEntriesByName(String name) throws Exception { public List<FunctionEntry> findEntriesByName(String name) throws Exception {
ensureConnection(); ensureConnection();
List<Entry> results = new ArrayList<>(); List<FunctionEntry> results = new ArrayList<>();
try { try {
// Search Functions table // Search Functions table
findByNameFunctions.setString(1, name); findByNameFunctions.setString(1, name);
try (ResultSet rs = findByNameFunctions.executeQuery()) { try (ResultSet rs = findByNameFunctions.executeQuery()) {
while (rs.next()) { while (rs.next()) {
Entry entry = createEntryFromResultSet(rs); FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) { if (entry != null) {
results.add(entry); results.add(entry);
} }
@ -261,7 +315,7 @@ public class FunctionDatabase implements AutoCloseable {
findByNameImports.setString(1, name); findByNameImports.setString(1, name);
try (ResultSet rs = findByNameImports.executeQuery()) { try (ResultSet rs = findByNameImports.executeQuery()) {
while (rs.next()) { while (rs.next()) {
Entry entry = createEntryFromResultSet(rs); FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) { if (entry != null) {
results.add(entry); results.add(entry);
} }
@ -276,9 +330,9 @@ public class FunctionDatabase implements AutoCloseable {
} }
// Helper method to find entries by address // Helper method to find entries by address
public List<Entry> findEntriesByAddress(Address address) throws Exception { public List<FunctionEntry> findEntriesByAddress(Address address) throws Exception {
ensureConnection(); ensureConnection();
List<Entry> results = new ArrayList<>(); List<FunctionEntry> results = new ArrayList<>();
String addressStr = address.toString(); String addressStr = address.toString();
try { try {
@ -286,7 +340,7 @@ public class FunctionDatabase implements AutoCloseable {
findByAddressFunctions.setString(1, addressStr); findByAddressFunctions.setString(1, addressStr);
try (ResultSet rs = findByAddressFunctions.executeQuery()) { try (ResultSet rs = findByAddressFunctions.executeQuery()) {
while (rs.next()) { while (rs.next()) {
Entry entry = createEntryFromResultSet(rs); FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) { if (entry != null) {
results.add(entry); results.add(entry);
} }
@ -297,7 +351,7 @@ public class FunctionDatabase implements AutoCloseable {
findByAddressImports.setString(1, addressStr); findByAddressImports.setString(1, addressStr);
try (ResultSet rs = findByAddressImports.executeQuery()) { try (ResultSet rs = findByAddressImports.executeQuery()) {
while (rs.next()) { while (rs.next()) {
Entry entry = createEntryFromResultSet(rs); FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) { if (entry != null) {
results.add(entry); results.add(entry);
} }
@ -312,7 +366,7 @@ public class FunctionDatabase implements AutoCloseable {
} }
// Helper method to add/update entry (insert or replace based on filename) // Helper method to add/update entry (insert or replace based on filename)
public void addEntryAt(Entry entry) throws Exception { public void addEntryAt(FunctionEntry entry) throws Exception {
ensureConnection(); ensureConnection();
String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath() String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath()
@ -353,7 +407,7 @@ public class FunctionDatabase implements AutoCloseable {
} }
} }
public void add(Entry entry) throws Exception { public void add(FunctionEntry entry) throws Exception {
// Add entry directly to database // Add entry directly to database
addEntryAt(entry); addEntryAt(entry);
} }
@ -369,12 +423,12 @@ public class FunctionDatabase implements AutoCloseable {
boolean madeAnyChanges = false; boolean madeAnyChanges = false;
// Load all entries from database // Load all entries from database
List<Entry> entries = loadAllEntries(); List<FunctionEntry> entries = loadAllEntries();
// Create a hash map to store symbol names // Create a hash map to store symbol names
Map<Address, String> symbolNames = new HashMap<>(); Map<Address, String> symbolNames = new HashMap<>();
Map<String, File> exportedFunctionNames = new HashMap<>(); Map<String, File> exportedFunctionNames = new HashMap<>();
for (Entry entry : entries) { for (FunctionEntry entry : entries) {
Function function = script.getFunctionAt(entry.address); Function function = script.getFunctionAt(entry.address);
if (function != null) { if (function != null) {
boolean isAuto = entry.type == Type.Auto; boolean isAuto = entry.type == Type.Auto;
@ -398,9 +452,9 @@ public class FunctionDatabase implements AutoCloseable {
HashSet<Function> functionsToRegenerate = new HashSet<>(); HashSet<Function> functionsToRegenerate = new HashSet<>();
Iterator<Entry> iterator = entries.iterator(); Iterator<FunctionEntry> iterator = entries.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Entry entry = iterator.next(); FunctionEntry entry = iterator.next();
Function function = script.getFunctionAt(entry.address); Function function = script.getFunctionAt(entry.address);
boolean pendingDelete = false; boolean pendingDelete = false;
@ -485,6 +539,142 @@ public class FunctionDatabase implements AutoCloseable {
} }
} }
// Global-specific methods
public List<GlobalEntry> loadAllGlobals() throws Exception {
ensureConnection();
List<GlobalEntry> globals = new ArrayList<>();
try {
try (ResultSet rs = loadAllGlobals.executeQuery()) {
while (rs.next()) {
GlobalEntry entry = createGlobalEntryFromResultSet(rs);
if (entry != null) {
globals.add(entry);
}
}
}
script.println("Loaded " + globals.size() + " global entries from database");
return globals;
} catch (SQLException e) {
script.println("Error loading globals: " + e.getMessage());
throw new Exception("Failed to load globals", e);
}
}
private GlobalEntry createGlobalEntryFromResultSet(ResultSet rs) throws SQLException {
String filepath = rs.getString("filepath");
String name = rs.getString("name");
String addressStr = rs.getString("address");
String typeStr = rs.getString("type");
if (addressStr != null && !addressStr.isEmpty()) {
Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr);
File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
return new GlobalEntry(address, name, typeStr, file);
}
return null;
}
public List<GlobalEntry> findGlobalsByName(String name) throws Exception {
ensureConnection();
List<GlobalEntry> results = new ArrayList<>();
try {
findByNameGlobals.setString(1, name);
try (ResultSet rs = findByNameGlobals.executeQuery()) {
while (rs.next()) {
GlobalEntry entry = createGlobalEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
return results;
} catch (SQLException e) {
script.println("Error finding globals by name: " + e.getMessage());
throw new Exception("Failed to find globals by name", e);
}
}
public List<GlobalEntry> findGlobalsByAddress(Address address) throws Exception {
ensureConnection();
List<GlobalEntry> results = new ArrayList<>();
String addressStr = address.toString();
try {
findByAddressGlobals.setString(1, addressStr);
try (ResultSet rs = findByAddressGlobals.executeQuery()) {
while (rs.next()) {
GlobalEntry entry = createGlobalEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
return results;
} catch (SQLException e) {
script.println("Error finding globals by address: " + e.getMessage());
throw new Exception("Failed to find globals by address", e);
}
}
public void addGlobal(Address address, String name, String dataType) throws Exception {
ensureConnection();
String filepath = "globals.h"; // Default filepath for globals
String addressStr = address.toString();
try {
insertOrReplaceGlobals.setString(1, filepath);
insertOrReplaceGlobals.setString(2, name);
insertOrReplaceGlobals.setString(3, addressStr);
insertOrReplaceGlobals.setString(4, dataType);
insertOrReplaceGlobals.executeUpdate();
script.println("Added/updated global: " + name + " at " + address + " with type " + dataType);
} catch (SQLException e) {
script.println("Error adding global: " + e.getMessage());
throw new Exception("Failed to add global", e);
}
}
public void removeGlobalsByFilepath(String filePath) throws Exception {
ensureConnection();
String relativePath;
// Check if filePath is already a relative path or just a filename
File inputFile = new File(filePath);
if (inputFile.isAbsolute()) {
// Convert absolute path to relative
try {
relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath()
.relativize(inputFile.toPath()).toString().replace('\\', '/');
} catch (IllegalArgumentException e) {
// Fallback if paths can't be relativized (different drives, etc.)
script.println("Warning: Could not relativize path: " + filePath + ", using as-is");
relativePath = filePath.replace('\\', '/');
}
} else {
// Already relative or just a filename, use as-is
relativePath = filePath.replace('\\', '/');
}
try {
deleteByFilepathGlobals.setString(1, relativePath);
int deletedCount = deleteByFilepathGlobals.executeUpdate();
script.println("Removed " + deletedCount + " global entries for file: " + relativePath);
} catch (SQLException e) {
script.println("Error removing global entries: " + e.getMessage());
throw new Exception("Failed to remove global entries", e);
}
}
@Override @Override
public void close() throws Exception { public void close() throws Exception {
this.disconnect(); this.disconnect();

View File

@ -5,7 +5,6 @@ import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -23,7 +22,6 @@ import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighSymbol; import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import re3lib.GlobalDumper.GlobalRec;
public class FunctionDumper { public class FunctionDumper {
GhidraScript script; GhidraScript script;
@ -125,30 +123,49 @@ public class FunctionDumper {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName()); String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx"; String fileName = sanitizedFunctionName + ".cxx";
Address entrypoint = function.getEntryPoint();
// Remove the stub file, since we now use the decompiled file List<FunctionDatabase.FunctionEntry> entries = functionDatabase.findEntriesByAddress(entrypoint);
File stubFile = new File(RemanConfig.INSTANCE.dirDecompStub, fileName); FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto;
if (stubFile.exists()) { for (FunctionDatabase.FunctionEntry entry : entries) {
script.println("Removing function stub " + stubFile); script.println("Found existing decompiled entry at " + entry.file + " - " + entry.name);
stubFile.delete(); if (targetType != FunctionDatabase.Type.Ref) {
createdFile = true; if (entry.type == FunctionDatabase.Type.Fix) {
} targetType = FunctionDatabase.Type.Ref;
}
File f0 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName); }
if (f0.exists()) { if (entry.type == FunctionDatabase.Type.Stub) {
script.println("Func " + function.getName() + " skipped (gh_fix)"); // Remove the stub file, since we now use the decompiled file
f0 = new File(RemanConfig.INSTANCE.dirDecompRef, fileName); File stubFile = entry.file;
} else { if (stubFile.exists()) {
f0 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName); script.println("Removing function stub " + stubFile);
if (f0.exists()) { stubFile.delete();
f0.delete(); createdFile = true;
} else { functionDatabase.removeEntryAt(entry.file.toString());
createdFile = true; }
} }
} }
File targetFilename = null;
if (targetType == FunctionDatabase.Type.Ref) {
targetFilename = new File(RemanConfig.INSTANCE.dirDecompRef, fileName);
} else {
targetFilename = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName);
}
if (targetFilename.exists()) {
targetFilename.delete();
script.println("Overwriting existing file " + targetFilename);
} else {
createdFile = true;
}
File f0 = targetFilename;
script.println("Processing " + function.getName() + " => " + f0.toString()); script.println("Processing " + function.getName() + " => " + f0.toString());
// Update database
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0,
targetType);
functionDatabase.addEntryAt(newEntry);
List<Function> externalFunctionCalls = new ArrayList<>(); List<Function> externalFunctionCalls = new ArrayList<>();
DecompileResults decompRes = RemanConfig.INSTANCE.decompCache.getOrInsert(function); DecompileResults decompRes = RemanConfig.INSTANCE.decompCache.getOrInsert(function);
@ -293,15 +310,20 @@ public class FunctionDumper {
// Possibly generate stubs for external functions // Possibly generate stubs for external functions
for (Function externalFunction : externalFunctionCalls) { for (Function externalFunction : externalFunctionCalls) {
String sanitizedExtFunctionName = Utils.sanitizeIdentifier(externalFunction.getName()); String sanitizedExtFunctionName = Utils.sanitizeIdentifier(externalFunction.getName());
fileName = sanitizedExtFunctionName + ".cxx";
File f2 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName);
File f3 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName);
if (f2.exists() || f3.exists()) {
// script.println("Skipping external function: " + externalFunction.getName() +
// " - " + externalFunction.getEntryPoint());
continue;
}
List<FunctionDatabase.FunctionEntry> entries1 = functionDatabase
.findEntriesByAddress(externalFunction.getEntryPoint());
boolean needStub = true;
for (FunctionDatabase.FunctionEntry entry : entries1) {
if (entry.type == FunctionDatabase.Type.Auto || entry.type == FunctionDatabase.Type.Fix) {
needStub = false;
break;
}
}
if (!needStub)
continue;
fileName = sanitizedExtFunctionName + ".cxx";
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());

View File

@ -47,91 +47,58 @@ public class GlobalDumper {
}; };
GhidraScript script; GhidraScript script;
File manifestFile;
FunctionDatabase functionDatabase; FunctionDatabase functionDatabase;
HashMap<Address, GlobalRec> globalAddrs = new HashMap<>(); HashMap<Address, GlobalRec> globalAddrs = new HashMap<>();
public GlobalDumper(GhidraScript script, FunctionDatabase functionDatabase) { public GlobalDumper(GhidraScript script, FunctionDatabase functionDatabase) {
this.script = script; this.script = script;
this.functionDatabase = functionDatabase; this.functionDatabase = functionDatabase;
manifestFile = new File(RemanConfig.INSTANCE.outputDir, "globals.txt");
} }
public void removeGlobalManifest() { public void removeGlobalManifest() {
if (manifestFile.exists()) { // Remove globals from database instead of file
manifestFile.delete(); try {
functionDatabase.removeGlobalsByFilepath("globals.h");
globalAddrs.clear();
} catch (Exception e) {
script.println("Error removing global manifest: " + e.getMessage());
} }
} }
public boolean loadGlobalManifest() throws Exception { public boolean loadGlobalManifest() throws Exception {
// Globals are stored in the format of globalAddrs.clear();
// <address> || <name> || <type>
// Load globals from database
if (!manifestFile.exists()) { List<FunctionDatabase.GlobalEntry> dbGlobals = functionDatabase.loadAllGlobals();
script.println("Global manifest file not found: " + manifestFile);
return false; // Get the dataTypeManagerService for parsing types
}
// Get the dataTypeManagerService
DataTypeManagerService dataTypeManagerService = (DataTypeManagerService) script.getState().getTool() DataTypeManagerService dataTypeManagerService = (DataTypeManagerService) script.getState().getTool()
.getService(DataTypeManagerService.class); .getService(DataTypeManagerService.class);
DataTypeManager dtm = script.getCurrentProgram().getDataTypeManager(); DataTypeManager dtm = script.getCurrentProgram().getDataTypeManager();
DataTypeParser dtp = new DataTypeParser(dataTypeManagerService, AllowedDataTypes.ALL); DataTypeParser dtp = new DataTypeParser(dataTypeManagerService, AllowedDataTypes.ALL);
try (BufferedReader reader = new BufferedReader(new FileReader(manifestFile))) {
String line; for (FunctionDatabase.GlobalEntry entry : dbGlobals) {
while ((line = reader.readLine()) != null) { // Note: The database stores type as string, need to reconstruct DataType
String[] parts = line.split("\\|\\|"); // For now, we'll parse it back from the type string stored in database
if (parts.length == 4) { // This is a limitation of moving from the manifest format
Address address = script.parseAddress(parts[0].trim()); DataType type = null;
String name = parts[1].trim();
String categoryPath = parts[2].trim(); // Try to get from existing data at address
String dataTypePath = parts[3].trim(); Data data = script.getDataAt(entry.address);
DataTypePath typePath = new DataTypePath(categoryPath, dataTypePath);
DataType type = null;
type = dtm.getDataType(typePath);
if (type == null) {
// script.println("Parsing type: " + dataTypePath);
type = dtp.parse(dataTypePath);
}
if (type == null) {
script.println("WARNING: Failed to find type: " + dataTypePath + " for global: " + name + " at " + address);
continue;
}
globalAddrs.put(address, new GlobalRec(address, name, type));
} else {
script.println("Invalid global manifest line: " + line);
}
}
}
script.println("Loaded " + globalAddrs.size() + " globals from " + manifestFile);
return true;
}
public void addGlobal(Address addr, HighSymbol sym) throws Exception {
if (addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockStart) < 0
|| addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockEnd) > 0) {
throw new Exception("Global address out of range: " + addr);
}
DataType dt = sym.getDataType();
// if(symb.get
if (sym.getDataType().getName() == "undefined") {
// script.println("UNDEFINED: " + addr + " - " + dt.getDisplayName() + " - " +
// dt.getClass().getName());
Data data = script.getDataAt(addr);
if (data != null) { if (data != null) {
dt = data.getDataType(); type = data.getDataType();
// script.println("DATA: " + addr + " - " + dt.getDisplayName());
} }
if (type == null) {
script.println("WARNING: Could not reconstruct type for global: " + entry.name + " at " + entry.address);
continue;
}
globalAddrs.put(entry.address, new GlobalRec(entry.address, entry.name, type));
} }
if (dt == null) {
script.println("WARNING: Missing type for global: " + sym.getName() + " at " + addr); script.println("Loaded " + globalAddrs.size() + " globals from database");
return; return !globalAddrs.isEmpty();
}
// script.println("Global: " + addr + " - " + sym.getName() + " - " +
// dt.getDisplayName());
globalAddrs.put(addr, new GlobalRec(addr, sym.getName(), dt));
} }
String escapeCString(String str) { String escapeCString(String str) {
@ -245,28 +212,43 @@ public class GlobalDumper {
} }
public void saveGlobalManifest() throws Exception { public void saveGlobalManifest() throws Exception {
File backupFile = new File(manifestFile.getParentFile(), manifestFile.getName() + ".bak"); // Save globals to database instead of file
if (backupFile.exists()) { script.println("Saving globals to database");
if (!backupFile.delete()) {
throw new Exception("Failed to delete backup file: " + backupFile + ", globals will not be saved!");
}
}
if (manifestFile.exists()) { // Clear existing globals for the default filepath
if (!manifestFile.renameTo(backupFile)) functionDatabase.removeGlobalsByFilepath("globals.h");
throw new Exception("Failed to rename manifest file: " + manifestFile + ", globals will not be saved!");
// Add all current globals to database
for (GlobalRec global : globalAddrs.values()) {
String dataTypeName = global.type.getDisplayName();
functionDatabase.addGlobal(global.address, global.name, dataTypeName);
}
}
public void addGlobal(Address addr, HighSymbol sym) throws Exception {
if (addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockStart) < 0
|| addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockEnd) > 0) {
throw new Exception("Global address out of range: " + addr);
} }
try (PrintWriter writer = new PrintWriter(manifestFile)) { DataType dt = sym.getDataType();
script.println("Saving global manifest to " + manifestFile); // if(symb.get
GlobalRec[] globals = globalAddrs.values().toArray(new GlobalRec[0]); if (sym.getDataType().getName() == "undefined") {
Arrays.sort(globals, (a, b) -> a.address.compareTo(b.address)); // script.println("UNDEFINED: " + addr + " - " + dt.getDisplayName() + " - " +
for (GlobalRec global : globals) { // dt.getClass().getName());
DataTypePath path = global.type.getDataTypePath(); Data data = script.getDataAt(addr);
writer.println(global.address.toString() + " || " + global.name + " || " + path.getCategoryPath() + " || " if (data != null) {
+ path.getDataTypeName()); dt = data.getDataType();
// script.println("DATA: " + addr + " - " + dt.getDisplayName());
} }
} }
if (dt == null) {
script.println("WARNING: Missing type for global: " + sym.getName() + " at " + addr);
return;
}
// script.println("Global: " + addr + " - " + sym.getName() + " - " +
// dt.getDisplayName());
globalAddrs.put(addr, new GlobalRec(addr, sym.getName(), dt));
} }
public void sanitizeGlobalSymbols() { public void sanitizeGlobalSymbols() {
@ -288,3 +270,4 @@ public class GlobalDumper {
} }
} }
} }