Add cparser java project

This commit is contained in:
2024-10-08 00:58:50 +08:00
parent 8f0e8f68bb
commit 0133a237ac
31 changed files with 299 additions and 324 deletions

184
java/ghidra/Decompile.java Normal file
View File

@@ -0,0 +1,184 @@
// Script to export decompiled C code from Ghidra
// @category _Reman3
// @menupath Reman3.Decompile All
import java.io.File;
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;
import java.util.regex.Pattern;
import java.util.Arrays;
import java.util.Dictionary;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.decompiler.PrettyPrinter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.AbstractStringDataType;
import ghidra.program.model.data.Array;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.BitFieldDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.EnumDataType;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.TypedefDataType;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.VariableStorage;
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 ghidra.program.model.symbol.Symbol;
import ghidra.util.task.TaskMonitor;
import re3lib.*;
public class Decompile extends GhidraScript {
// 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 dumpGlobals(Hashtable<String, GlobalRec> globalSymbols) throws Exception {
// File globalSymbolsListH = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.h");
// PrintWriter hwriter = new PrintWriter(globalSymbolsListH, "UTF-8");
// hwriter.println("// AUTO-GENERATED FILE ");
// headerGuardPre(hwriter, "GLOBALS");
// hwriter.println("#include <r3/binder/global.h>");
// hwriter.println();
// File globalSymbolsListC = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.cxx");
// PrintWriter cwriter = new PrintWriter(globalSymbolsListC, "UTF-8");
// cwriter.println("// AUTO-GENERATED FILE ");
// cwriter.println("#include <r3/binder/global.h>");
// hwriter.println();
// for (GlobalRec sym : globalSymbols.values()) {
// HighSymbol highSym = sym.highSymbol;
// DataType dt = highSym.getDataType();
// String dataType = dt.getDisplayName();
// String sanitizedName = sanitizeFunctionName(highSym.getName());
// String name = highSym.getName();
// if (!sanitizedName.equals(name)) {
// println("Invalid global symbol name: " + name);
// name = sanitizedName;
// }
// Address addr = sym.address;
// // println("Symbol: " + symbol + " Addr: " + addr + " Size:" + symSize + " " +
// // storage.getSerializationString());
// try {
// String initBlk = " = ";
// boolean fullyDefinedType = false;
// if (dt instanceof AbstractStringDataType) {
// AbstractStringDataType sdt = (AbstractStringDataType) dt;
// dataType = "const char*";
// // String type
// initBlk += "\"" + escapeCString(readCString(addr, 2048)) + "\"";
// fullyDefinedType = true;
// } else if (dt instanceof PointerDataType) {
// PointerDataType pdt = (PointerDataType) dt;
// DataType baseType = pdt.getDataType();
// dataType = baseType.getDisplayName() + "*";
// initBlk += "(" + dataType + ")&GH_MEM(0x" + addr + ")";
// fullyDefinedType = true;
// }
// if (fullyDefinedType) {
// hwriter.println("extern " + dataType + " " + name + "; // " + addr);
// cwriter.println(dataType + " " + name + initBlk + "; // " + addr);
// } else {
// if (dt instanceof Array) {
// // println("Array: " + dt.getDisplayName() + " - " + addr + " - " +
// // dt.getClass().getSimpleName());
// Array adt = (Array) dt;
// DataType baseType = adt.getDataType();
// hwriter.println(
// "extern " + 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 + ");");
// }
// }
// } catch (Exception e) {
// println("Error processing global symbol: " + e);
// println("Symbol: " + highSym.getName() + " - " + addr + " - "
// + highSym.getHighFunction().getFunction().getName());
// }
// }
// headerGuardPost(hwriter, "GLOBALS");
// hwriter.close();
// cwriter.close();
// }
void decompileAll(List<Function> functions) throws Exception {
// Hashtable<String, GlobalRec> globalSymbols = new Hashtable<>();
// for (Function function : functions) {
// decompileFunction(globalSymbols, function);
// }
// dumpStructureTypes();
// dumpGlobals(globalSymbols);
}
@Override
public void run() throws Exception {
if (currentProgram == null) {
return;
}
RecompileConfig.INSTANCE = new RecompileConfig(this);
if (!new File(RecompileConfig.INSTANCE.outputDir).exists()) {
throw new Exception("Output directory does not exist: " + RecompileConfig.INSTANCE.outputDir);
}
// Make sure to create output folders
RecompileConfig.INSTANCE.dirDecompFix.mkdirs();
RecompileConfig.INSTANCE.dirDecompAuto.mkdirs();
RecompileConfig.INSTANCE.dirDecompRef.mkdirs();
// buildFunctionBlacklist();
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);
}
decompileAll(functions);
}
String sanitizeFunctionName(String name) {
return name.replaceAll("[^a-zA-Z0-9_]", "_");
}
}

View File

@@ -0,0 +1,34 @@
// Script to export decompiled C code for selected function from Ghidra
// @category _Reman3
// @menupath Reman3.Dump Current Function
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.RecompileConfig;
public class DumpCurrentFunction extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper);
Function currentFunction = getFunctionContaining(currentAddress);
if (currentFunction != null) {
functionDumper.dump(currentFunction);
} else {
println("No function found at the current address.");
}
if (functionDumper.createdFile)
RecompileConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}
}

View File

@@ -0,0 +1,107 @@
// Decompile selected function recursively (until a given number of new functions is reached)
// @category _Reman3
// @menupath Reman3.Dump N Functions
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.pcodeCPort.address.Address;
import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
import re3lib.RecompileConfig;
import re3lib.TypeDumper;
public class DumpCurrentFunctionN extends GhidraScript {
final int NumFunctions = 8;
// class Entry {
// Function function;
// }
// class QueueEntry {
// Function function;
// List<Function> callees;
// }
// HashSet<Address> visited = new HashSet<>();
// QueueEntry enter(Function function) {
// if (visited.contains(function.getEntryPoint()))
// return null;
// visited.add(function.getEntryPoint());
// QueueEntry entry = new QueueEntry();
// entry.function = function;
// function.getCalledFunctions(monitor);
// }
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper);
PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionDumper.functionAddrBlackList);
tracer.traceCalls(getFunctionContaining(currentAddress));
List<Address> queue = new ArrayList<>();
// 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);
// 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;
// }
// for (Function func : functionsToDump) {
// functionDumper.dump(func);
// }
// if (functionDumper.createdFile)
// RecompileConfig.INSTANCE.touchCMakeTimestamp();
// globalDumper.dumpGlobals();
// globalDumper.saveGlobalManifest();
// }
// // Dump types
// TypeDumper dumper = new TypeDumper(this);
// dumper.run();
}
}

View File

@@ -0,0 +1,78 @@
// Decompile selected function recursively
// @category _Reman3
// @menupath Reman3.Dump Current Function (recursive)
import java.util.ArrayList;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
import re3lib.RecompileConfig;
import re3lib.TypeDumper;
public class DumpCurrentFunctionRecursive extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
FunctionDumper functionDumper = new FunctionDumper(this, globalDumper);
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;
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;
}
for (Function func : functionsToDump) {
functionDumper.dump(func);
}
if (functionDumper.createdFile)
RecompileConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}
// Dump types
TypeDumper dumper = new TypeDumper(this);
dumper.run();
}
}

View File

@@ -0,0 +1,18 @@
// Script to dump all custom types from Ghidra
// @category _Reman3
// @menupath Reman3.Dump Types
import ghidra.app.script.GhidraScript;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.TypedefDataType;
import re3lib.TypeDumper;
public class DumpTypes extends GhidraScript {
@Override
protected void run() throws Exception {
TypeDumper dumper = new TypeDumper(this);
dumper.run();
}
}

View File

@@ -0,0 +1,51 @@
// Exports binary read only and data segments to a binary + header file
// @category _Reman3
// @menupath Tools.Reman3.Export Data
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import re3lib.RecompileConfig;
public class ExportData extends GhidraScript {
@Override
protected void run() throws Exception {
if (currentProgram == null) {
return;
}
RecompileConfig.INSTANCE = new RecompileConfig(this);
String dataFile = new File(RecompileConfig.INSTANCE.outputDir, "gh_datasegment.bin").toString();
String headerFile = new File(RecompileConfig.INSTANCE.outputDir, "gh_datasegment.h").toString();
FileOutputStream dataOutputStream = new FileOutputStream(dataFile);
PrintWriter headerWriter = new PrintWriter(headerFile, "UTF-8");
Address startAddr = RecompileConfig.INSTANCE.staticMemoryBlockStart;
Address endAddr = RecompileConfig.INSTANCE.staticMemoryBlockEnd;
// Dump all the memory to the bin file
int numBytes = (int) endAddr.subtract(startAddr);
byte[] buffer = new byte[numBytes];
currentProgram.getMemory().getBytes(startAddr, buffer);
dataOutputStream.write(buffer);
headerWriter.println("// AUTO-GENERATED FILE, DO NOT MODIFY!!!!!");
headerWriter.println("// Use 'Export Data' script to export the data segment");
headerWriter.println("#pragma once");
headerWriter.println("#include <cstddef>");
headerWriter.println("");
headerWriter.println("#define GH_DATA_START 0x" + startAddr.toString());
headerWriter.println("#define GH_DATA_END 0x" + endAddr.toString());
headerWriter.println("#define GH_DATA_SIZE (GH_DATA_END - GH_DATA_START)");
headerWriter.println("constexpr size_t gh_data_start = 0x" + startAddr.toString() + ";");
headerWriter.println("constexpr size_t gh_data_end = 0x" + endAddr.toString() + ";");
headerWriter.println("constexpr size_t gh_data_size = gh_data_end - gh_data_start;");
headerWriter.close();
dataOutputStream.close();
}
}

