WIP Generate function stubs

This commit is contained in:
2024-09-23 23:43:42 +08:00
parent 3d9181f654
commit 2b945e4406
12 changed files with 192 additions and 155 deletions

View File

@@ -25,6 +25,9 @@ public class DumpCurrentFunction extends GhidraScript {
println("No function found at the current address.");
}
if (functionDumper.createdFile)
RecompileConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}

View File

@@ -1,103 +1,23 @@
// Script to sanitize global symbols in Ghidra
// @category _Reman3
// @menupath Reman3.Sanitize Global Symbols
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import re3lib.GlobalDumper;
import re3lib.RecompileConfig;
import re3lib.Utils;
public class SanitizeGlobalSymbols extends GhidraScript {
private static final boolean AUTO_RENAME_SYMBOLS = true;
HashSet<Address> functionAddrBlackList = new HashSet<>();
boolean shouldDecompileFunction(Function function) {
return !functionAddrBlackList.contains(function.getEntryPoint());
}
void sanitizeGlobalSymbolsPass(List<Function> functions) {
Hashtable<String, HighSymbol> globalSymbols = new Hashtable<>();
for (Function function : functions) {
println("Processing global symbols for " + function.getName());
DecompileResults decompRes = RecompileConfig.INSTANCE.decompCache.getOrInsert(function);
Iterator<HighSymbol> smyIt = decompRes.getHighFunction().getGlobalSymbolMap().getSymbols();
HighSymbol gsym = smyIt.next();
if (globalSymbols.containsKey(gsym.getName()))
continue;
println("GLOBAL: " + gsym.getName());
String sanitizedName = sanitizeFunctionName(gsym.getName());
if (!sanitizedName.equals(gsym.getName())) {
if (AUTO_RENAME_SYMBOLS) {
Symbol symbol = gsym.getSymbol();
VariableStorage storage = gsym.getStorage();
Address addr = storage.getMinAddress();
println("Renaming global symbol: " + gsym.getName() + " (" + addr
+ ") -> " + sanitizedName);
if (symbol != null) {
AddLabelCmd cmd = new AddLabelCmd(addr, sanitizedName, symbol.getParentNamespace(),
SourceType.USER_DEFINED);
if (cmd.applyTo(currentProgram)) {
println("Renamed global symbol: " + gsym.getName() + " -> " + sanitizedName);
} else {
println("Error renaming symbol: " + cmd.getStatusMsg());
}
} else {
println("Symbol is null: " + gsym.getName() + " - " + function.getName());
}
} else {
println("Invalid global symbol name: " + gsym.getName() + " - " + function.getName());
}
}
}
}
private static String sanitizeFunctionName(String name) {
return name.replaceAll("[^a-zA-Z0-9_]", "_");
}
@Override
public void run() throws Exception {
if (currentProgram == null) {
return;
}
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
if (!new File(RecompileConfig.INSTANCE.outputDir).exists()) {
throw new Exception("Output directory does not exist: " + RecompileConfig.INSTANCE.outputDir);
}
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
functionAddrBlackList = Utils.loadFunctionBlacklist(RecompileConfig.INSTANCE.functionBlacklistPath);
globalDumper.sanitizeGlobalSymbols();
List<Function> functions = new ArrayList<>();
Iterator<Function> functionsIt = currentProgram.getFunctionManager().getFunctions(true).iterator();
while (functionsIt.hasNext()) {
Function function = functionsIt.next();
if (!shouldDecompileFunction(function)) {
continue;
}
functions.add(function);
}
sanitizeGlobalSymbolsPass(functions);
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}
}

View File

@@ -30,6 +30,8 @@ public class FunctionDumper {
GlobalDumper globalDumper;
HashSet<Address> functionAddrBlackList = new HashSet<>();
public boolean createdFile = false;
static final Pattern fieldAccessRegex = Pattern.compile("^_([0-9]+)_([0-9]+)_$");
public FunctionDumper(GhidraScript script, GlobalDumper globalDumper) {
@@ -112,11 +114,15 @@ public class FunctionDumper {
f0 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
if (f0.exists()) {
f0.delete();
} else {
createdFile = true;
}
}
script.println("Processing " + function.getName() + " => " + f0.toString());
List<Function> externalFunctionCalls = new ArrayList<>();
DecompileResults decompRes = RecompileConfig.INSTANCE.decompCache.getOrInsert(function);
try (PrintWriter writer2 = new PrintWriter(f0, "UTF-8")) {
writer2.println("// AUTO-GENERATED FILE, MOVE TO 'gh_fix' FOLDER PREVENT OVERWRITING!!!!! ");
@@ -213,6 +219,7 @@ public class FunctionDumper {
headers.add("extern " + proto
+ "; // " + calledFunction.getEntryPoint() + " // "
+ calledFunction.getName());
externalFunctionCalls.add(calledFunction);
}
}
}
@@ -236,5 +243,39 @@ public class FunctionDumper {
writer2.print(codeWriter.toString());
writer2.println();
}
// Possibly generate stubs for external functions
for (Function externalFunction : externalFunctionCalls) {
File f2 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
File f3 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
if (f2.exists() || f3.exists()) {
continue;
}
File f4 = new File(RecompileConfig.INSTANCE.dirDecompStub, fileName);
script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString());
try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) {
writer2.println("// AUTO-GENERATED FILE!!!!");
writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra");
writer2.println("// with possible manualy fixes");
writer2.println();
writer2.println("#include <gh_auto_binder.h>");
writer2.println("#include \"../gh_global.h\"");
writer2.println("#include <stdexcept>");
writer2.println();
writer2.println("// " + externalFunction.getEntryPoint());
writer2.println("// " + externalFunction.getName());
writer2.println(externalFunction.getSignature().getPrototypeString(false) + " {");
writer2.println(" // TODO: Implement this function");
writer2
.println(" throw std::runtime_error(\"Function not implemented: " + externalFunction.getName() + "\");");
writer2.println("}");
}
if (!f4.exists()) {
createdFile = true;
}
}
}
}

