diff --git a/ghidra/ghidra_scripts/EnhancedExport.java b/ghidra/ghidra_scripts/EnhancedExport.java index de693af..e8f294a 100644 --- a/ghidra/ghidra_scripts/EnhancedExport.java +++ b/ghidra/ghidra_scripts/EnhancedExport.java @@ -1,17 +1,19 @@ -// TODO +// Writes user-defined data and function symbols to a specified TSV file for +// re-import by the EnhancedImport script. // // @category Export import ghidra.app.script.GhidraScript; -import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Function; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolIterator; import java.io.FileWriter; import java.util.Arrays; +import java.util.Optional; public class EnhancedExport extends GhidraScript{ @@ -19,41 +21,18 @@ public class EnhancedExport extends GhidraScript{ public void run() throws Exception { final FileWriter out = new FileWriter(askFile("Specify output file", "OK")); - final SymbolIterator iter = currentProgram.getSymbolTable() - .getPrimarySymbolIterator(true); - while (iter.hasNext() && !monitor.isCancelled()) { - final Symbol s = iter.next(); + for (final Symbol s : currentProgram.getSymbolTable() + .getPrimarySymbolIterator(true)) { if (s.getSource() != SourceType.USER_DEFINED) continue; final Object obj = s.getObject(); if (obj != null) switch (obj) { case Data d: - out.write( - "0x" + s.getAddress().toString() + "\t" + - "d" + "\t" + - d.getDataType().getDisplayName() + "\t" + - s.getName(true) + "\n" - ); + outputData(s.getAddress(), d.getDataType(), s.getName(true), out); break; case Function f: - out.write( - "0x" + s.getAddress().toString() + "\t" + - "f" + "\t" + - f.getSignature(true).getReturnType() - .getDisplayName() + "\t" + - f.getCallingConventionName() + "\t" + - f.getName(true) + - String.join( - "\t", - Arrays.stream(f.getSignature(true).getArguments()) - .map(arg -> "\t" + - arg.getDataType().getDisplayName() + "\t" + - arg.getName() - ).toArray(String[]::new) - ) + - (f.hasVarArgs() ? "\t..." : "") + "\n" - ); + outputFunc(s.getAddress(), f, out); break; default: {} @@ -62,4 +41,46 @@ public class EnhancedExport extends GhidraScript{ out.close(); } + + private static void outputData( + final Address addr, + final DataType type, + final String name, + final FileWriter out + ) throws Exception { + out.write( + "0x" + addr.toString() + "\t" + + "data" + "\t" + + type.getDisplayName() + "\t" + + name + "\n" + ); + } + + private static void outputFunc( + final Address addr, + final Function f, + final FileWriter out + ) throws Exception { + out.write( + "0x" + addr.toString() + "\t" + + "func" + "\t" + + f.getSignature(true).getReturnType() + .getDisplayName() + "\t" + + f.getCallingConventionName() + "\t" + + (f.isInline() ? "inline" : "notinline") + "\t" + + Optional.ofNullable(f.getCallFixup()) + .orElse("nofixup") + "\t" + + f.getName(true) + + String.join( + "\t", + Arrays.stream(f.getSignature(true) + .getArguments()) + .map(arg -> + "\t" + arg.getDataType().getDisplayName() + + "\t" + arg.getName() + ).toArray(String[]::new) + ) + + (f.hasVarArgs() ? "\t..." : "") + "\n" + ); + } } diff --git a/ghidra/ghidra_scripts/EnhancedImport.java b/ghidra/ghidra_scripts/EnhancedImport.java new file mode 100644 index 0000000..3bdf040 --- /dev/null +++ b/ghidra/ghidra_scripts/EnhancedImport.java @@ -0,0 +1,102 @@ +// Imports data from a file produced by the EnhancedExport script. +// +// @category Import + +import ghidra.app.script.GhidraScript; +import ghidra.app.services.DataTypeQueryService; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.symbol.Namespace; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.Symbol; + +import java.io.FileReader; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + + +public class EnhancedImport extends GhidraScript{ + @Override + public void run() throws Exception { + final FileReader in = new FileReader(askFile("Select input file", "OK")); + final List lines = in.readAllLines(); + in.close(); + + for (int i = 0; i < lines.size(); i++) { + final String[] parts = lines.get(i).split("\t");; + final Address addr = toAddr(parts[0]); + + switch (parts[1]) { + case "data": importData(addr, parts); break; + case "func": importFunc(addr, parts); break; + default: throw new Exception( + "Symbol type \"" + parts[1] + + "\" on line " + Integer.toString(i) + + " is not \"data\" or \"func\"" + ); + } + } + } + + private void importData( + final Address addr, + final String[] parts + ) throws Exception { + final String name = unqualified(parts[3]); + + print("Importing data symbol \"" + parts[3] + "\"..."); + + // Create symbol + final Namespace ns = importNamespace(parts[3]); + final Symbol s = Optional.ofNullable(getSymbolAt(addr)) + .orElse(createLabel(addr, name, ns, true, SourceType.USER_DEFINED)); + + // Create data (TODO: parse type name to get base name, then pass base type to PointerDataType and ArrayDataType constructors to create final type) + final List foundTypes = state.getTool() + .getService(DataTypeQueryService.class) + .findDataTypes(parts[2], null); + if (foundTypes.size() == 0) { + println(" can't find data type \"" + parts[2] + "\", skipping."); + return; + } + + final DataType t = foundTypes.get(0); // Boldly assume first is right + + clearListing(addr, addr.add(Math.max(t.getLength(), 1) - 1)); + currentProgram.getListing().createData(addr, t); + + println(" done."); + } + + private static String unqualified(final String qualifiedName) { + /* Strips the namespaces off of a qualified name */ + final String[] parts = qualifiedName.split("::"); + return parts[parts.length - 1]; + } + + private Namespace importNamespace(final String qualifiedName) throws Exception { + /* Creates namespaces from the given name, returning the deepest one + Returns null if the qualified name is in the global namespace. + */ + final String[] parts = qualifiedName.split("::"); + + if (parts.length < 2) return null; + + final String[] names = Arrays.copyOfRange(parts, 0, parts.length - 1); + + Namespace ns = null; + for (final String name : names) ns = createNamespace(ns, name); + + return ns; + } + + private void importFunc( + final Address addr, + final String[] parts + ) throws Exception { + println("TODO: function \"" + parts[6] + "\""); + } +} diff --git a/ghidra/ghidra_scripts/MSVC7Mangle.java b/ghidra/ghidra_scripts/MSVC7Mangle.java index b62ed52..7bf4ab5 100644 --- a/ghidra/ghidra_scripts/MSVC7Mangle.java +++ b/ghidra/ghidra_scripts/MSVC7Mangle.java @@ -59,7 +59,6 @@ import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolIterator; import java.util.Arrays; import java.util.ArrayList; @@ -86,12 +85,8 @@ public class MSVC7Mangle extends GhidraScript{ setCurrentSelection(addr); } - final SymbolIterator iter = currentProgram.getSymbolTable() - .getPrimarySymbolIterator(currentSelection, true); - - while (iter.hasNext() && !monitor.isCancelled()) { - final Symbol s = iter.next(); - + for (final Symbol s : currentProgram.getSymbolTable() + .getPrimarySymbolIterator(currentSelection, true)) { mangle(s); // Also mangle everything referenced inside functions @@ -303,9 +298,9 @@ public class MSVC7Mangle extends GhidraScript{ scalar deleting destructor. */ final Reference[] refs = getReferencesTo(f.getEntryPoint()); - for (int i = 0; i < refs.length; i++) { - final Data data = getDataContaining (refs[i].getFromAddress()); - final Function func = getFunctionContaining(refs[i].getFromAddress()); + for (final Reference ref : refs) { + final Data data = getDataContaining (ref.getFromAddress()); + final Function func = getFunctionContaining(ref.getFromAddress()); if (data != null) { final Symbol s = getSymbolAt(data.getRoot() @@ -481,12 +476,12 @@ public class MSVC7Mangle extends GhidraScript{ // freaks out at is actually useful (and Optional still // helped us out here) String mangledArgs = ""; - for (int i = 0; i < args.length; i++) { - final String mangledArg = mangleType(args[i], dict, loc); + for (final DataType arg : args) { + final String mangledArg = mangleType(arg, dict, loc); mangledArgs += mangledArg.length() == 1 ? mangledArg : - backref(args[i], argDict).orElse(mangledArg); + backref(arg, argDict).orElse(mangledArg); } return mangledArgs + (f.hasVarArgs() ? "Z" : "@"); } @@ -592,8 +587,8 @@ public class MSVC7Mangle extends GhidraScript{ ins = ins.getNext() ) { final Reference[] refs = ins.getReferencesFrom(); - for (int i = 0; i < refs.length; i++) { - final Symbol symbol = getSymbolAt(refs[i].getToAddress()); + for (final Reference ref : refs) { + final Symbol symbol = getSymbolAt(ref.getToAddress()); // Guard against spurious references to nonexisting things if ( @@ -605,10 +600,10 @@ public class MSVC7Mangle extends GhidraScript{ d.getBaseDataType() instanceof Undefined || d.getBaseDataType() instanceof DefaultDataType ) && - refs[i].getSource() != SourceType.USER_DEFINED + ref.getSource() != SourceType.USER_DEFINED ) ) { - removeReference(refs[i]); + removeReference(ref); continue; }