23
java/ghidra/LICENSE Normal file
View File

@@ -0,0 +1,23 @@
Parts used from https://github.com/tenable/ghidra_tools
MIT License
Copyright (c) 2022 Tenable
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,148 @@
// @category _Reman3
// @menupath Reman3.Rebuild Function Database
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StandAloneDataTypeManager;
import re3lib.FunctionDatabase;
import re3lib.RecompileConfig;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cparser.Parser;
import cparser.Tokenizer;
public class RebuildFunctionDatabase extends GhidraScript {
// Will rebuild all functions
public boolean rebuildAllGlobals = true;
FunctionDatabase functionDB;
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
functionDB = new FunctionDatabase(this);
scanFile(new File(RecompileConfig.INSTANCE.outputDir, "gh_auto/r3_engineLoop.cxx"), FunctionDatabase.Type.Auto);
// scanDirectory(RecompileConfig.INSTANCE.dirDecompAuto, FunctionDatabase.Type.Auto);
// scanDirectory(RecompileConfig.INSTANCE.dirDecompFix, FunctionDatabase.Type.Fix);
// scanDirectory(RecompileConfig.INSTANCE.dirDecompStub, FunctionDatabase.Type.Stub);
println("Applying default filters...");
functionDB.applyDefaultFilters(rebuildAllGlobals);
println("Saving function database...");
functionDB.save();
println("Function database rebuilt successfully.");
// for (FunctionDatabase.Entry entry : functionDB.entries) {
// println(entry.address + " " + entry.name + " " + entry.file.getName());
// for (FunctionDatabase.Dependency dependency : entry.dependencies) {
// println(" " + dependency.address + " " + dependency.name);
// }
// }
}
private void scanDirectory(File directory, FunctionDatabase.Type type) throws Exception {
File[] files = directory.listFiles((dir, name) -> name.endsWith(".cxx"));
if (files == null)
return;
for (File file : files) {
scanFile(file, type);
}
}
private void parseOld(BufferedReader reader, File file, FunctionDatabase.Type type) throws Exception {
String line;
Pattern dependencyPattern = Pattern.compile("(\\w+)\\s+(\\w+)\\(.*\\);\\s*//\\s*([0-9A-Fa-f]{8})\\s*//\\s*(.*)");
Pattern addressPattern = Pattern.compile("//\\s*([0-9A-Fa-f]{8})");
Pattern functionNamePattern = Pattern.compile("(\\S+)\\s+(\\S+)\\s*\\(");
List<FunctionDatabase.Dependency> dependencies = new ArrayList<>();
String address = null;
String functionName = null;
while ((line = reader.readLine()) != null) {
Matcher dependencyMatcher = dependencyPattern.matcher(line);
if (dependencyMatcher.find()) {
// println("Found dependency: " + dependencyMatcher.group(3));
Address depAddress = currentProgram.getAddressFactory().getAddress(dependencyMatcher.group(3));
String name = dependencyMatcher.group(2);
FunctionDatabase.Dependency dependency = functionDB.new Dependency(depAddress, name);
dependencies.add(dependency);
continue;
}
Matcher addressMatcher = addressPattern.matcher(line);
if (addressMatcher.find()) {
// println("Found address: " + addressMatcher.group(1));
address = addressMatcher.group(1);
// Skip any comments or newlines between address and function definition
while ((line = reader.readLine()) != null) {
line = line.trim();
// println("Line: " + line);
if (!line.isEmpty()) {
Matcher functionNameMatcher = functionNamePattern.matcher(line);
if (functionNameMatcher.find()) {
functionName = functionNameMatcher.group(2).trim();
break;
}
}
}
if (functionName != null) {
break;
}
}
}
if (address != null && functionName != null) {
Address functionAddress = currentProgram.getAddressFactory().getAddress(address);
FunctionDatabase.Entry entry = functionDB.new Entry();
entry.address = functionAddress;
entry.name = functionName;
entry.file = file;
entry.type = type;
entry.dependencies = dependencies;
functionDB.entries.add(entry);
} else {
// throw new Exception("Failed to parse function at " + file.getName());
println("Failed to parse function at " + file.getName());
}
}
private void scanFile(File file, FunctionDatabase.Type type) throws Exception {
println("Scanning " + file);
String text = new String(Files.readAllBytes(file.toPath()));
Tokenizer.TokenSet tokens = new Tokenizer(text).parse();
Parser parser = new Parser(tokens);
parser.parse();
// for (CTokenizer.Token token : tokens.getTokens()) {
// int line = tokens.getLine(token.ofs);
// println("Line " + line + ": " + token.ofs + " " + token.len + " " + token.type + " - "
// + tokens.getTextNoNewlines(token));
// }
for (Parser.Function function : parser.getFunctions()) {
println("Function: " + function.name + " " + function.startOffset + " " + function.endOffset);
}
for (Parser.FunctionCall functionCall : parser.getFunctionCalls()) {
println("FunctionCall: " + functionCall.name + " " + functionCall.startOffset + " " + functionCall.endOffset);
}
for (Parser.Variable variable : parser.getVariables()) {
println("Variable: " + variable.name + " " + variable.startOffset + " " + variable.endOffset);
}
}
}

View File

@@ -0,0 +1,23 @@
// Script to sanitize global symbols in Ghidra
// @category _Reman3
// @menupath Reman3.Sanitize Global Symbols
import ghidra.app.script.GhidraScript;
import re3lib.GlobalDumper;
import re3lib.RecompileConfig;
public class SanitizeGlobalSymbols extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
globalDumper.sanitizeGlobalSymbols();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
}
}

View File

