Compare commits

...

3 commits

Author SHA1 Message Date
KeybadeBlox
0922356a40 Begin Ghidra symbol import script 2026-02-16 00:21:55 -05:00
KeybadeBlox
e0c9fc4c83 Use enhanced for loops in Ghidra mangler script 2026-02-16 00:21:22 -05:00
KeybadeBlox
1e8ae0f72f Finish Ghidra symbol export script 2026-02-16 00:20:57 -05:00
3 changed files with 165 additions and 47 deletions

View file

@ -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 // @category Export
import ghidra.app.script.GhidraScript; 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.Data;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import java.io.FileWriter; import java.io.FileWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional;
public class EnhancedExport extends GhidraScript{ public class EnhancedExport extends GhidraScript{
@ -19,41 +21,18 @@ public class EnhancedExport extends GhidraScript{
public void run() throws Exception { public void run() throws Exception {
final FileWriter out = new FileWriter(askFile("Specify output file", "OK")); final FileWriter out = new FileWriter(askFile("Specify output file", "OK"));
final SymbolIterator iter = currentProgram.getSymbolTable() for (final Symbol s : currentProgram.getSymbolTable()
.getPrimarySymbolIterator(true); .getPrimarySymbolIterator(true)) {
while (iter.hasNext() && !monitor.isCancelled()) {
final Symbol s = iter.next();
if (s.getSource() != SourceType.USER_DEFINED) continue; if (s.getSource() != SourceType.USER_DEFINED) continue;
final Object obj = s.getObject(); final Object obj = s.getObject();
if (obj != null) switch (obj) { if (obj != null) switch (obj) {
case Data d: case Data d:
out.write( outputData(s.getAddress(), d.getDataType(), s.getName(true), out);
"0x" + s.getAddress().toString() + "\t" +
"d" + "\t" +
d.getDataType().getDisplayName() + "\t" +
s.getName(true) + "\n"
);
break; break;
case Function f: case Function f:
out.write( outputFunc(s.getAddress(), f, out);
"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"
);
break; break;
default: {} default: {}
@ -62,4 +41,46 @@ public class EnhancedExport extends GhidraScript{
out.close(); 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"
);
}
} }

View file

@ -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<String> 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<DataType> 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] + "\"");
}
}

View file

@ -59,7 +59,6 @@ import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import java.util.Arrays; import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
@ -86,12 +85,8 @@ public class MSVC7Mangle extends GhidraScript{
setCurrentSelection(addr); setCurrentSelection(addr);
} }
final SymbolIterator iter = currentProgram.getSymbolTable() for (final Symbol s : currentProgram.getSymbolTable()
.getPrimarySymbolIterator(currentSelection, true); .getPrimarySymbolIterator(currentSelection, true)) {
while (iter.hasNext() && !monitor.isCancelled()) {
final Symbol s = iter.next();
mangle(s); mangle(s);
// Also mangle everything referenced inside functions // Also mangle everything referenced inside functions
@ -303,9 +298,9 @@ public class MSVC7Mangle extends GhidraScript{
scalar deleting destructor. scalar deleting destructor.
*/ */
final Reference[] refs = getReferencesTo(f.getEntryPoint()); final Reference[] refs = getReferencesTo(f.getEntryPoint());
for (int i = 0; i < refs.length; i++) { for (final Reference ref : refs) {
final Data data = getDataContaining (refs[i].getFromAddress()); final Data data = getDataContaining (ref.getFromAddress());
final Function func = getFunctionContaining(refs[i].getFromAddress()); final Function func = getFunctionContaining(ref.getFromAddress());
if (data != null) { if (data != null) {
final Symbol s = getSymbolAt(data.getRoot() final Symbol s = getSymbolAt(data.getRoot()
@ -481,12 +476,12 @@ public class MSVC7Mangle extends GhidraScript{
// freaks out at is actually useful (and Optional still // freaks out at is actually useful (and Optional still
// helped us out here) // helped us out here)
String mangledArgs = ""; String mangledArgs = "";
for (int i = 0; i < args.length; i++) { for (final DataType arg : args) {
final String mangledArg = mangleType(args[i], dict, loc); final String mangledArg = mangleType(arg, dict, loc);
mangledArgs += mangledArg.length() == 1 ? mangledArgs += mangledArg.length() == 1 ?
mangledArg : mangledArg :
backref(args[i], argDict).orElse(mangledArg); backref(arg, argDict).orElse(mangledArg);
} }
return mangledArgs + (f.hasVarArgs() ? "Z" : "@"); return mangledArgs + (f.hasVarArgs() ? "Z" : "@");
} }
@ -592,8 +587,8 @@ public class MSVC7Mangle extends GhidraScript{
ins = ins.getNext() ins = ins.getNext()
) { ) {
final Reference[] refs = ins.getReferencesFrom(); final Reference[] refs = ins.getReferencesFrom();
for (int i = 0; i < refs.length; i++) { for (final Reference ref : refs) {
final Symbol symbol = getSymbolAt(refs[i].getToAddress()); final Symbol symbol = getSymbolAt(ref.getToAddress());
// Guard against spurious references to nonexisting things // Guard against spurious references to nonexisting things
if ( if (
@ -605,10 +600,10 @@ public class MSVC7Mangle extends GhidraScript{
d.getBaseDataType() instanceof Undefined || d.getBaseDataType() instanceof Undefined ||
d.getBaseDataType() instanceof DefaultDataType d.getBaseDataType() instanceof DefaultDataType
) && ) &&
refs[i].getSource() != SourceType.USER_DEFINED ref.getSource() != SourceType.USER_DEFINED
) )
) { ) {
removeReference(refs[i]); removeReference(ref);
continue; continue;
} }