mirror of
https://codeberg.org/KeybadeBlox/JSRF-Decompilation.git
synced 2026-04-07 04:50:23 +03:00
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).
216 lines
6.4 KiB
Java
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.");
|
|
}
|
|
}
|