@@ -0,0 +1,842 @@
// Source code is decompiled from a .class file using FernFlower decompiler.
package re3lib;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DataTypeWriter {
public HashSet<String> blacklistedTypes;
private static String[] INTEGRAL_TYPES = new String[] { "char", "short", "int", "long", "long long", "__int64",
"float", "double", "long double", "void" };
private static String[] INTEGRAL_MODIFIERS = new String[] { "signed", "unsigned", "const", "static", "volatile",
"mutable" };
private static String EOL = System.getProperty("line.separator");
private Set<DataType> resolved;
private Map<String, DataType> resolvedTypeMap;
private Set<Composite> deferredCompositeDeclarations;
private ArrayDeque<DataType> deferredTypeFIFO;
private Set<DataType> deferredTypes;
private int writerDepth;
private Writer writer;
private DataTypeManager dtm;
private DataOrganization dataOrganization;
private AnnotationHandler annotator;
private boolean cppStyleComments;
public DataTypeWriter(DataTypeManager dtm, Writer writer) throws IOException {
this(dtm, writer, new DefaultAnnotationHandler());
}
public DataTypeWriter(DataTypeManager dtm, Writer writer, boolean cppStyleComments) throws IOException {
this(dtm, writer, new DefaultAnnotationHandler(), cppStyleComments);
}
public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator) throws IOException {
this(dtm, writer, annotator, false);
}
public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator, boolean cppStyleComments)
throws IOException {
this.blacklistedTypes = new HashSet<>();
this.resolved = new HashSet();
this.resolvedTypeMap = new HashMap();
this.deferredCompositeDeclarations = new HashSet();
this.deferredTypeFIFO = new ArrayDeque();
this.deferredTypes = new HashSet();
this.writerDepth = 0;
this.cppStyleComments = false;
this.dtm = dtm;
if (dtm != null) {
this.dataOrganization = dtm.getDataOrganization();
}
if (this.dataOrganization == null) {
this.dataOrganization = DataOrganizationImpl.getDefaultOrganization();
}
this.writer = writer;
this.annotator = annotator;
this.cppStyleComments = cppStyleComments;
if (dtm != null) {
// this.writeBuiltInDeclarations(dtm);
}
}
private String comment(String text) {
if (text == null) {
return "";
} else {
return this.cppStyleComments ? "// " + text : "/* " + text + " */";
}
}
public void write(DataTypeManager dataTypeManager, TaskMonitor monitor) throws IOException, CancelledException {
this.write(dataTypeManager.getRootCategory(), monitor);
}
public void write(Category category, TaskMonitor monitor) throws IOException, CancelledException {
DataType[] dataTypes = category.getDataTypes();
this.write(dataTypes, monitor);
Category[] subCategories = category.getCategories();
Category[] var5 = subCategories;
int var6 = subCategories.length;
for (int var7 = 0; var7 < var6; ++var7) {
Category subCategory = var5[var7];
if (monitor.isCancelled()) {
return;
}
this.write(subCategory, monitor);
}
}
public void write(DataType[] dataTypes, TaskMonitor monitor) throws IOException, CancelledException {
monitor.initialize((long) dataTypes.length);
int cnt = 0;
DataType[] var4 = dataTypes;
int var5 = dataTypes.length;
for (int var6 = 0; var6 < var5; ++var6) {
DataType dataType = var4[var6];
monitor.checkCancelled();
this.write(dataType, monitor);
++cnt;
monitor.setProgress((long) cnt);
}
}
public void write(List<DataType> dataTypes, TaskMonitor monitor) throws IOException, CancelledException {
this.write(dataTypes, monitor, true);
}
public void write(List<DataType> dataTypes, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
throws IOException, CancelledException {
monitor.initialize((long) dataTypes.size());
int cnt = 0;
Iterator var5 = dataTypes.iterator();
while (var5.hasNext()) {
DataType dataType = (DataType) var5.next();
monitor.checkCancelled();
this.write(dataType, monitor, throwExceptionOnInvalidType);
++cnt;
monitor.setProgress((long) cnt);
}
}
private void deferWrite(DataType dt) {
if (!this.resolved.contains(dt) && !this.deferredTypes.contains(dt)) {
this.deferredTypes.add(dt);
this.deferredTypeFIFO.addLast(dt);
}
}
void write(DataType dt, TaskMonitor monitor) throws IOException, CancelledException {
this.doWrite(dt, monitor, true);
}
void write(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
throws IOException, CancelledException {
this.doWrite(dt, monitor, throwExceptionOnInvalidType);
}
private void doWrite(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
throws IOException, CancelledException {
if (dt != null) {
if (blacklistedTypes.contains(dt.getDisplayName()))
return;
if (!(dt instanceof FunctionDefinition)) {
if (dt instanceof FactoryDataType) {
IllegalArgumentException iae = new IllegalArgumentException("Factory data types may not be written");
if (throwExceptionOnInvalidType) {
throw iae;
}
Msg.error(this, "Factory data types may not be written - type: " + dt);
}
if (!(dt instanceof Pointer) && !(dt instanceof Array) && !(dt instanceof BitFieldDataType)) {
dt = dt.clone(this.dtm);
if (!this.resolved.contains(dt)) {
this.resolved.add(dt);
DataType resolvedType = (DataType) this.resolvedTypeMap.get(dt.getName());
if (resolvedType != null) {
if (!resolvedType.isEquivalent(dt)) {
if (dt instanceof TypeDef) {
DataType baseType = ((TypeDef) dt).getBaseDataType();
if ((resolvedType instanceof Composite || resolvedType instanceof Enum)
&& baseType.isEquivalent(resolvedType)) {
return;
}
}
this.writer.write(EOL);
Writer var10000 = this.writer;
String var10002 = dt.getPathName();
var10000.write(this
.comment("WARNING! conflicting data type names: " + var10002 + " - " + resolvedType.getPathName()));
this.writer.write(EOL);
this.writer.write(EOL);
}
} else {
this.resolvedTypeMap.put(dt.getName(), dt);
++this.writerDepth;
if (dt.equals(DataType.DEFAULT)) {
this.writer.write("typedef unsigned char " + DataType.DEFAULT.getName() + ";");
this.writer.write(EOL);
this.writer.write(EOL);
} else if (dt instanceof Dynamic) {
this.writeDynamicBuiltIn((Dynamic) dt, monitor);
} else if (dt instanceof Structure) {
Structure struct = (Structure) dt;
this.writeCompositePreDeclaration(struct, monitor);
this.deferredCompositeDeclarations.add(struct);
} else if (dt instanceof Union) {
Union union = (Union) dt;
this.writeCompositePreDeclaration(union, monitor);
this.deferredCompositeDeclarations.add(union);
} else if (dt instanceof Enum) {
this.writeEnum((Enum) dt, monitor);
} else if (dt instanceof TypeDef) {
this.writeTypeDef((TypeDef) dt, monitor);
} else if (dt instanceof BuiltInDataType) {
this.writeBuiltIn((BuiltInDataType) dt, monitor);
} else if (!(dt instanceof BitFieldDataType)) {
this.writer.write(EOL);
this.writer.write(EOL);
this.writer.write(this.comment("Unable to write datatype. Type unrecognized: " + dt.getClass()));
this.writer.write(EOL);
this.writer.write(EOL);
}
if (this.writerDepth == 1) {
this.writeDeferredDeclarations(monitor);
}
--this.writerDepth;
}
}
} else {
this.write(this.getBaseDataType(dt), monitor);
}
}
}
}
private void writeDeferredDeclarations(TaskMonitor monitor) throws IOException, CancelledException {
while (!this.deferredTypes.isEmpty()) {
DataType dt = (DataType) this.deferredTypeFIFO.removeFirst();
this.deferredTypes.remove(dt);
this.write(dt, monitor);
}
this.writeDeferredCompositeDeclarations(monitor);
this.deferredCompositeDeclarations.clear();
}
private DataType getBaseArrayTypedefType(DataType dt) {
while (true) {
if (dt != null) {
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
continue;
}
if (dt instanceof Array) {
dt = ((Array) dt).getDataType();
continue;
}
}
return dt;
}
}
private boolean containsComposite(Composite container, Composite contained) {
DataTypeComponent[] var3 = container.getDefinedComponents();
int var4 = var3.length;
for (int var5 = 0; var5 < var4; ++var5) {
DataTypeComponent component = var3[var5];
DataType dt = this.getBaseArrayTypedefType(component.getDataType());
if (dt instanceof Composite && dt.getName().equals(contained.getName()) && dt.isEquivalent(contained)) {
return true;
}
}
return false;
}
private void writeDeferredCompositeDeclarations(TaskMonitor monitor) throws IOException, CancelledException {
int cnt = this.deferredCompositeDeclarations.size();
if (cnt != 0) {
LinkedList<Composite> list = new LinkedList(this.deferredCompositeDeclarations);
if (list.size() > 1) {
int sortChange = 1;
while (sortChange != 0) {
sortChange = 0;
for (int i = cnt - 1; i > 0; --i) {
if (this.resortComposites(list, i)) {
++sortChange;
}
}
}
}
Iterator var6 = list.iterator();
while (var6.hasNext()) {
Composite composite = (Composite) var6.next();
this.writeCompositeBody(composite, monitor);
}
}
}
private boolean resortComposites(List<Composite> list, int index) {
int listSize = list.size();
if (listSize <= 0) {
return false;
} else {
Composite composite = (Composite) list.get(index);
for (int i = 0; i < index; ++i) {
Composite other = (Composite) list.get(i);
if (this.containsComposite(other, composite)) {
list.remove(index);
list.add(i, composite);
composite = null;
return true;
}
}
return false;
}
}
private String getDynamicComponentString(Dynamic dynamicType, String fieldName, int length) {
if (dynamicType.canSpecifyLength()) {
DataType replacementBaseType = dynamicType.getReplacementBaseType();
if (replacementBaseType != null) {
replacementBaseType = replacementBaseType.clone(this.dtm);
int elementLen = replacementBaseType.getLength();
if (elementLen > 0) {
int elementCnt = (length + elementLen - 1) / elementLen;
return replacementBaseType.getDisplayName() + " " + fieldName + "[" + elementCnt + "]";
}
String var10001 = dynamicType.getClass().getSimpleName();
Msg.error(this,
var10001 + " returned bad replacementBaseType: " + replacementBaseType.getClass().getSimpleName());
}
}
return null;
}
private void writeCompositePreDeclaration(Composite composite, TaskMonitor monitor)
throws IOException, CancelledException {
String compositeType = composite instanceof Structure ? "struct" : "union";
this.writer.write("typedef " + compositeType + " " + composite.getDisplayName() + " " + composite.getDisplayName()
+ ", *P" + composite.getDisplayName() + ";");
this.writer.write(EOL);
this.writer.write(EOL);
DataTypeComponent[] var4 = composite.getComponents();
int var5 = var4.length;
for (int var6 = 0; var6 < var5; ++var6) {
DataTypeComponent component = var4[var6];
if (monitor.isCancelled()) {
break;
}
DataType componentType = component.getDataType();
this.deferWrite(componentType);
this.getTypeDeclaration((String) null, componentType, component.getLength(), true, monitor);
}
}
private void writeCompositeBody(Composite composite, TaskMonitor monitor) throws IOException, CancelledException {
String compositeType = composite instanceof Structure ? "struct" : "union";
StringBuilder sb = new StringBuilder();
sb.append(compositeType + " " + composite.getDisplayName() + " {");
String descrip = composite.getDescription();
if (descrip != null && descrip.length() > 0) {
String var10001 = this.comment(descrip);
sb.append(" " + var10001);
}
sb.append(EOL);
DataTypeComponent[] var6 = composite.getComponents();
int var7 = var6.length;
for (int var8 = 0; var8 < var7; ++var8) {
DataTypeComponent component = var6[var8];
monitor.checkCancelled();
this.writeComponent(component, composite, sb, monitor);
}
sb.append(this.annotator.getSuffix(composite, (DataTypeComponent) null));
sb.append("};");
this.writer.write(sb.toString());
this.writer.write(EOL);
this.writer.write(EOL);
}
private void writeComponent(DataTypeComponent component, Composite composite, StringBuilder sb, TaskMonitor monitor)
throws IOException, CancelledException {
sb.append(" ");
sb.append(this.annotator.getPrefix(composite, component));
String fieldName = component.getFieldName();
if (fieldName == null || fieldName.length() == 0) {
fieldName = component.getDefaultFieldName();
}
DataType componentDataType = component.getDataType();
sb.append(this.getTypeDeclaration(fieldName, componentDataType, component.getLength(), false, monitor));
sb.append(";");
sb.append(this.annotator.getSuffix(composite, component));
String comment = component.getComment();
if (comment != null && comment.length() > 0) {
String var10001 = this.comment(comment);
sb.append(" " + var10001);
}
sb.append(EOL);
}
private String getTypeDeclaration(String name, DataType dataType, int instanceLength, boolean writeEnabled,
TaskMonitor monitor) throws IOException, CancelledException {
if (name == null) {
name = "";
}
StringBuilder sb = new StringBuilder();
String componentString = null;
if (dataType instanceof Dynamic) {
componentString = this.getDynamicComponentString((Dynamic) dataType, name, instanceLength);
if (componentString != null) {
sb.append(componentString);
} else {
sb.append(this.comment("ignoring dynamic datatype inside composite: " + dataType.getDisplayName()));
sb.append(EOL);
}
}
if (componentString == null) {
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
name = name + ":" + bfDt.getDeclaredBitSize();
dataType = bfDt.getBaseDataType();
}
label44: while (true) {
while (!(dataType instanceof Array)) {
if (!(dataType instanceof Pointer)) {
break label44;
}
Pointer pointer = (Pointer) dataType;
DataType elem = pointer.getDataType();
if (elem == null) {
break label44;
}
name = "*" + name;
dataType = elem;
if (elem instanceof Array) {
name = "(" + name + ")";
}
}
Array array = (Array) dataType;
name = name + "[" + array.getNumElements() + "]";
dataType = array.getDataType();
}
DataType baseDataType = this.getBaseDataType(dataType);
if (baseDataType instanceof FunctionDefinition) {
componentString = this.getFunctionPointerString((FunctionDefinition) baseDataType, name, dataType, writeEnabled,
monitor);
} else {
String var10000 = this.getDataTypePrefix(dataType);
componentString = var10000 + dataType.getDisplayName();
if (name.length() != 0) {
componentString = componentString + " " + name;
}
}
sb.append(componentString);
}
return sb.toString();
}
private String getDataTypePrefix(DataType dataType) {
dataType = this.getBaseDataType(dataType);
if (dataType instanceof Structure) {
return "struct ";
} else if (dataType instanceof Union) {
return "union ";
} else {
return dataType instanceof Enum ? "enum " : "";
}
}
private void writeEnum(Enum enumm, TaskMonitor monitor) throws IOException {
String enumName = enumm.getDisplayName();
Writer var10000;
String var10001;
if (enumName.startsWith("define_") && enumName.length() > 7 && enumm.getCount() == 1) {
long val = enumm.getValues()[0];
var10000 = this.writer;
var10001 = enumName.substring(7);
var10000.append("#define " + var10001 + " " + Long.toString(val));
this.writer.write(EOL);
this.writer.write(EOL);
} else {
this.writer.write("enum " + enumName + " {");
String description = enumm.getDescription();
if (description != null && description.length() != 0) {
var10000 = this.writer;
var10001 = this.comment(description);
var10000.write(" " + var10001);
}
this.writer.write(EOL);
String[] names = enumm.getNames();
for (int j = 0; j < names.length; ++j) {
this.writer.write(" ");
this.writer.write(this.annotator.getPrefix(enumm, names[j]));
this.writer.write(names[j]);
this.writer.write("=");
this.writer.write(Long.toString(enumm.getValue(names[j])));
String comment = enumm.getComment(names[j]);
if (!comment.isBlank()) {
var10000 = this.writer;
var10001 = this.comment(comment);
var10000.write(" " + var10001);
}
this.writer.write(this.annotator.getSuffix(enumm, names[j]));
if (j < names.length - 1) {
this.writer.write(",");
}
this.writer.write(EOL);
}
this.writer.write("};");
this.writer.write(EOL);
this.writer.write(EOL);
}
}
private void writeTypeDef(TypeDef typeDef, TaskMonitor monitor) throws IOException, CancelledException {
String typedefName = typeDef.getDisplayName();
DataType dataType = typeDef.getDataType();
String dataTypeName = dataType.getDisplayName();
if (!this.isIntegral(typedefName, dataTypeName)) {
DataType baseType = typeDef.getBaseDataType();
label125: {
try {
if (!(baseType instanceof Composite) && !(baseType instanceof Enum)) {
if (!(baseType instanceof Pointer) || !typedefName.startsWith("P")) {
break label125;
}
DataType dt = ((Pointer) baseType).getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (!(dt instanceof Composite) || !dt.getName().equals(typedefName.substring(1))) {
break label125;
}
this.resolvedTypeMap.remove(typedefName);
return;
}
if (!typedefName.equals(baseType.getName())) {
break label125;
}
this.resolvedTypeMap.remove(typedefName);
} finally {
this.write(dataType, monitor);
}
return;
}
if (baseType instanceof Array && this.getBaseArrayTypedefType(baseType) instanceof Composite) {
this.writeDeferredDeclarations(monitor);
}
String typedefString = this.getTypeDeclaration(typedefName, dataType, -1, true, monitor);
this.writer.write("typedef " + typedefString + ";");
this.writer.write(EOL);
this.writer.write(EOL);
}
}
private boolean isIntegral(String typedefName, String basetypeName) {
String[] var3 = INTEGRAL_TYPES;
int var4 = var3.length;
int var5;
for (var5 = 0; var5 < var4; ++var5) {
String type = var3[var5];
if (typedefName.equals(type)) {
return true;
}
}
boolean endsWithIntegralType = false;
String[] var10 = INTEGRAL_TYPES;
var5 = var10.length;
int var13;
for (var13 = 0; var13 < var5; ++var13) {
String type = var10[var13];
if (typedefName.endsWith(" " + type)) {
endsWithIntegralType = true;
break;
}
}
boolean containsIntegralModifier = false;
String[] var12 = INTEGRAL_MODIFIERS;
var13 = var12.length;
for (int var14 = 0; var14 < var13; ++var14) {
String modifier = var12[var14];
if (typedefName.indexOf(modifier + " ") >= 0 || typedefName.indexOf(" " + modifier) >= 0) {
return true;
}
}
if (endsWithIntegralType && containsIntegralModifier) {
return true;
} else if (typedefName.endsWith(" " + basetypeName)) {
return containsIntegralModifier;
} else {
return false;
}
}
private void writeDynamicBuiltIn(Dynamic dt, TaskMonitor monitor) throws IOException, CancelledException {
DataType baseDt = dt.getReplacementBaseType();
if (baseDt != null) {
this.write(baseDt, monitor);
}
}
private void writeBuiltIn(BuiltInDataType dt, TaskMonitor monitor) throws IOException {
String declaration = dt.getCTypeDeclaration(this.dataOrganization);
if (declaration != null) {
if (dt.getDisplayName() == "bool")
return;
this.writer.write(declaration);
this.writer.write(EOL);
}
}
private void writeBuiltInDeclarations(DataTypeManager manager) throws IOException {
try {
this.write(DataType.DEFAULT, TaskMonitor.DUMMY);
SourceArchive builtInArchive = manager.getSourceArchive(DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID);
if (builtInArchive == null) {
return;
}
Iterator var3 = manager.getDataTypes(builtInArchive).iterator();
while (var3.hasNext()) {
DataType dt = (DataType) var3.next();
if (!(dt instanceof Pointer) && !(dt instanceof FactoryDataType) && !(dt instanceof Dynamic)) {
this.write(dt, TaskMonitor.DUMMY);
}
}
} catch (CancelledException var5) {
}
this.writer.flush();
}
private static String getArrayDimensions(Array arrayDt) {
String dimensionString = "[" + arrayDt.getNumElements() + "]";
DataType dataType = arrayDt.getDataType();
if (dataType instanceof Array) {
dimensionString = dimensionString + getArrayDimensions((Array) dataType);
}
return dimensionString;
}
private DataType getBaseDataType(DataType dt) {
while (true) {
if (dt != null) {
if (dt instanceof Array) {
Array array = (Array) dt;
dt = array.getDataType();
continue;
}
if (dt instanceof Pointer) {
Pointer pointer = (Pointer) dt;
dt = pointer.getDataType();
continue;
}
if (dt instanceof BitFieldDataType) {
BitFieldDataType bitfieldDt = (BitFieldDataType) dt;
dt = bitfieldDt.getBaseDataType();
continue;
}
}
return dt;
}
}
private DataType getArrayBaseType(Array arrayDt) {
DataType dataType;
for (dataType = arrayDt.getDataType(); dataType instanceof Array; dataType = ((Array) dataType).getDataType()) {
}
return dataType;
}
private DataType getPointerBaseDataType(Pointer p) {
DataType dt;
for (dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) {
}
return dt;
}
private int getPointerDepth(Pointer p) {
int depth = 1;
for (DataType dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) {
++depth;
}
return depth;
}
private String getFunctionPointerString(FunctionDefinition fd, String name, DataType functionPointerArrayType,
boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException {
DataType originalType = functionPointerArrayType;
StringBuilder sb = new StringBuilder();
DataType returnType = fd.getReturnType();
if (writeEnabled) {
this.write(returnType, monitor);
}
sb.append("(");
String arrayDecorations = "";
if (functionPointerArrayType instanceof Array a) {
functionPointerArrayType = this.getArrayBaseType(a);
arrayDecorations = getArrayDimensions(a);
}
if (functionPointerArrayType instanceof Pointer p) {
for (int i = 0; i < this.getPointerDepth(p); ++i) {
sb.append('*');
}
if (name != null) {
sb.append(' ');
}
functionPointerArrayType = this.getPointerBaseDataType(p);
}
if (!(functionPointerArrayType instanceof FunctionDefinition)) {
this.writer.append(this
.comment("Attempting output of invalid function pointer type declaration: " + originalType.getDisplayName()));
}
if (name != null) {
sb.append(name);
}
if (arrayDecorations.length() != 0) {
sb.append(arrayDecorations);
}
sb.append(")");
sb.append(this.getParameterListString(fd, false, writeEnabled, monitor));
DataType baseReturnType = this.getBaseDataType(returnType);
if (baseReturnType instanceof FunctionDefinition) {
return this.getFunctionPointerString((FunctionDefinition) baseReturnType, sb.toString(), returnType, writeEnabled,
monitor);
} else {
String var10000 = returnType.getDisplayName();
return var10000 + " " + sb.toString();
}
}
private String getParameterListString(FunctionDefinition fd, boolean includeParamNames, boolean writeEnabled,
TaskMonitor monitor) throws IOException, CancelledException {
StringBuilder buf = new StringBuilder();
buf.append("(");
boolean hasVarArgs = fd.hasVarArgs();
ParameterDefinition[] parameters = fd.getArguments();
int n = parameters.length;
for (int i = 0; i < n; ++i) {
ParameterDefinition param = parameters[i];
String paramName = includeParamNames ? param.getName() : null;
DataType dataType = param.getDataType();
if (writeEnabled) {
this.write(dataType, monitor);
}
String argument = this.getTypeDeclaration(paramName, dataType, param.getLength(), writeEnabled, monitor);
buf.append(argument);
if (i < n - 1 || hasVarArgs) {
buf.append(", ");
}
}
if (hasVarArgs) {
buf.append("...");
}
if (n == 0 && !hasVarArgs) {
buf.append(VoidDataType.dataType.getName());
}
buf.append(")");
return buf.toString();
}
}

View File

@@ -0,0 +1,33 @@
package re3lib;
import java.util.Hashtable;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.program.model.listing.Function;
import ghidra.util.task.TaskMonitor;
public class DecompileCache {
private static final int TIMEOUT = 10000;
Hashtable<Function, DecompileResults> cache = new Hashtable<>();
DecompInterface decomp;
TaskMonitor monitor;
public DecompileCache(DecompInterface decomp, TaskMonitor monitor) {
this.decomp = decomp;
this.monitor = monitor;
}
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;
}
}

