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.pcodeCPort.address.Address;
import ghidra.program.model.listing.Function;
import re3lib.FunctionDatabase;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
@ -19,26 +20,26 @@ public class DumpCurrentFunctionN extends GhidraScript {
final int NumFunctions = 8;
// class Entry {
// Function function;
// Function function;
// }
// class QueueEntry {
// Function function;
// List<Function> callees;
// Function function;
// List<Function> callees;
// }
// HashSet<Address> visited = new HashSet<>();
// QueueEntry enter(Function function) {
// if (visited.contains(function.getEntryPoint()))
// return null;
// if (visited.contains(function.getEntryPoint()))
// return null;
// visited.add(function.getEntryPoint());
// visited.add(function.getEntryPoint());
// QueueEntry entry = new QueueEntry();
// entry.function = function;
// QueueEntry entry = new QueueEntry();
// entry.function = function;
// function.getCalledFunctions(monitor);
// function.getCalledFunctions(monitor);
// }
@Override
@ -46,62 +47,66 @@ public class DumpCurrentFunctionN extends GhidraScript {
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) {
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper);
GlobalDumper globalDumper = new GlobalDumper(this, functionDatabase);
globalDumper.loadGlobalManifest();
PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
FunctionDumper functionDumper = new FunctionDumper(this, functionDatabase, globalDumper);
List<Address> queue = new ArrayList<>();
PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
// List<Function> functionsToDump = new ArrayList<>();
// List<Function> functionsToDumpNew = new ArrayList<>();
// for (Function func : tracer.out) {
// if (FunctionDumper.isDumpedFix(func))
// continue;
List<Address> queue = new ArrayList<>();
// println("Dump: " + func.getName());
// functionsToDump.add(func);
// List<Function> functionsToDump = new ArrayList<>();
// List<Function> functionsToDumpNew = new ArrayList<>();
// for (Function func : tracer.out) {
// if (FunctionDumper.isDumpedFix(func))
// continue;
// if (!FunctionDumper.isDumpedAuto(func))
// functionsToDumpNew.add(func);
// }
// println("Dump: " + func.getName());
// functionsToDump.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;
// }
// if (!FunctionDumper.isDumpedAuto(func))
// functionsToDumpNew.add(func);
// }
// for (Function func : functionsToDump) {
// functionDumper.dump(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;
// }
// if (functionDumper.createdFile)
// RecompileConfig.INSTANCE.touchCMakeTimestamp();
// for (Function func : functionsToDump) {
// functionDumper.dump(func);
// }
// globalDumper.dumpGlobals();
// globalDumper.saveGlobalManifest();
// }
// if (functionDumper.createdFile)
// RecompileConfig.INSTANCE.touchCMakeTimestamp();
// // Dump types
// TypeDumper dumper = new TypeDumper(this);
// dumper.run();
// globalDumper.dumpGlobals();
// globalDumper.saveGlobalManifest();
// }
// // 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.program.model.listing.Function;
import re3lib.FunctionDatabase;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
@ -19,60 +20,62 @@ public class DumpCurrentFunctionRecursive extends GhidraScript {
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) {
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();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
List<Function> functionsToDump = new ArrayList<>();
List<Function> functionsToDumpNew = new ArrayList<>();
for (Function func : tracer.out) {
if (FunctionDumper.isDumpedFix(func))
continue;
List<Function> functionsToDump = new ArrayList<>();
List<Function> functionsToDumpNew = new ArrayList<>();
for (Function func : tracer.out) {
if (FunctionDumper.isDumpedFix(func))
continue;
println("Dump: " + func.getName());
functionsToDump.add(func);
println("Dump: " + func.getName());
functionsToDump.add(func);
if (!FunctionDumper.isDumpedAuto(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;
if (!FunctionDumper.isDumpedAuto(func))
functionsToDumpNew.add(func);
}
for (Function func : functionsToDump) {
functionDumper.dump(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) {
functionDumper.dump(func);
}
if (functionDumper.createdFile)
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}
if (functionDumper.createdFile)
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
// Dump types
TypeDumper dumper = new TypeDumper(this);
dumper.run();
}
// Dump types
TypeDumper dumper = new TypeDumper(this);
dumper.run();
}
}

View File

@ -15,8 +15,8 @@ public class Test extends GhidraScript {
// Example SQLite usage
try (FunctionDatabase db = new FunctionDatabase(this)) {
List<FunctionDatabase.Entry> entries = db.loadAllEntries();
for (FunctionDatabase.Entry entry : entries) {
List<FunctionDatabase.FunctionEntry> entries = db.loadAllEntries();
for (FunctionDatabase.FunctionEntry entry : entries) {
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;
public class FunctionDatabase implements AutoCloseable {
public enum Type {
public static enum Type {
Auto(0),
Fix(1),
Stub(2),
@ -46,13 +46,13 @@ public class FunctionDatabase implements AutoCloseable {
}
}
public class Entry {
public static class FunctionEntry {
public Address address;
public String name;
public File file;
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.name = name;
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 transient GhidraScript script;
private Connection connection;
@ -75,6 +89,13 @@ public class FunctionDatabase implements AutoCloseable {
private PreparedStatement loadAllFunctions;
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) {
this.script = script;
dbFile = RemanConfig.INSTANCE.databasePath;
@ -155,6 +176,18 @@ public class FunctionDatabase implements AutoCloseable {
"SELECT filepath, name, address, type FROM Functions");
loadAllImports = connection.prepareStatement(
"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 {
@ -176,17 +209,28 @@ public class FunctionDatabase implements AutoCloseable {
loadAllFunctions.close();
if (loadAllImports != null)
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();
List<Entry> entries = new ArrayList<>();
List<FunctionEntry> entries = new ArrayList<>();
try {
// Load from Functions table
try (ResultSet rs = loadAllFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) {
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 name = rs.getString("name");
String addressStr = rs.getString("address");
@ -212,7 +256,7 @@ public class FunctionDatabase implements AutoCloseable {
File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
Type type = Type.fromValue(typeValue);
return new Entry(address, name, file, type);
return new FunctionEntry(address, name, file, type);
}
return null;
}
@ -236,21 +280,31 @@ public class FunctionDatabase implements AutoCloseable {
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(createImports).executeUpdate();
connection.prepareStatement(createGlobals).executeUpdate();
}
// Helper method to find entries by name
public List<Entry> findEntriesByName(String name) throws Exception {
public List<FunctionEntry> findEntriesByName(String name) throws Exception {
ensureConnection();
List<Entry> results = new ArrayList<>();
List<FunctionEntry> results = new ArrayList<>();
try {
// Search Functions table
findByNameFunctions.setString(1, name);
try (ResultSet rs = findByNameFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
@ -261,7 +315,7 @@ public class FunctionDatabase implements AutoCloseable {
findByNameImports.setString(1, name);
try (ResultSet rs = findByNameImports.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
@ -276,9 +330,9 @@ public class FunctionDatabase implements AutoCloseable {
}
// Helper method to find entries by address
public List<Entry> findEntriesByAddress(Address address) throws Exception {
public List<FunctionEntry> findEntriesByAddress(Address address) throws Exception {
ensureConnection();
List<Entry> results = new ArrayList<>();
List<FunctionEntry> results = new ArrayList<>();
String addressStr = address.toString();
try {
@ -286,7 +340,7 @@ public class FunctionDatabase implements AutoCloseable {
findByAddressFunctions.setString(1, addressStr);
try (ResultSet rs = findByAddressFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
@ -297,7 +351,7 @@ public class FunctionDatabase implements AutoCloseable {
findByAddressImports.setString(1, addressStr);
try (ResultSet rs = findByAddressImports.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
FunctionEntry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
@ -312,7 +366,7 @@ public class FunctionDatabase implements AutoCloseable {
}
// 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();
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
addEntryAt(entry);
}
@ -369,12 +423,12 @@ public class FunctionDatabase implements AutoCloseable {
boolean madeAnyChanges = false;
// Load all entries from database
List<Entry> entries = loadAllEntries();
List<FunctionEntry> entries = loadAllEntries();
// Create a hash map to store symbol names
Map<Address, String> symbolNames = new HashMap<>();
Map<String, File> exportedFunctionNames = new HashMap<>();
for (Entry entry : entries) {
for (FunctionEntry entry : entries) {
Function function = script.getFunctionAt(entry.address);
if (function != null) {
boolean isAuto = entry.type == Type.Auto;
@ -398,9 +452,9 @@ public class FunctionDatabase implements AutoCloseable {
HashSet<Function> functionsToRegenerate = new HashSet<>();
Iterator<Entry> iterator = entries.iterator();
Iterator<FunctionEntry> iterator = entries.iterator();
while (iterator.hasNext()) {
Entry entry = iterator.next();
FunctionEntry entry = iterator.next();
Function function = script.getFunctionAt(entry.address);
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
public void close() throws Exception {
this.disconnect();

View File

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

View File

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