JSRF-Decompilation/ghidra/ghidra_scripts/EnhancedImport.java
KeybadeBlox a2b777d666 Disable function thunking when importing
Thunked functions can confuse the delinker extension into thinking that
multiple symbols have the same name and lead to delinking failures (with
no diagnostic, conveniently).
2026-03-20 23:25:19 -04:00

216 lines
6.4 KiB
Java

// Imports data from a file produced by the EnhancedExport script.
//
// @category Import
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeQueryService;
import ghidra.app.util.NamespaceUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Undefined4DataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.listing.ParameterImpl;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.StringUtilities;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class EnhancedImport extends GhidraScript {
@Override
public void run() throws Exception {
final List<String> lines = Files.readAllLines(askFile("Select input file", "OK").toPath());
final Set<String> unknownTypes = new HashSet<>(256);
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, unknownTypes); break;
case "func": importFunc(addr, parts, unknownTypes); break;
default: throw new Exception(
"Symbol type \"" + parts[1] +
"\" on line " + String.valueOf(i) +
" is not \"data\" or \"func\""
);
}
}
if (unknownTypes.size() > 0)
println(
"\nWarning: following types were unrecognized:\n" +
String.join(", ", unknownTypes)
);
}
private void importData(
final Address addr,
final String[] parts,
final Set<String> unknownTypes
) throws Exception {
print("Importing data symbol \"" + parts[3] + "\"...");
// Create symbol
final Namespace ns = importNamespace(parts[3]);
final Symbol s = Optional.ofNullable(getSymbolAt(addr))
.orElse(createLabel(
addr,
unqualified(parts[3]),
ns,
true,
SourceType.USER_DEFINED
));
// Create data
if (makeType(parts[2], unknownTypes).orElse(null) instanceof DataType t) {
clearListing(addr, addr.add(Math.max(t.getLength(), 1) - 1));
currentProgram.getListing().createData(addr, t);
println(" done.");
} else println(" skipping.");
}
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.
*/
return qualifiedName.contains("::") ?
NamespaceUtils.createNamespaceHierarchy(
qualifiedName.substring( // Cut off symbol name
0,
qualifiedName.length() - "::".length() -
unqualified(qualifiedName).length()
),
null,
currentProgram,
SourceType.USER_DEFINED
) : null;
}
private Optional<DataType> makeType(
final String type,
final Set<String> unknownTypes
) throws Exception {
/* Attempt to create the described type from a known base type */
final String baseName = StringUtilities.findWord(type, 0);
final List<DataType> foundTypes = state.getTool()
.getService(DataTypeQueryService.class)
.findDataTypes(baseName, null);
if (foundTypes.size() == 0) {
print(" can't find data type \"" + baseName + "\",");
unknownTypes.add(type);
return Optional.empty();
}
return Optional.of(derivedType(
foundTypes.get(0), // Boldly assume first is right
type.substring(baseName.length())
));
}
private static DataType derivedType(
final DataType t,
final String modifiers
) throws Exception {
/* Return the given datatype with given pointer/array modifiers */
DataType ret = t;
for (int i = 0; i < modifiers.length(); i++) switch (modifiers.charAt(i)) {
case '*':
ret = new PointerDataType(ret);
continue;
case '[':
// Read array dimensions
final ArrayList<Integer> dims = new ArrayList<>();
do {
final String n = StringUtilities.findWord(modifiers, i+1);
i += n.length() + 1;
dims.add(Integer.valueOf(n));
} while (modifiers.charAt(i) == '[');
// Apply array
Collections.reverse(dims);
for (int dim : dims) ret = new ArrayDataType(ret, dim);
continue;
case ' ':
continue;
default:
throw new Exception(
"Unexpected character \"" +
String.valueOf(modifiers.charAt(i)) +
"\" in type modifiers"
);
}
return ret;
}
private void importFunc(
final Address addr,
final String[] parts,
final Set<String> unknownTypes
) throws Exception {
print("Importing function symbol \"" + parts[6] + "\"...");
final Function f = Optional.ofNullable(getFunctionAt(addr))
.orElse(createFunction(addr, parts[6]));
// Thunks trip up the delinker extension by making it see
// duplicate symbol names
f.setThunkedFunction(null);
f.setReturnType(
makeType(parts[2], unknownTypes).orElse(Undefined4DataType.dataType),
SourceType.USER_DEFINED
);
f.setInline(parts[4].equals("inline"));
f.setCallFixup(parts[5].equals("nofixup") ? null : parts[5]);
f.setName(unqualified(parts[6]), SourceType.USER_DEFINED);
if (importNamespace(parts[6]) instanceof Namespace ns)
f.setParentNamespace(ns);
final ArrayList<Parameter> args = new ArrayList<>();
for (int i = 7; i < parts.length - 1; i += 2)
args.add(new ParameterImpl(
parts[i+1],
makeType(parts[i], unknownTypes).orElse(Undefined4DataType.dataType),
currentProgram
));
f.updateFunction(
parts[3],
null,
args,
FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS,
true,
SourceType.USER_DEFINED
);
f.setVarArgs(parts[parts.length - 1].equals("..."));
println(" done.");
}
}