View File

@@ -0,0 +1,247 @@
package re3lib;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class FunctionDatabase {
public enum Type {
Auto,
Fix,
Stub
}
public class Dependency implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public Address address;
public String name;
public Dependency(Address address, String name) {
this.address = address;
this.name = name;
}
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.writeObject(address != null ? address.toString() : null);
out.writeObject(name);
}
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
String addressString = (String) in.readObject();
if (addressString != null) {
address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString);
}
name = (String) in.readObject();
}
}
public class Entry implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public Address address;
public String name;
public File file;
public Type type;
public List<Dependency> dependencies = new ArrayList<>();
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.writeObject(address != null ? address.toString() : null);
out.writeObject(name);
out.writeObject(file != null ? file.toString() : null);
out.writeObject(type);
out.writeObject(dependencies);
}
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
String addressString = (String) in.readObject();
if (addressString != null) {
address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString);
}
name = (String) in.readObject();
String fileString = (String) in.readObject();
if (fileString != null) {
file = new File(fileString);
}
type = (Type) in.readObject();
dependencies = (List<Dependency>) in.readObject();
}
}
public List<Entry> entries = new ArrayList<>();
private File file;
private transient GhidraScript script;
public FunctionDatabase(GhidraScript script) {
this.script = script;
file = new File(RecompileConfig.INSTANCE.outputDir, "functions.dat");
}
public void load() throws Exception {
if (!file.exists()) {
return;
}
try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new java.io.FileInputStream(file))) {
entries = (List<Entry>) ois.readObject();
script.println("Loaded " + entries.size() + " function entries from " + file);
} catch (java.io.IOException | ClassNotFoundException e) {
script.println("Error loading function database: " + e.getMessage());
}
}
public void save() throws Exception {
try (java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(new java.io.FileOutputStream(file))) {
oos.writeObject(entries);
script.println("Saved " + entries.size() + " function entries to " + file);
} catch (java.io.IOException e) {
script.println("Error saving function database: " + e.getMessage());
}
}
public void add(Entry entry) {
entries.add(entry);
}
public void applyDefaultFilters(boolean rebuildAllGlobals) throws Exception {
GlobalDumper globalDumper = new GlobalDumper(script);
FunctionDumper dumper = new FunctionDumper(script, globalDumper);
if (rebuildAllGlobals) {
globalDumper.removeGlobalManifest();
}
boolean madeAnyChanges = false;
// Create a hash map to store symbol names
Map<Address, String> symbolNames = new HashMap<>();
Map<String, File> exportedFunctionNames = new HashMap<>();
for (Entry entry : entries) {
Function function = script.getFunctionAt(entry.address);
if (function != null) {
String dirComponent = entry.file.getParent().toString();
boolean isAuto = entry.type == Type.Auto;
boolean isFix = entry.type == Type.Fix;
// Get the actual symbol name and store it in the hash map
String symbolName = function.getName();
symbolNames.put(entry.address, symbolName);
if (isAuto && !exportedFunctionNames.containsKey(entry.name)) {
exportedFunctionNames.put(entry.name, entry.file);
} else if (isFix) {
exportedFunctionNames.replace(entry.name, entry.file);
}
}
}
// Print the number of symbol names collected
script.println("Collected " + symbolNames.size() + " symbol names");
boolean dryMode = false;
HashSet<Function> functionsToRegenerate = new HashSet<>();
Iterator<Entry> iterator = entries.iterator();
while (iterator.hasNext()) {
Entry entry = iterator.next();
Function function = script.getFunctionAt(entry.address);
boolean pendingDelete = false;
boolean pendingRegenerate = false;
if (rebuildAllGlobals) {
pendingRegenerate = true;
}
// Remove CRT and other blacklisted functions
if (function == null || !dumper.isValidFunction(function)) {
// Remove the file
if (entry.file != null && entry.file.exists()) {
script.println("Removed file: " + entry.file.getAbsolutePath());
pendingDelete = true;
}
// Remove entry from the list
script.println("Removed invalid function entry: " + entry.name + " at " + entry.address);
function = null;
}
// Check if symbol name matches the symbol name parsed from the file
if (function != null) {
String actualSymbolName = symbolNames.get(entry.address);
if (actualSymbolName == null) {
throw new Exception(
"Symbol name not found for function at " + entry.address + " in file " + entry.file.getAbsolutePath());
}
if (actualSymbolName != null && !actualSymbolName.equals(entry.name)) {
File fnExportedFile = exportedFunctionNames.get(entry.name);
if (fnExportedFile != null && fnExportedFile != entry.file) {
// Already exists elsewhere, so remove this file
script.println("Removing duplicate function: " + entry.name + " at " + entry.address + " overridden by "
+ fnExportedFile);
pendingDelete = true;
} else {
// Regeneral this function
script.println("Symbol name mismatch for function at " + entry.address + ": " +
"File name: " + entry.name + ", Actual symbol: " + actualSymbolName);
entry.name = actualSymbolName; // Update the entry name to match the actual symbol
pendingRegenerate = true;
}
}
// Check if dependencies are valid
for (Dependency dependency : entry.dependencies) {
Function depFunction = script.getFunctionAt(dependency.address);
if (depFunction == null) {
script.println(
"Dependency not found: " + dependency.name + " at " + dependency.address + " in " + entry.file);
pendingRegenerate = true;
} else if (!dumper.isValidFunction(depFunction) || !depFunction.getName().equals(dependency.name)) {
script
.println("Invalid dependency: " + dependency.name + " at " + dependency.address + " in " + entry.file
+ " should be " + dependency.name);
pendingRegenerate = true;
}
}
entry.name = actualSymbolName; // Update the entry name to match the actual symbol
madeAnyChanges = true;
}
if (pendingDelete) {
iterator.remove();
if (!dryMode) {
entry.file.delete();
madeAnyChanges = true;
}
} else if (pendingRegenerate && entry.type != Type.Stub) {
if (!dryMode) {
functionsToRegenerate.add(function);
madeAnyChanges = true;
}
}
}
for (Function function : functionsToRegenerate) {
script.println("Regenerating function: " + function.getName() + " at " + function.getEntryPoint());
dumper.dump(function);
}
if (madeAnyChanges) {
// Update CMake timestamp
RecompileConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();
TypeDumper typeDumper = new TypeDumper(script);
typeDumper.run();
}
}
}