View File

@@ -15,6 +15,7 @@ import java.util.Map;
import javax.xml.datatype.DatatypeFactory;
import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.program.model.address.Address;
@@ -27,6 +28,8 @@ import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Undefined;
import ghidra.program.model.listing.Data;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.data.DataTypeParser;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
@@ -181,8 +184,9 @@ public class GlobalDumper {
initBlk += "(" + dataType + ")&GH_MEM(0x" + addr + ")";
fullyDefinedType = true;
}
String linkagePrefix = "extern ";
if (fullyDefinedType) {
hwriter.println("extern " + dataType + " " + name + "; // " + addr);
hwriter.println(linkagePrefix + dataType + " " + name + "; // " + addr);
cwriter.println(dataType + " " + name + initBlk + "; // " + addr);
} else {
if (dt instanceof Array) {
@@ -191,14 +195,14 @@ public class GlobalDumper {
Array adt = (Array) dt;
DataType baseType = adt.getDataType();
hwriter.println(
"extern " + baseType.getDisplayName() + "(&" + name + ")[" + adt.getNumElements() + "]; // " + addr);
linkagePrefix + baseType.getDisplayName() + "(&" + name + ")[" + adt.getNumElements() + "]; // " + addr);
cwriter.println(
baseType.getDisplayName() + "(&" + name + ")[" + adt.getNumElements() + "] = *reinterpret_cast<"
+ baseType.getDisplayName() + "(*)[" + adt.getNumElements() + "]>(GH_MEM(0x" + addr + "));");
} else {
String refTypeStr = dt.getDisplayName() + "&";
hwriter.println("extern " + refTypeStr + " " + name + "; // " + addr);
cwriter.println(dataType + " " + name + "= (" + refTypeStr + ") GH_MEM(0x" + addr + ");");
hwriter.println(linkagePrefix + refTypeStr + " " + name + "; // " + addr);
cwriter.println(refTypeStr + " " + name + "= (" + refTypeStr + ") GH_MEM(0x" + addr + ");");
}
}
} catch (Exception e) {
@@ -233,4 +237,23 @@ public class GlobalDumper {
}
}
}
public void sanitizeGlobalSymbols() {
for (GlobalRec global : globalAddrs.values()) {
String sanitizedName = Utils.sanitizeIdentifier(global.name);
if (!sanitizedName.equals(global.name)) {
Symbol symbol = script.getSymbolAt(global.address);
if (symbol != null) {
script.println("Renaming global symbol: " + global.name + " -> " + sanitizedName);
AddLabelCmd cmd = new AddLabelCmd(global.address, sanitizedName,
symbol.getParentNamespace(),
SourceType.USER_DEFINED);
if (!cmd.applyTo(script.getCurrentProgram())) {
script.println("Error renaming symbol: " + cmd.getStatusMsg());
}
global.name = sanitizedName;
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
package re3lib;
import java.io.File;
import java.io.IOException;
import generic.jar.ResourceFile;
import ghidra.app.decompiler.DecompInterface;
@@ -27,10 +28,20 @@ public class RecompileConfig {
// The manually decompiled files (will not be overwritten by the auto
// decompiler)
public final File dirDecompFix;
// The automatically generated files get written here in case a gh_fix entry exists
// The automatically generated files get written here in case a gh_fix entry
// exists
// usable for referencing the modified function against the auto-decompiled one
public final File dirDecompRef;
// The path for generated function stubs, for yet-to-be-compiled functions
// Usefully for testing a part of the recompiled code without linker errors
public final File dirDecompStub;
// The CMake timestamp file, automatically touched sometimes to trigger a
// reconfigure
// mostly when adding new files to the project
public final File cmakeTimestampFile;
public final Program currentProgram;
public final DecompileCache decompCache;
@@ -54,6 +65,9 @@ public class RecompileConfig {
dirDecompAuto = new File(outputDir, "gh_auto");
dirDecompFix = new File(outputDir, "gh_fix");
dirDecompRef = new File(outputDir, "gh_ref");
dirDecompStub = new File(outputDir, "gh_stub");
cmakeTimestampFile = new File(outputDir, "gh_cmake_timestamp");
currentProgram = script.getCurrentProgram();
@@ -66,5 +80,16 @@ public class RecompileConfig {
dirDecompAuto.mkdirs();
dirDecompFix.mkdirs();
dirDecompRef.mkdirs();
dirDecompStub.mkdirs();
}
public void touchCMakeTimestamp() {
try {
if (!cmakeTimestampFile.exists()) {
cmakeTimestampFile.createNewFile();
}
cmakeTimestampFile.setLastModified(System.currentTimeMillis());
} catch (IOException e) {
}
}
}