WIP Recompile

This commit is contained in:
2024-09-16 01:42:27 +08:00
parent 5ac970ecdc
commit 556fa70207
16 changed files with 2115 additions and 197 deletions

View File

@@ -16,14 +16,22 @@ import java.util.List;
import java.util.Scanner;
import java.util.Arrays;
import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.decompiler.ClangMarkup;
import ghidra.app.decompiler.ClangNode;
import ghidra.app.decompiler.ClangTokenGroup;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.decompiler.DecompiledFunction;
import ghidra.app.script.GhidraScript;
import ghidra.docking.settings.Settings;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.AbstractStringDataType;
import ghidra.program.model.data.BuiltInDataType;
import ghidra.program.model.data.DataOrganization;
import ghidra.program.model.data.DataOrganizationImpl;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Variable;
@@ -39,6 +47,7 @@ import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SymbolType;
public class DecompileC extends GhidraScript {
public class PCallTracer {
@@ -113,6 +122,10 @@ public class DecompileC extends GhidraScript {
private static final String OUTPUT_DIR = "game_re";
private static final int TIMEOUT = 10000;
// The static memory block
private Address staticMemoryBlockStart;
private Address staticMemoryBlockEnd;
// Auto rename invalid symbols
private static final boolean AUTO_RENAME_SYMBOLS = true;
@@ -129,7 +142,6 @@ public class DecompileC extends GhidraScript {
void loadFunctionBlacklist() {
functionAddrBlackList.clear();
File blacklistFile = new File(outputDir, "blacklist.txt");
try (Scanner scanner = new Scanner(blacklistFile)) {
while (scanner.hasNextLine()) {
@@ -261,93 +273,143 @@ public class DecompileC extends GhidraScript {
}
String escapeCString(String str) {
str = str.replaceAll("\"", "\\\"");
str = str.replaceAll("\n", "\\n");
str = str.replaceAll("\r", "\\r");
str = str.replaceAll("\t", "\\t");
str = str.replaceAll("\b", "\\b");
str = str.replaceAll("\f", "\\f");
str = str.replaceAll("\0", "\\0");
str = str.replace("\\", "\\\\");
str = str.replace("\"", "\\\"");
// str = str.replaceAll("\n", "\\n");
// str = str.replaceAll("\r", "\\r");
// str = str.replaceAll("\t", "\\t");
// str = str.replaceAll("\b", "\\b");
// str = str.replaceAll("\f", "\\f");
// str = str.replaceAll("\0", "\\0");
return str;
}
String readCString(Address addr, int maxLen) throws Exception {
StringBuilder sb = new StringBuilder();
int ofs = 0;
while (true) {
Address read = addr.add(ofs++);
// println("Reading: " + read);
byte b = currentProgram.getMemory().getByte(read);
// println("Read: " + b);
if (b == 0 || ofs >= maxLen) {
break;
}
sb.append((char) b);
}
if (sb.length() > 0) {
println("STR \"" + sb.toString() + "\"");
}
return sb.toString();
}
void decompileFunction(Hashtable<String, HighSymbol> outGlobalSymbols, DecompInterface decomp, Function function)
throws Exception {
String fileName = sanitizeFunctionName(function.getName()) + ".cxx";
File f1 = new File(dirDecompFix, fileName);
if (f1.exists()) {
println("Func " + function.getName() + " skipped (gh_fix)");
return;
}
File f0 = new File(dirDecompAuto, fileName);
if (f0.exists()) {
f0.delete();
}
println("Processing " + function.getName() + " => " + f0.toString());
DecompileResults decompRes = decomp.decompileFunction(function, TIMEOUT, monitor);
PrintWriter writer2 = new PrintWriter(f0, "UTF-8");
writer2.println("// AUTO-GENERATED FILE, MOVE TO 'gh_fix' FOLDER PREVENT OVERWRITING!!!!! ");
writer2.println("// " + function.getEntryPoint());
writer2.println();
writer2.println("#include <gh_auto_shared.h>");
writer2.println("#include \"../gh_global.h\"");
writer2.println();
HighFunction highFunction = decompRes.getHighFunction();
// ClangTokenGroup
// ClangNode.
// ClangTokenGroup ctg = decompRes.getCCodeMarkup();
// for (ClangTokenGroup it = ctg.groupIterator(); it.hasNext();) {
// }
writer2.println(cm.getCode());
writer2.close();
// Collect referenced global symbols
Iterator<HighSymbol> smyIt = decompRes.getHighFunction().getGlobalSymbolMap().getSymbols();
while (smyIt.hasNext()) {
HighSymbol gsym = smyIt.next();
if (outGlobalSymbols.containsKey(gsym.getName()))
continue;
outGlobalSymbols.put(gsym.getName(), gsym);
}
}
void decompileAll(DecompInterface decomp, List<Function> functions) throws Exception {
Hashtable<String, HighSymbol> globalSymbols = new Hashtable<>();
for (Function function : functions) {
String fileName = sanitizeFunctionName(function.getName()) + ".c";
File f1 = new File(dirDecompFix, fileName);
if (f1.exists()) {
println("Func " + function.getName() + " skipped (gh_fix)");
continue;
}
File f0 = new File(dirDecompAuto, fileName);
if (f0.exists()) {
f0.delete();
}
println("Processing " + function.getName() + " => " + f0.toString());
DecompileResults decompRes = decomp.decompileFunction(function, TIMEOUT, monitor);
PrintWriter writer2 = new PrintWriter(f0, "UTF-8");
writer2.println("// AUTO-GENERATED FILE, MOVE TO 'gh_fix' FOLDER PREVENT OVERWRITING!!!!! ");
writer2.println("// " + function.getEntryPoint());
writer2.println();
writer2.println("#include <gh_auto_shared.h>");
writer2.println();
writer2.println(decompRes.getDecompiledFunction().getC());
writer2.close();
// Collect referenced global symbols
Iterator<HighSymbol> smyIt = decompRes.getHighFunction().getGlobalSymbolMap().getSymbols();
while (smyIt.hasNext()) {
HighSymbol gsym = smyIt.next();
if (globalSymbols.containsKey(gsym.getName()))
continue;
globalSymbols.put(gsym.getName(), gsym);
}
decompileFunction(globalSymbols, decomp, function);
}
writer.close();
File globalSymbolsListH = new File(outputDir, "gh_global.h");
File globalSymbolsListC = new File(outputDir, "gh_global.c");
PrintWriter hwriter = new PrintWriter(globalSymbolsListH, "UTF-8");
hwriter.println("// AUTO-GENERATED FILE ");
hwriter.println("#include <gh_auto_shared.h>");
File globalSymbolsListC = new File(outputDir, "gh_global.cxx");
PrintWriter cwriter = new PrintWriter(globalSymbolsListC, "UTF-8");
for (HighSymbol sym : globalSymbols.values()) {
DataType dt = sym.getDataType();
String sanitizedName = sanitizeFunctionName(sym.getName());
if (!sanitizedName.equals(sym.getName())) {
println("Invalid global symbol name: " + sym.getName() + " - " + sym.getHighFunction().getFunction().getName());
cwriter.println("// AUTO-GENERATED FILE ");
cwriter.println("#include <gh_auto_shared.h>");
for (HighSymbol highSym : globalSymbols.values()) {
DataType dt = highSym.getDataType();
String dataType = dt.getDisplayName();
String name = highSym.getName();
String sanitizedName = sanitizeFunctionName(highSym.getName());
if (!sanitizedName.equals(highSym.getName())) {
println("Invalid global symbol name: " + highSym.getName() + " - "
+ highSym.getHighFunction().getFunction().getName());
} else {
Address address = sym.getStorage().getMinAddress();
MemoryBlock block = currentProgram.getMemory().getBlock(address);
String dataType = dt.toString();
String name = sym.getName();
if (block == null) {
println("Can not read variable " + name + " (" + dataType + ") at " + address);
continue;
Symbol symbol = highSym.getSymbol();
VariableStorage storage = highSym.getStorage();
Address addr = storage.getMinAddress();
int symSize = highSym.getSize();
if (addr == null) {
// Not sure why this is sometimes null
// also when it is not null, Symbol.getAddress() is not correct but very small
// like 00000056
// Not that storage will be <undefined> so maybe can check that
addr = symbol.getAddress();
}
if (dt instanceof AbstractStringDataType) {
// String type
hwriter.println("extern " + dataType + " " + name + "; // " + address);
String srcBlock = "";
// Read the actual string data from Ghidra
if (block != null && block.isInitialized()) {
byte[] bytes = new byte[dt.getLength()];
block.getBytes(address, bytes);
// Parse from UTF-8
String stringValue = new String(bytes, StandardCharsets.UTF_8);
srcBlock = dataType + " " + name + " = \"" + escapeCString(stringValue) + "\";";
println("Symbol: " + symbol + " Addr: " + addr + " Size:" + symSize + " " + storage.getSerializationString());
try {
String initBlk = " = ";
if (dt instanceof AbstractStringDataType) {
AbstractStringDataType sdt = (AbstractStringDataType) dt;
dataType = "const char*";
// String type
initBlk += "\"" + escapeCString(readCString(addr, 2048)) + "\"";
} else if (dt instanceof PointerDataType) {
PointerDataType pdt = (PointerDataType) dt;
DataType baseType = pdt.getDataType();
dataType = baseType.getDisplayName() + "*";
initBlk += "gh_ptr(0x" + addr + ")";
} else {
initBlk = " = 0";
}
cwriter.println(srcBlock + " // " + address);
cwriter.println(dataType + " " + name + initBlk + "; // " + addr);
} catch (Exception e) {
println("Error processing global symbol: " + e);
println("Symbol: " + highSym.getName() + " - " + addr + " - "
+ highSym.getHighFunction().getFunction().getName());
}
hwriter.println("extern " + dataType + " " + name + "; // " + addr);
}
}
hwriter.close();
@@ -360,6 +422,9 @@ public class DecompileC extends GhidraScript {
return;
}
staticMemoryBlockStart = currentProgram.getAddressFactory().getAddress("005b6400");
staticMemoryBlockEnd = currentProgram.getAddressFactory().getAddress("00843fff");
// Make sure to create OUTPUT_PATH
rootDir = new File(sourceFile.getAbsolutePath()).getParentFile().getParentFile();
outputDir = new File(rootDir, OUTPUT_DIR);
@@ -390,15 +455,11 @@ public class DecompileC extends GhidraScript {
functions.add(function);
}
// File functionList = new File(outputDir, "functions.txt");
// PrintWriter writer = new PrintWriter(functionList, "UTF-8");
int mode = 1;
if (mode == 0) { // Sanitize symbols
sanitizeGlobalSymbolsPass(decomp, functions);
} else if (mode == 1) { // Decompile all functions
decompileAll(decomp, functions);
}
}