WIP
This commit is contained in:
parent
16e5456079
commit
e19a123b94
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue