diff --git a/ghidra/delink.sh b/ghidra/delink.sh index 2df52e2..9b14b9b 100755 --- a/ghidra/delink.sh +++ b/ghidra/delink.sh @@ -65,8 +65,11 @@ delink() { export MSYS_NO_PATHCONV=1 "$1/support/analyzeHeadless$suffix" "$2" "$3"\ + -readOnly\ -process default.xbe\ -noanalysis\ + -scriptPath ghidra_scripts\ + -preScript MSVC7Mangle.java $4\ -postScript DelinkProgram.java\ /exporter 'COFF relocatable object'\ $(printf "/include-range %s " $4)\ diff --git a/ghidra/ghidra_scripts/MSVC7Mangle.java b/ghidra/ghidra_scripts/MSVC7Mangle.java index ff13d16..718ca2b 100644 --- a/ghidra/ghidra_scripts/MSVC7Mangle.java +++ b/ghidra/ghidra_scripts/MSVC7Mangle.java @@ -7,17 +7,20 @@ // permissive form (public, non-const, etc.). // // Special symbol names like "operator new" or "scalar deleting destructor" -// are given unique mangling. To properly demangle these, name them as they +// are given unique mangling. To properly mangle these, name them as they // appear in objdiff, replacing spaces with underscores, e.g. "operator_new" // and "`scalar_deleting_destructor'" (notice the ` and '). // +// This script can be called in headless mode with the address ranges to mangle +// as arguments, e.g. 0x1234-0x5678. Any symbols referenced by functions being +// mangled will also be mangled in this mode (so that the references are +// correct if the mangling is done in preparation for exporting functions). +// // @category Symbol import ghidra.app.script.GhidraScript; import ghidra.program.model.address.Address; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.FunctionSignature; +import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.Array; import ghidra.program.model.data.BooleanDataType; import ghidra.program.model.data.CharDataType; @@ -38,6 +41,7 @@ import ghidra.program.model.data.StringDataInstance; import ghidra.program.model.data.Structure; import ghidra.program.model.data.TerminatedUnicodeDataType; import ghidra.program.model.data.TypeDef; +import ghidra.program.model.data.Undefined; import ghidra.program.model.data.Union; import ghidra.program.model.data.UnsignedCharDataType; import ghidra.program.model.data.UnsignedIntegerDataType; @@ -46,6 +50,9 @@ import ghidra.program.model.data.UnsignedLongLongDataType; import ghidra.program.model.data.UnsignedShortDataType; import ghidra.program.model.data.VoidDataType; import ghidra.program.model.data.WideCharDataType; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.FunctionSignature; import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.SourceType; @@ -65,6 +72,22 @@ import java.util.zip.CRC32; public class MSVC7Mangle extends GhidraScript{ @Override public void run() throws Exception { + // Get selected ranges from arguments if invoked headless + if (isRunningHeadless()) { + final String[] args = getScriptArgs(); + final AddressSet addr = new AddressSet(); + + for (int i = 0; i < args.length; i++) { + final String[] range = args[i].split("-"); + addr.add( + currentAddress.getAddress(range[0]), + currentAddress.getAddress(range[1]) + ); + } + + setCurrentSelection(addr); + } + final SymbolIterator iter = currentProgram.getSymbolTable() .getPrimarySymbolIterator(currentSelection, true); @@ -86,6 +109,9 @@ public class MSVC7Mangle extends GhidraScript{ s.setName(mangled, SourceType.USER_DEFINED); makeGlobal(s); } + + // TODO: in headless mode, also mangle everything + // referenced by functions } } @@ -238,24 +264,29 @@ public class MSVC7Mangle extends GhidraScript{ } private boolean isVirtual(final Function f) { - /* Attempt to determine whether a method is virtual - We essentially try to figure out if any references are from a vtable - by checking if they lie in non-executable memory, or from a scalar - deleting destructor. + /* Determine whether a method is virtual + We essentially check whether any references are from a vtable or a + scalar deleting destructor. */ final Reference[] refs = getReferencesTo(f.getEntryPoint()); for (int i = 0; i < refs.length; i++) { - final Address addr = refs[i].getFromAddress(); - final Optional caller = Optional.ofNullable(getFunctionContaining(addr)) - .map(x -> x.getName(false)); + final Data data = getDataContaining (refs[i].getFromAddress()); + final Function func = getFunctionContaining(refs[i].getFromAddress()); - if ( - !getMemoryBlock(addr).isExecute() || - caller.map(x -> x.equals("`scalar_deleting_destructor'")) - .orElse(false) || - caller.map(x -> x.startsWith("??_G")) // From mangled name - .orElse(false) - ) return true; + if (data != null) { + final String name = getSymbolAt(data.getRoot() + .getAddress()).getName(false); + if ( + name.equals("`vftable'") || + name.startsWith("??_7") + ) return true; + } else if (func != null) { + final String name = func.getName(false); + if ( + name.equals("`scalar_deleting_destructor'") || + name.startsWith("??_G") + ) return true; + } } return false; @@ -305,7 +336,7 @@ public class MSVC7Mangle extends GhidraScript{ */ if (t == null) throw new Exception ( "A data type was reported as null. Ensure that all " + - "data types in the demangled code/data have been " + + "data types in the code/data to mangle have been " + "defined." ); @@ -336,7 +367,8 @@ public class MSVC7Mangle extends GhidraScript{ case Array a -> "PA" + mangleArrDims(a) + mangleType(arrType(a), dict); case FunctionSignature f -> mangleFnType(f, dict); case TypeDef d -> mangleType(d.getBaseDataType(), dict); - case DefaultDataType _ -> throw new Exception ("Encountered data marked \"undefined\". All data types must be defined."); + case DefaultDataType _ -> throw new Exception ("Encountered data marked \"undefined\". Ensure that all data types in the code/data to mangle have been defined."); + case Undefined _ -> throw new Exception ("Encountered data marked \"undefined\". Ensure that all data types in the code/data to mangle have been defined."); default -> throw new Exception ("Unknown type \"" + t.getClass().getName() + "\""); }; }