View File

@@ -0,0 +1,328 @@
package re3lib;
import java.io.File;
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;
import java.util.regex.Pattern;
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.decompiler.PrettyPrinter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
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;
GlobalDumper globalDumper;
public HashSet<Address> functionAddrBlackList = new HashSet<>();
public boolean createdFile = false;
// Collects functions called by the current function
public HashSet<Function> functionReferences = new HashSet<>();
static final Pattern fieldAccessRegex = Pattern.compile("^_([0-9]+)_([0-9]+)_$");
public FunctionDumper(GhidraScript script, GlobalDumper globalDumper) {
this.script = script;
this.globalDumper = globalDumper;
initFunctionBlacklist();
}
boolean isValidFunction(Function function) {
if (functionAddrBlackList.contains(function.getEntryPoint()))
return false;
if (function.getComment() != null) {
if (function.getComment().startsWith("/i"))
return false;
if (function.getComment().startsWith("library function"))
return false;
}
if (function.getName().startsWith("crt_"))
return false;
return true;
}
void initFunctionBlacklist() {
functionAddrBlackList = Utils.loadFunctionBlacklist(RecompileConfig.INSTANCE.functionBlacklistPath);
// Build blacklist if not loaded
if (functionAddrBlackList == null) {
boolean modified = false;
Iterator<Function> functionsIt = script.getCurrentProgram().getFunctionManager().getFunctions(true).iterator();
while (functionsIt.hasNext()) {
Function function = functionsIt.next();
if (functionAddrBlackList.contains(function.getEntryPoint())) {
continue;
}
String comment = function.getComment();
boolean isIgnoredFunction = false;
if (comment != null && comment.contains("Library Function")) {
script.println("Adding library function " + function.getName() + " to blacklist");
script.println("ac:" + functionAddrBlackList.size() + " jj:"
+ functionAddrBlackList.contains(function.getEntryPoint()) + " "
+ function.getEntryPoint());
isIgnoredFunction = true;
}
if (function.getName().startsWith("crt_")) {
script.println("Adding crt function " + function.getName() + " to blacklist");
isIgnoredFunction = true;
}
if (isIgnoredFunction) {
// Decompile and trace
PCallTracer tracer = new PCallTracer();
tracer.setBlacklist(functionAddrBlackList);
tracer.traceCalls(function);
for (Function f : tracer.out) {
script.println(" Adding " + f.getName() + " to blacklist");
functionAddrBlackList.add(f.getEntryPoint());
modified = true;
}
}
}
if (modified) {
Utils.saveFunctionBlacklist(functionAddrBlackList, RecompileConfig.INSTANCE.functionBlacklistPath);
}
}
}
public static boolean isDumpedFix(Function function) {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx";
File f0 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
return f0.exists();
}
public static boolean isDumpedAuto(Function function) {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx";
File f0 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
return f0.exists();
}
public void dump(Function function)
throws Exception {
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(RecompileConfig.INSTANCE.dirDecompStub, fileName);
if (stubFile.exists()) {
script.println("Removing function stub " + stubFile);
stubFile.delete();
createdFile = true;
}
File f0 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
if (f0.exists()) {
script.println("Func " + function.getName() + " skipped (gh_fix)");
f0 = new File(RecompileConfig.INSTANCE.dirDecompRef, fileName);
} else {
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!!!!! ");
writer2.println();
writer2.println("#include <r3/binders/auto.h>");
writer2.println("#include <gh_global.h>");
writer2.println();
writer2.println("extern \"C\" {");
HighFunction highFunction = decompRes.getHighFunction();
HashSet<String> headers = new HashSet<>();
StringWriter codeWriter = new StringWriter();
PrettyPrinter pp = new PrettyPrinter(decompRes.getFunction(), decompRes.getCCodeMarkup(), null);
Iterator<ClangLine> lines = pp.getLines().iterator();
while (lines.hasNext()) {
ClangLine line = lines.next();
for (int i = 0; i < line.getIndent(); i++) {
codeWriter.write(' ');
}
List<ClangToken> tokens = new ArrayList<>();
// Parse preliminary line tokens
for (int i = 0; i < line.getNumTokens(); i++) {
ClangToken token = line.getToken(i);
if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall")
|| token.getText().equals("__stdcall")) {
// Remove function declaration
continue;
}
if (!token.getText().isEmpty())
tokens.add(token);
}
// Preprocess tokens
boolean prevDot = false;
for (int t = 0; t < tokens.size(); t++) {
ClangToken token = tokens.get(t);
boolean thisDot = false;
// script.println("Token: " + token.toString());
if (token.toString().equals(".")) {
// println("Found dot: " + token.toString() + " - " + token.getClass());
thisDot = true;
}
if (prevDot) {
// println("Possible field access: " + token.getText());
if (token instanceof ClangSyntaxToken) {
// Parse _4_4_ sub-access using regex
String text = token.getText();
Matcher matcher = fieldAccessRegex.matcher(text);
if (matcher.matches()) {
int offset = Integer.parseInt(matcher.group(1));
int size = Integer.parseInt(matcher.group(2));
// println("MATCHED: " + token.getText() + " - " + token.getSyntaxType() + " - "
// + token.getVarnode() + " - "
// + token.getPcodeOp());
// Replace tokens with + Field<offset, size>
ClangToken replacement = new ClangToken(token.Parent(), " + Field<" + offset + ", " + size + ">()");
tokens.remove(t);
tokens.remove(t - 1);
tokens.add(t - 1, replacement);
t--;
}
}
}
// Extract memory references
HighSymbol gsym = token.getHighSymbol(highFunction);
if (gsym != null) {
var symStorage = gsym.getStorage();
var sym = gsym.getSymbol();
Address address;
if (symStorage.isUnassignedStorage()) {
address = sym.getAddress();
} else {
address = gsym.getStorage().getMinAddress();
}
// Check if it's a function pointer, otherwise add to globals
if (address.isMemoryAddress()) {
// script.println("Address: " + address + " - " + sym.getName());
Function maybeFunction = script.getFunctionAt(address);
if (maybeFunction != null) {
externalFunctionCalls.add(maybeFunction);
} else {
script.println("Add globals " + address + " - " + gsym.getName() + " - " + token.getText());
globalDumper.addGlobal(address, gsym);
}
}
}
// Extract external function calls
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 = script.getFunctionAt(callAddr);
if (calledFunction != null) {
if (isValidFunction(calledFunction)) {
externalFunctionCalls.add(calledFunction);
}
}
}
}
prevDot = thisDot;
}
// Print tokens
for (int t = 0; t < tokens.size(); t++) {
ClangToken token = tokens.get(t);
codeWriter.write(token.toString());
}
codeWriter.write('\n');
}
for (Function externalFunction : externalFunctionCalls) {
String proto = externalFunction.getSignature().getPrototypeString(false);
headers.add("" + proto
+ "; // " + externalFunction.getEntryPoint() + " // "
+ externalFunction.getName());
}
for (String header : headers) {
writer2.println(header);
}
writer2.println();
writer2.print("// " + function.getEntryPoint());
writer2.print(codeWriter.toString());
writer2.println("}");
writer2.println();
}
// Possibly generate stubs for external functions
for (Function externalFunction : externalFunctionCalls) {
String sanitizedExtFunctionName = Utils.sanitizeIdentifier(externalFunction.getName());
fileName = sanitizedExtFunctionName + ".cxx";
File f2 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
File f3 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
if (f2.exists() || f3.exists()) {
// script.println("Skipping external function: " + externalFunction.getName() +
// " - " + externalFunction.getEntryPoint());
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 <r3/binders/auto.h>");
writer2.println("#include <gh_global.h>");
writer2.println("#include <stdexcept>");
writer2.println();
writer2.println("// " + externalFunction.getEntryPoint());
writer2.println("// " + externalFunction.getName());
writer2.println("extern \"C\" " + externalFunction.getSignature().getPrototypeString(false) + " {");
writer2.println(" // TODO: Implement this function");
writer2
.println(" throw GHStubException(\"Function not implemented: " + externalFunction.getName() + "\");");
writer2.println("}");
}
if (!f4.exists()) {
createdFile = true;
}
}
}
}

