From 0a3025302fcae638e2bcd7583475d45f7a23fb9c Mon Sep 17 00:00:00 2001 From: Guus Waals <_@guusw.nl> Date: Wed, 18 Sep 2024 01:56:43 +0800 Subject: [PATCH] WIP --- CMakeLists.txt | 1 + game_re/gh_global.cxx | 60 + game_re/gh_global.h | 66 ++ ghidra.rep/idata/00/00000000.prp | 4 +- ghidra.rep/projectState | 1881 +----------------------------- scripts/DecompileC.java | 279 ++++- 6 files changed, 365 insertions(+), 1926 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 481569be..02b37b23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.27.0) project(reman3) add_subdirectory(game_re) +add_subdirectory(reference_bin) # Use highest possible C standard set_target_properties(game_re PROPERTIES C_STANDARD 23) diff --git a/game_re/gh_global.cxx b/game_re/gh_global.cxx index 0e3b995e..6f72954a 100644 --- a/game_re/gh_global.cxx +++ b/game_re/gh_global.cxx @@ -1,2 +1,62 @@ // AUTO-GENERATED FILE #include +#include "gh_structs.h" +undefined UNK_0077d5ed {}; // 0077d5ed +undefined DAT_0077d5ec {}; // 0077d5ec +undefined lpString_0077d3c0 {}; // 0077d3c0 +const char* s_CompleteInstall_005b6854 = "CompleteInstall"; // 005b6854 +undefined lpchText_0077d5e0 {}; // 0077d5e0 +undefined4 DAT_0077d0b4 {}; // 0077d0b4 +const char* s_Italian_005b6730 = "Italian"; // 005b6730 +const char* s_ou_appuyez_sur_Echap_pour_quitte_005b67d0 = "ou appuyez sur Echap pour quitter Rayman 3."; // 005b67d0 +const char* s_Gli_Mode_005b6414 = "Gli_Mode"; // 005b6414 +undefined lpString_0077d2c0 {}; // 0077d2c0 +char UNK_0077d5f1 {}; // 0077d5f1 +char DAT_0077d5f2 {}; // 0077d5f2 +undefined DAT_0077d5f0 {}; // 0077d5f0 +HANDLE DAT_0077d4c8 {}; // 0077d4c8 +const char* s_Spanish_005b67a0 = "Spanish"; // 005b67a0 +undefined lpString_0077d1c0 {}; // 0077d1c0 +HWND DAT_0077d4c4 {}; // 0077d4c4 +undefined DAT_005b683c {}; // 005b683c +char DAT_005b6628 {}; // 005b6628 +HINSTANCE DAT_0077d4c0 {}; // 0077d4c0 +const char* s_Rayman_3_Error_005b68bc = "Rayman 3 Error"; // 005b68bc +uint DAT_0077d0a8 {}; // 0077d0a8 +undefined4 DAT_005b6624 {}; // 005b6624 +const char* s_Erreur_Rayman_3_005b65e4 = "Erreur Rayman 3 "; // 005b65e4 +undefined2 DAT_005b6684 {}; // 005b6684 +undefined2 DAT_007825c0 {}; // 007825c0 +undefined UNK_0077d5e9 {}; // 0077d5e9 +const char* s_Adapter_005b68e4 = "Adapter"; // 005b68e4 +const char* s_French_005b6828 = "French"; // 005b6828 +undefined UNK_0077d5e5 {}; // 0077d5e5 +undefined DAT_0077d5e8 {}; // 0077d5e8 +const char* s_DRAWSEM_005b6608 = "DRAWSEM"; // 005b6608 +undefined UNK_0077d5e1 {}; // 0077d5e1 +undefined DAT_0077d5e4 {}; // 0077d5e4 +r3_main_data r3_main_data_005d28b6 {}; // 005d28b6 +const char* s_Identifier_005b6420 = "Identifier"; // 005b6420 +long lpDefault_005cf96c {}; // 005cf96c +HANDLE DAT_0077d0bc {}; // 0077d0bc +undefined lpName_0077d0c0 {}; // 0077d0c0 +const char* s_English_005b684c = "English"; // 005b684c +pointer PTR_DAT_005b6410 {}; // 005b6410 +undefined s_USCIRE_005b66ec {}; // 005b66ec +undefined DAT_0077d4e0 {}; // 0077d4e0 +const char* s_Rayman_3_005b6588 = "Rayman 3"; // 005b6588 +const char* s_Directory_005b68f8 = "Directory"; // 005b68f8 +const char* s_Language_005b6840 = "Language"; // 005b6840 +const char* s_German_005b66c4 = "German"; // 005b66c4 +char UNK_00782609 {}; // 00782609 +undefined DAT_00782608 {}; // 00782608 +undefined UNK_00782605 {}; // 00782605 +char DAT_00782606 {}; // 00782606 +undefined s_SALIR_005b675c {}; // 005b675c +uint DAT_007d9cc4 {}; // 007d9cc4 +undefined s_BEENDIGEN_005b6678 {}; // 005b6678 +undefined DAT_00782604 {}; // 00782604 +const char* s_Premi_ESC_per_uscire_da_Rayman_3_005b66f4 = "Premi ESC per uscire da Rayman 3."; // 005b66f4 +undefined UNK_00782601 {}; // 00782601 +undefined DAT_00782600 {}; // 00782600 +undefined s_QUITTER_005b67fc {}; // 005b67fc diff --git a/game_re/gh_global.h b/game_re/gh_global.h index 0e3b995e..d24160a7 100644 --- a/game_re/gh_global.h +++ b/game_re/gh_global.h @@ -1,2 +1,68 @@ // AUTO-GENERATED FILE +#ifndef GH_GENERATED_GLOBALS_H +#define GH_GENERATED_GLOBALS_H + #include +#include "gh_structs.h" + + +extern undefined UNK_0077d5ed; // 0077d5ed +extern undefined DAT_0077d5ec; // 0077d5ec +extern undefined lpString_0077d3c0; // 0077d3c0 +extern const char* s_CompleteInstall_005b6854; // 005b6854 +extern undefined lpchText_0077d5e0; // 0077d5e0 +extern undefined4 DAT_0077d0b4; // 0077d0b4 +extern const char* s_Italian_005b6730; // 005b6730 +extern const char* s_ou_appuyez_sur_Echap_pour_quitte_005b67d0; // 005b67d0 +extern const char* s_Gli_Mode_005b6414; // 005b6414 +extern undefined lpString_0077d2c0; // 0077d2c0 +extern char UNK_0077d5f1; // 0077d5f1 +extern char DAT_0077d5f2; // 0077d5f2 +extern undefined DAT_0077d5f0; // 0077d5f0 +extern HANDLE DAT_0077d4c8; // 0077d4c8 +extern const char* s_Spanish_005b67a0; // 005b67a0 +extern undefined lpString_0077d1c0; // 0077d1c0 +extern HWND DAT_0077d4c4; // 0077d4c4 +extern undefined DAT_005b683c; // 005b683c +extern char DAT_005b6628; // 005b6628 +extern HINSTANCE DAT_0077d4c0; // 0077d4c0 +extern const char* s_Rayman_3_Error_005b68bc; // 005b68bc +extern uint DAT_0077d0a8; // 0077d0a8 +extern undefined4 DAT_005b6624; // 005b6624 +extern const char* s_Erreur_Rayman_3_005b65e4; // 005b65e4 +extern undefined2 DAT_005b6684; // 005b6684 +extern undefined2 DAT_007825c0; // 007825c0 +extern undefined UNK_0077d5e9; // 0077d5e9 +extern const char* s_Adapter_005b68e4; // 005b68e4 +extern const char* s_French_005b6828; // 005b6828 +extern undefined UNK_0077d5e5; // 0077d5e5 +extern undefined DAT_0077d5e8; // 0077d5e8 +extern const char* s_DRAWSEM_005b6608; // 005b6608 +extern undefined UNK_0077d5e1; // 0077d5e1 +extern undefined DAT_0077d5e4; // 0077d5e4 +extern r3_main_data r3_main_data_005d28b6; // 005d28b6 +extern const char* s_Identifier_005b6420; // 005b6420 +extern long lpDefault_005cf96c; // 005cf96c +extern HANDLE DAT_0077d0bc; // 0077d0bc +extern undefined lpName_0077d0c0; // 0077d0c0 +extern const char* s_English_005b684c; // 005b684c +extern pointer PTR_DAT_005b6410; // 005b6410 +extern undefined s_USCIRE_005b66ec; // 005b66ec +extern undefined DAT_0077d4e0; // 0077d4e0 +extern const char* s_Rayman_3_005b6588; // 005b6588 +extern const char* s_Directory_005b68f8; // 005b68f8 +extern const char* s_Language_005b6840; // 005b6840 +extern const char* s_German_005b66c4; // 005b66c4 +extern char UNK_00782609; // 00782609 +extern undefined DAT_00782608; // 00782608 +extern undefined UNK_00782605; // 00782605 +extern char DAT_00782606; // 00782606 +extern undefined s_SALIR_005b675c; // 005b675c +extern uint DAT_007d9cc4; // 007d9cc4 +extern undefined s_BEENDIGEN_005b6678; // 005b6678 +extern undefined DAT_00782604; // 00782604 +extern const char* s_Premi_ESC_per_uscire_da_Rayman_3_005b66f4; // 005b66f4 +extern undefined UNK_00782601; // 00782601 +extern undefined DAT_00782600; // 00782600 +extern undefined s_QUITTER_005b67fc; // 005b67fc +#endif // GH_GENERATED_GLOBALS_H diff --git a/ghidra.rep/idata/00/00000000.prp b/ghidra.rep/idata/00/00000000.prp index f9d0b29e..32675433 100644 --- a/ghidra.rep/idata/00/00000000.prp +++ b/ghidra.rep/idata/00/00000000.prp @@ -2,12 +2,12 @@ - + - + diff --git a/ghidra.rep/projectState b/ghidra.rep/projectState index 8b7470cc..11eed53f 100644 --- a/ghidra.rep/projectState +++ b/ghidra.rep/projectState @@ -9,1886 +9,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/scripts/DecompileC.java b/scripts/DecompileC.java index 259942a7..4cde493c 100644 --- a/scripts/DecompileC.java +++ b/scripts/DecompileC.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; +import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; @@ -15,24 +16,34 @@ import java.util.Iterator; import java.util.List; import java.util.Scanner; import java.util.Arrays; +import java.util.Dictionary; + import ghidra.app.cmd.label.AddLabelCmd; +import ghidra.app.decompiler.ClangLine; import ghidra.app.decompiler.ClangMarkup; import ghidra.app.decompiler.ClangNode; +import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangTokenGroup; import ghidra.app.decompiler.DecompInterface; import ghidra.app.decompiler.DecompileResults; import ghidra.app.decompiler.DecompiledFunction; +import ghidra.app.decompiler.PrettyPrinter; 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.CategoryPath; import ghidra.program.model.data.DataOrganization; import ghidra.program.model.data.DataOrganizationImpl; import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeComponent; import ghidra.program.model.data.PointerDataType; +import ghidra.program.model.data.ProgramBasedDataTypeManager; +import ghidra.program.model.data.SourceArchive; import ghidra.program.model.data.StringDataInstance; +import ghidra.program.model.data.Structure; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Variable; import ghidra.program.model.listing.VariableStorage; @@ -44,12 +55,37 @@ import ghidra.program.model.pcode.HighSymbol; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOpAST; import ghidra.program.model.pcode.Varnode; +import ghidra.program.model.symbol.NameTransformer; 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 DecompileCache { + private static final int TIMEOUT = 10000; + + Hashtable cache = new Hashtable<>(); + DecompInterface decomp; + + public DecompileCache(DecompInterface decomp) { + this.decomp = decomp; + } + + public DecompileResults get(Function function) { + return cache.get(function); + } + + public DecompileResults getOrInsert(Function function) { + DecompileResults res = cache.get(function); + if (res == null) { + res = decomp.decompileFunction(function, TIMEOUT, monitor); + cache.put(function, res); + } + return res; + } + } + public class PCallTracer { public class QueueItem { Function function; @@ -65,9 +101,9 @@ public class DecompileC extends GhidraScript { public boolean trace = false; List queue = new ArrayList<>(); HashSet
visited = new HashSet<>(); - DecompInterface decomp; + DecompileCache decomp; - PCallTracer(DecompInterface decomp) { + PCallTracer(DecompileCache decomp) { this.decomp = decomp; } @@ -104,7 +140,7 @@ public class DecompileC extends GhidraScript { if (trace) { println("PCallTracer, visiting " + function.getName() + " (depth:" + depth + ")"); } - DecompileResults decompRes = decomp.decompileFunction(function, TIMEOUT, monitor); + DecompileResults decompRes = decomp.getOrInsert(function); visit(decompRes.getHighFunction(), depth); out.add(function); } @@ -120,12 +156,13 @@ 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; + private DecompileCache decompCache; + // Auto rename invalid symbols private static final boolean AUTO_RENAME_SYMBOLS = true; @@ -187,7 +224,7 @@ public class DecompileC extends GhidraScript { static final boolean BUILD_BLACKLIST = true; - void buildFunctionBlacklist(DecompInterface decomp) { + void buildFunctionBlacklist() { loadFunctionBlacklist(); if (BUILD_BLACKLIST) { @@ -216,7 +253,7 @@ public class DecompileC extends GhidraScript { if (isIgnoredFunction) { // Decompile and trace - PCallTracer tracer = new PCallTracer(decomp); + PCallTracer tracer = new PCallTracer(decompCache); tracer.setBlacklist(functionAddrBlackList); tracer.traceCalls(function); for (Function f : tracer.out) { @@ -233,13 +270,13 @@ public class DecompileC extends GhidraScript { } } - void sanitizeGlobalSymbolsPass(DecompInterface decomp, List functions) { + void sanitizeGlobalSymbolsPass(List functions) { Hashtable globalSymbols = new Hashtable<>(); for (Function function : functions) { println("Processing global symbols for " + function.getName()); - DecompileResults decompRes = decomp.decompileFunction(function, TIMEOUT, monitor); + DecompileResults decompRes = decompCache.getOrInsert(function); Iterator smyIt = decompRes.getHighFunction().getGlobalSymbolMap().getSymbols(); HighSymbol gsym = smyIt.next(); @@ -303,7 +340,7 @@ public class DecompileC extends GhidraScript { return sb.toString(); } - void decompileFunction(Hashtable outGlobalSymbols, DecompInterface decomp, Function function) + void decompileFunction(Hashtable outGlobalSymbols, Function function) throws Exception { String fileName = sanitizeFunctionName(function.getName()) + ".cxx"; @@ -320,51 +357,192 @@ public class DecompileC extends GhidraScript { 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 "); - writer2.println("#include \"../gh_global.h\""); - writer2.println(); + DecompileResults decompRes = decompCache.getOrInsert(function); + try (PrintWriter writer2 = new PrintWriter(f0, "UTF-8")) { + writer2.println("// AUTO-GENERATED FILE, MOVE TO 'gh_fix' FOLDER PREVENT OVERWRITING!!!!! "); + writer2.println(); + writer2.println("#include "); + 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()); + // decompRes.get + HighFunction highFunction = decompRes.getHighFunction(); - writer2.close(); + // Remap for dynamic symbols + // Dictionary symbolRemap = new Hashtable<>(); - // Collect referenced global symbols - Iterator smyIt = decompRes.getHighFunction().getGlobalSymbolMap().getSymbols(); - while (smyIt.hasNext()) { - HighSymbol gsym = smyIt.next(); - if (outGlobalSymbols.containsKey(gsym.getName())) - continue; - outGlobalSymbols.put(gsym.getName(), gsym); + HashSet headers = new HashSet<>(); + StringWriter codeWriter = new StringWriter(); + + PrettyPrinter pp = new PrettyPrinter(decompRes.getFunction(), decompRes.getCCodeMarkup(), null); + Iterator lines = pp.getLines().iterator(); + while (lines.hasNext()) { + ClangLine line = lines.next(); + for (int i = 0; i < line.getIndent(); i++) { + codeWriter.write(' '); + } + for (int t = 0; t < line.getNumTokens(); t++) { + ClangToken token = line.getToken(t); + HighSymbol gsym = token.getHighSymbol(highFunction); + if (gsym != null) { + var symStorage = gsym.getStorage(); + if (symStorage.isMemoryStorage() || symStorage.isConstantStorage()) { + // println("Token: " + token.toString() + " - " + gsym.getName()); + outGlobalSymbols.put(gsym.getName(), gsym); + } + } + PcodeOp op = token.getPcodeOp(); + if (op != null && op.getOpcode() == PcodeOp.CALL) { + println("PcodeOp: " + op.toString() + " - " + op.getInput(0).toString()); + Varnode target = op.getInput(0); + if (target.isAddress()) { + Address callAddr = target.getAddress(); + Function calledFunction = getFunctionAt(callAddr); + if (calledFunction != null) { + if (functionAddrBlackList.contains(calledFunction.getEntryPoint())) { + println("Adding header: " + calledFunction + " / " + + calledFunction.getSignature().getPrototypeString(true)); + headers.add("extern " + calledFunction.getSignature().getPrototypeString(true)); + } + } + } + } + codeWriter.write(token.toString()); + } + codeWriter.write('\n'); + } + + for (String header : headers) { + writer2.println(header + ";"); + } + writer2.println(); + writer2.println("// " + function.getEntryPoint()); + writer2.print(codeWriter.toString()); + writer2.println(); + + // Iterator it = decompRes.getCCodeMarkup().tokenIterator(true); + // int ln = 0; + // while(it.hasNext()) { + // ClangToken token = it.next(); + // ClangLine line = token.getLineParent(); + // while (line != null && ln < line.getLineNumber()) { + // writer2.println(); + // ln++; + // } + // writer2.print(token.toString()); + // } + + // Collect referenced global symbols + // Iterator smyIt = highFunction.getGlobalSymbolMap().getSymbols(); + // while (smyIt.hasNext()) { + // HighSymbol gsym = smyIt.next(); + + // Address addr = gsym.getSymbol().getAddress(); + // println("FunctionSym " + addr + " " + gsym.getName() + " " + + // gsym.getStorage().getMinAddress()); + // println(" IsMem: " + gsym.getStorage().isMemoryStorage() + " " + + // gsym.getStorage().getSerializationString()); + + // if (outGlobalSymbols.containsKey(gsym.getName())) + // continue; + // outGlobalSymbols.put(gsym.getName(), gsym); + // } } } - void decompileAll(DecompInterface decomp, List functions) throws Exception { - Hashtable globalSymbols = new Hashtable<>(); - - for (Function function : functions) { - decompileFunction(globalSymbols, decomp, function); + HashSet loadStructBlacklist() { + File file = new File(outputDir, "struct_blacklist.txt"); + HashSet structBlacklist = new HashSet<>(); + try (Scanner scanner = new Scanner(file)) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + structBlacklist.add(line.trim()); + } + } catch (FileNotFoundException e) { + return null; } + return structBlacklist; + } + void saveStructBlacklist(HashSet structBlacklist) throws Exception { + String[] arr = structBlacklist.toArray(new String[0]); + Arrays.sort(arr); + File file = new File(outputDir, "struct_blacklist.txt"); + try (PrintWriter writer = new PrintWriter(file)) { + for (String structName : arr) { + writer.println(structName); + } + } + } + + void headerGuardPre(PrintWriter writer, String tag) { + writer.println("#ifndef GH_GENERATED_" + tag + "_H"); + writer.println("#define GH_GENERATED_" + tag + "_H"); + writer.println(); + } + + void headerGuardPost(PrintWriter writer, String tag) { + writer.println("#endif // GH_GENERATED_" + tag + "_H"); + } + + void dumpStructureTypes() throws Exception { + try (PrintWriter writer = new PrintWriter(new File(outputDir, "gh_structs.h"), "UTF-8")) { + headerGuardPre(writer, "STRUCTS"); + writer.println("// AUTO-GENERATED FILE "); + writer.println("#include "); + ProgramBasedDataTypeManager dtm = currentProgram.getDataTypeManager(); + + HashSet structBlacklist = loadStructBlacklist(); + if (structBlacklist == null) { + println("Building struct blacklist from existing data types"); + structBlacklist = new HashSet<>(); + Iterator it = dtm.getAllDataTypes(); + while (it.hasNext()) { + DataType dt = it.next(); + if (dt instanceof Structure) { + structBlacklist.add(dt.getName()); + } + } + saveStructBlacklist(structBlacklist); + } + + Iterator it = dtm.getAllDataTypes(); + while (it.hasNext()) { + DataType dt = it.next(); + if (dt instanceof Structure) { + Structure struct = (Structure) dt; + if (structBlacklist.contains(struct.getName())) + continue; + writer.println("struct " + struct.getName() + " {"); + for (DataTypeComponent component : struct.getComponents()) { + writer.println( + " " + component.getDataType().getDisplayName() + " " + component.getDefaultFieldName() + ";"); + } + writer.println("};"); + writer.println(); + } + } + + headerGuardPost(writer, "STRUCTS"); + } + } + + void dumpGlobals(Hashtable globalSymbols) throws Exception { File globalSymbolsListH = new File(outputDir, "gh_global.h"); PrintWriter hwriter = new PrintWriter(globalSymbolsListH, "UTF-8"); hwriter.println("// AUTO-GENERATED FILE "); + headerGuardPre(hwriter, "GLOBALS"); hwriter.println("#include "); + hwriter.println("#include \"gh_structs.h\""); + hwriter.println(); File globalSymbolsListC = new File(outputDir, "gh_global.cxx"); PrintWriter cwriter = new PrintWriter(globalSymbolsListC, "UTF-8"); cwriter.println("// AUTO-GENERATED FILE "); cwriter.println("#include "); + cwriter.println("#include \"gh_structs.h\""); + hwriter.println(); + for (HighSymbol highSym : globalSymbols.values()) { DataType dt = highSym.getDataType(); String dataType = dt.getDisplayName(); @@ -400,7 +578,7 @@ public class DecompileC extends GhidraScript { dataType = baseType.getDisplayName() + "*"; initBlk += "gh_ptr(0x" + addr + ")"; } else { - initBlk = " = 0"; + initBlk = " {}"; } cwriter.println(dataType + " " + name + initBlk + "; // " + addr); } catch (Exception e) { @@ -412,16 +590,32 @@ public class DecompileC extends GhidraScript { hwriter.println("extern " + dataType + " " + name + "; // " + addr); } } + headerGuardPost(hwriter, "GLOBALS"); hwriter.close(); cwriter.close(); } + void decompileAll(List functions) throws Exception { + Hashtable globalSymbols = new Hashtable<>(); + + for (Function function : functions) { + decompileFunction(globalSymbols, function); + } + + dumpStructureTypes(); + dumpGlobals(globalSymbols); + } + @Override public void run() throws Exception { if (currentProgram == null) { return; } + DecompInterface decomp = new DecompInterface(); + decomp.openProgram(currentProgram); + decompCache = new DecompileCache(decomp); + staticMemoryBlockStart = currentProgram.getAddressFactory().getAddress("005b6400"); staticMemoryBlockEnd = currentProgram.getAddressFactory().getAddress("00843fff"); @@ -438,10 +632,7 @@ public class DecompileC extends GhidraScript { println("Output path: " + outputDir.getCanonicalPath()); - DecompInterface decomp = new DecompInterface(); - decomp.openProgram(currentProgram); - - buildFunctionBlacklist(decomp); + buildFunctionBlacklist(); List functions = new ArrayList<>(); @@ -457,9 +648,9 @@ public class DecompileC extends GhidraScript { int mode = 1; if (mode == 0) { // Sanitize symbols - sanitizeGlobalSymbolsPass(decomp, functions); + sanitizeGlobalSymbolsPass(functions); } else if (mode == 1) { // Decompile all functions - decompileAll(decomp, functions); + decompileAll(functions); } }