View File

@@ -0,0 +1,289 @@
package re3lib;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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;
import ghidra.program.model.data.AbstractStringDataType;
import ghidra.program.model.data.Array;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.DataTypePath;
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;
public class GlobalDumper {
public class GlobalRec {
public Address address;
public String name;
public DataType type;
public GlobalRec(Address address, String name, DataType type) {
this.address = address;
this.name = name;
this.type = type;
}
};
GhidraScript script;
File manifestFile;
HashMap<Address, GlobalRec> globalAddrs = new HashMap<>();
public GlobalDumper(GhidraScript script) {
this.script = script;
manifestFile = new File(RecompileConfig.INSTANCE.outputDir, "globals.txt");
}
public void removeGlobalManifest() {
if (manifestFile.exists()) {
manifestFile.delete();
}
}
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
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(RecompileConfig.INSTANCE.staticMemoryBlockStart) < 0
|| addr.compareTo(RecompileConfig.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);
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));
}
String escapeCString(String str) {
str = str.replace("\\", "\\\\");
str = str.replace("\"", "\\\"");
str = str.replace("\n", "\\n");
str = str.replace("\r", "\\r");
str = str.replace("\t", "\\t");
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 = script.getCurrentProgram().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();
}
public void dumpGlobals() throws Exception {
File globalSymbolsListH = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.h");
PrintWriter hwriter = new PrintWriter(globalSymbolsListH, "UTF-8");
hwriter.println("// AUTO-GENERATED FILE ");
Utils.headerGuardPre(hwriter, "GLOBALS");
hwriter.println("#include <r3/binders/global.h>");
hwriter.println();
File globalSymbolsListC = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.cxx");
PrintWriter cwriter = new PrintWriter(globalSymbolsListC, "UTF-8");
cwriter.println("// AUTO-GENERATED FILE ");
cwriter.println("#include <r3/binders/global.h>");
hwriter.println();
List<GlobalRec> globals = new ArrayList<>(globalAddrs.values());
globals.sort((o1, o2) -> o1.address.compareTo(o2.address));
for (GlobalRec global : globals) {
DataType dt = global.type;
if (dt == null) {
script.println("WARNING: Missing type for global: " + global.name + " at " + global.address);
continue;
}
String dataType = dt.getDisplayName();
String sanitizedName = Utils.sanitizeIdentifier(global.name);
String name = global.name;
Address addr = global.address;
if (!sanitizedName.equals(name)) {
script.println("WARNING: Invalid global symbol name: " + name);
name = sanitizedName;
}
// println("Symbol: " + symbol + " Addr: " + addr + " Size:" + symSize + " " +
// storage.getSerializationString());
try {
String initBlk = " = ";
boolean fullyDefinedType = false;
if (dt instanceof AbstractStringDataType) {
AbstractStringDataType sdt = (AbstractStringDataType) dt;
dataType = "const char*";
// String type
initBlk += "\"" + escapeCString(readCString(addr, 2048)) + "\"";
fullyDefinedType = true;
} else if (dt instanceof PointerDataType) {
PointerDataType pdt = (PointerDataType) dt;
DataType baseType = pdt.getDataType();
dataType = baseType.getDisplayName() + "*";
initBlk += "(" + dataType + ")&GH_MEM(0x" + addr + ")";
fullyDefinedType = true;
}
String linkagePrefix = "extern ";
if (fullyDefinedType) {
hwriter.println(linkagePrefix + dataType + " " + name + "; // " + addr);
cwriter.println(dataType + " " + name + initBlk + "; // " + addr);
} else {
if (dt instanceof Array) {
// println("Array: " + dt.getDisplayName() + " - " + addr + " - " +
// dt.getClass().getSimpleName());
Array adt = (Array) dt;
DataType baseType = adt.getDataType();
hwriter.println(
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(linkagePrefix + refTypeStr + " " + name + "; // " + addr);
cwriter.println(refTypeStr + " " + name + "= (" + refTypeStr + ") GH_MEM(0x" + addr + ");");
}
}
} catch (Exception e) {
script.println("Error processing global symbol: " + e);
script.println("Symbol: " + name + " - " + addr);
}
}
Utils.headerGuardPost(hwriter, "GLOBALS");
hwriter.close();
cwriter.close();
}
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!");
}
}
if (manifestFile.exists()) {
if (!manifestFile.renameTo(backupFile))
throw new Exception("Failed to rename manifest file: " + manifestFile + ", globals will not be saved!");
}
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());
}
}
}
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

@@ -0,0 +1,194 @@
package re3lib;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.PcodeOpAST;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.Reference;
public class PCallTracer {
public class QueueItem {
Function function;
int depth;
QueueItem(Function function, int depth) {
this.function = function;
this.depth = depth;
}
}
public List<Function> out = new ArrayList<>();
public boolean trace = false;
List<QueueItem> queue = new ArrayList<>();
HashSet<Address> visited = new HashSet<>();
// DecompileCache decomp;
GhidraScript script;
Program program;
public PCallTracer() {
this.script = RecompileConfig.INSTANCE.script;
this.program = this.script.getCurrentProgram();
// this.decomp = RecompileConfig.INSTANCE.decompCache;
}
public void setBlacklist(HashSet<Address> blacklist) {
this.visited = new HashSet<>(blacklist);
}
// public List<Address> getReferenceFromAddresses(Address address) {
// Reference[] referencesFrom = script.getReferencesFrom(address);
// // get only the address references at the given address (ie no stack refs,
// ...)
// List<Address> refFromAddresses = new ArrayList<Address>();
// for (Reference referenceFrom : referencesFrom) {
// if (referenceFrom.isMemoryReference()) {
// refFromAddresses.add(referenceFrom.getToAddress());
// }
// }
// return refFromAddresses;
// }
public Function getReferencedFunction(Address address, boolean getThunkedFunction) {
Reference[] referencesFrom = script.getReferencesFrom(address);
if (referencesFrom.length == 0) {
return null;
}
for (Reference referenceFrom : referencesFrom) {
Address referencedAddress = referenceFrom.getToAddress();
if (referencedAddress == null) {
continue;
}
Function function = script.getFunctionAt(referencedAddress);
if (function == null) {
continue;
}
if (!getThunkedFunction) {
return function;
}
if (function.isThunk()) {
function = function.getThunkedFunction(true);
}
return function;
}
return null;
// List<Address> referencesFrom = getReferenceFromAddresses(address);
// if (referencesFrom.size() != 1) {
// return null;
// }
// Address functionAddress = referencesFrom.get(0);
// Register lowBitCodeMode = program.getRegister("LowBitCodeMode");
// if (lowBitCodeMode != null) {
// long longValue = functionAddress.getOffset();
// longValue = longValue & ~0x1;
// functionAddress = functionAddress.getNewAddress(longValue);
// }
// Function function = script.getFunctionAt(functionAddress);
// if (function == null) {
// // try to create function
// function = script.createFunction(functionAddress, null);
// if (function == null) {
// return null;
// }
// }
// // if function is a thunk, get the thunked function
// if (function.isThunk()) {
// Function thunkedFunction = function.getThunkedFunction(true);
// function = thunkedFunction;
// }
// return function;
}
void visitInstructions(Function function, int depth) {
Set<Function> calledFunctions = new HashSet<Function>();
InstructionIterator instructions = script.getCurrentProgram()
.getListing()
.getInstructions(function.getBody(), true);
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
if (instruction.getFlowType().isCall()) {
FunctionManager functionManager = script.getCurrentProgram().getFunctionManager();
Function calledFunction = getReferencedFunction(instruction.getMinAddress(), true);
if (calledFunction == null) {
continue;
}
if (!visited.contains(calledFunction.getEntryPoint())) {
queue.add(new QueueItem(calledFunction, depth + 1));
}
}
}
// Iterator<PcodeOpAST> opIter = highFunction.getPcodeOps();
// while (opIter.hasNext()) {
// PcodeOpAST op = opIter.next();
// if (op.getOpcode() == PcodeOp.CALL) {
// Varnode target = op.getInput(0);
// if (target.isAddress()) {
// Address callAddr = target.getAddress();
// Function calledFunction = script.getFunctionAt(callAddr);
// if (calledFunction == null) {
// script.println("PCallTracer, called function not found: " + op.toString() + "
// - "
// + highFunction.getFunction().getName());
// continue;
// }
// if (!visited.contains(calledFunction.getEntryPoint())) {
// queue.add(new QueueItem(calledFunction, depth + 1));
// }
// }
// }
// }
}
void visit(Function function, int depth) {
if (!visited.contains(function.getEntryPoint())) {
visited.add(function.getEntryPoint());
if (trace) {
script.println("PCallTracer, visiting " + function.getName() + " (depth:" + depth + ")");
}
// DecompileResults decompRes = decomp.getOrInsert(function);
visitInstructions(function, depth);
out.add(function);
}
}
public void traceCalls(Function inFunction) {
queue.add(new QueueItem(inFunction, 0));
while (queue.size() > 0) {
QueueItem item = queue.remove(0);
visit(item.function, item.depth);
}
}
}

View File

@@ -0,0 +1,97 @@
package re3lib;
import java.io.File;
import java.io.IOException;
import generic.jar.ResourceFile;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.script.GhidraScript;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
public class RecompileConfig {
private static final String RECOMPILE_PREFIX = "game_re";
// Version control project root
public final String rootDir;
// The output directory for the recompiled game
public final String outputDir;
public final String typeBlacklistPath;
public final String categoryPathBlacklistPath;
public final String functionBlacklistPath;
// The static memory block
public final Address staticMemoryBlockStart;
public final Address staticMemoryBlockEnd;
// The automatically decompiled files
public final File dirDecompAuto;
// 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
// 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;
public final GhidraScript script;
public static RecompileConfig INSTANCE;
public RecompileConfig(GhidraScript script) {
staticMemoryBlockStart = script.getCurrentProgram().getAddressFactory().getAddress("00597000");
staticMemoryBlockEnd = script.getCurrentProgram().getAddressFactory().getAddress("00843fff");
this.script = script;
rootDir = new File(script.getSourceFile().getAbsolutePath()).getParentFile().getParentFile().toString();
outputDir = new File(rootDir, RECOMPILE_PREFIX).toString();
script.println("Output path: " + outputDir);
typeBlacklistPath = new File(outputDir, "type_blacklist.txt").toString();
categoryPathBlacklistPath = new File(outputDir, "type_path_blacklist.txt").toString();
functionBlacklistPath = new File(outputDir, "function_blacklist.txt").toString();
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();
DecompInterface decomp = new DecompInterface();
decomp.openProgram(currentProgram);
decompCache = new DecompileCache(decomp, script.getMonitor());
}
public void createDirectories() {
dirDecompAuto.mkdirs();
dirDecompFix.mkdirs();
dirDecompRef.mkdirs();
dirDecompStub.mkdirs();
}
public void touchCMakeTimestamp() {
try {
if (!cmakeTimestampFile.exists()) {
cmakeTimestampFile.createNewFile();
}
cmakeTimestampFile.setLastModified(System.currentTimeMillis());
} catch (IOException e) {
}
}
}

View File

@@ -0,0 +1,108 @@
package re3lib;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.EnumDataType;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.TypeDef;
import ghidra.program.model.data.TypedefDataType;
import ghidra.program.model.listing.Program;
public class TypeDumper {
Program currentProgram;
GhidraScript script;
public TypeDumper(GhidraScript script) {
this.script = script;
currentProgram = script.getCurrentProgram();
RecompileConfig.INSTANCE = new RecompileConfig(script);
}
public void run() throws Exception {
ProgramBasedDataTypeManager dtm = currentProgram.getDataTypeManager();
HashSet<String> typeBlacklist = Utils.loadSimpleBlacklist(RecompileConfig.INSTANCE.typeBlacklistPath);
HashSet<String> categoryPathBlacklist = Utils
.loadSimpleBlacklist(RecompileConfig.INSTANCE.categoryPathBlacklistPath);
if (typeBlacklist == null) {
script.println("Building struct blacklist from existing data types");
typeBlacklist = new HashSet<>();
Iterator<DataType> it = dtm.getAllDataTypes();
while (it.hasNext()) {
DataType dt = it.next();
if (dt instanceof Structure || dt instanceof TypedefDataType) {
typeBlacklist.add(dt.getDisplayName());
}
}
Utils.saveStructBlacklist(typeBlacklist, RecompileConfig.INSTANCE.typeBlacklistPath);
}
List<DataType> filteredTypes = new ArrayList<>();
// Iterator<DataType> compIt = dtm.getAllDataTypes();
// while (compIt.hasNext()) {
// DataType comp = compIt.next();
// // script.println("Found: " + comp.getDisplayName() + " - " +
// // comp.getClass().getSimpleName());
// if (comp instanceof TypeDef) {
// if (comp.getDisplayName().startsWith("FIL_")) {
// script.println("Found: " + comp.getDisplayName() + " - " + comp.getName() + "
// - " + comp.getClass().getSimpleName());
// }
// }
// // if (comp.getName() == "FIL_tdstConcatFile") {
// // // script.println("Found: " + dt.getDisplayName() + " - " +
// // // dt.getClass().getSimpleName());
// // throw new Exception("Found: " + comp.getDisplayName() + " - " +
// comp.getClass().getSimpleName());
// // }
// }
Iterator<DataType> it = dtm.getAllDataTypes();
while (it.hasNext()) {
DataType dt = it.next();
if (typeBlacklist.contains(dt.getDisplayName()))
continue;
CategoryPath catPath = dt.getCategoryPath();
if (catPath.getPathElements().length > 0 && categoryPathBlacklist.contains(catPath.getPathElements()[0]))
continue;
// script.println("Type: " + dt.getDisplayName() + " - CatPath: " + dt.getCategoryPath());
// if (dt.getName().equals("ImageBaseOffset32"))
// throw new Exception("Found: " + dt.getDisplayName() + " - " + catPath.getPathElements()[0] + " - " + dt.getClass().getSimpleName());
if (dt instanceof Structure || dt instanceof TypeDef || dt instanceof EnumDataType) {
// script.println("Adding: " + dt.getDisplayName() + " - " +
// dt.getClass().getSimpleName());
filteredTypes.add(dt);
}
}
try (PrintWriter writer = new PrintWriter(new File(RecompileConfig.INSTANCE.outputDir, "gh_types.h"),
"UTF-8")) {
Utils.headerGuardPre(writer, "STRUCTS");
writer.println("// AUTO-GENERATED FILE ");
writer.println("#include <r3/binders/type.h>");
DataTypeWriter dtw = new DataTypeWriter(dtm, writer);
dtw.blacklistedTypes = typeBlacklist;
dtw.write(filteredTypes, script.getMonitor());
Utils.headerGuardPost(writer, "STRUCTS");
}
}
}

View File

@@ -0,0 +1,84 @@
package re3lib;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
public class Utils {
public static void headerGuardPre(PrintWriter writer, String tag) {
writer.println("#ifndef GH_GENERATED_" + tag + "_H");
writer.println("#define GH_GENERATED_" + tag + "_H");
writer.println();
}
public static void headerGuardPost(PrintWriter writer, String tag) {
writer.println("#endif // GH_GENERATED_" + tag + "_H");
}
public static String sanitizeIdentifier(String name) {
return name.replaceAll("[^a-zA-Z0-9_]", "_");
}
public static HashSet<String> loadSimpleBlacklist(String path) {
File file = new File(path);
HashSet<String> 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;
}
public static void saveStructBlacklist(HashSet<String> structBlacklist, String path) throws Exception {
String[] arr = structBlacklist.toArray(new String[0]);
Arrays.sort(arr);
File file = new File(path);
try (PrintWriter writer = new PrintWriter(file)) {
for (String structName : arr) {
writer.println(structName);
}
}
}
public static HashSet<Address> loadFunctionBlacklist(String path) {
GhidraScript script = RecompileConfig.INSTANCE.script;
HashSet<Address> fnBlacklist = new HashSet<>();
File blacklistFile = new File(path);
try (Scanner scanner = new Scanner(blacklistFile)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// Strip comment
String line1 = line.split("//")[0].trim();
// Deserialize address
Address addr = RecompileConfig.INSTANCE.currentProgram.getAddressFactory().getAddress(line1);
fnBlacklist.add(addr);
}
script.println("Loaded blacklist with " + fnBlacklist.size() + " entries");
} catch (FileNotFoundException e) {
script.println("No blacklist found");
}
return fnBlacklist;
}
public static void saveFunctionBlacklist(HashSet<Address> fnBlacklist, String path) {
GhidraScript script = RecompileConfig.INSTANCE.script;
File blacklistFile = new File(path);
try (PrintWriter writer = new PrintWriter(blacklistFile)) {
for (Address addr : fnBlacklist) {
writer.println(addr.toString() + " // " + script.getFunctionAt(addr).getName());
}
} catch (FileNotFoundException e) {
script.println("Error saving blacklist: " + e.getMessage());
}
}
}

3
java/ghidra/readme.md Normal file
View File

@@ -0,0 +1,3 @@
# Ghidra Scripts
Add this to your scripts folder and run to generate c code for all functions in the project