JSRF-Decompilation/ghidra/ghidra_scripts/MSVC7Mangle.java
KeybadeBlox 76e39fdd27 Begin Ghidra mangling script
Looks like we'll be writing our own Ghidra scripts.  At least these
should enable pretty thorough sharing of work and decent UX.
2026-02-05 22:31:36 -05:00

111 lines
3.3 KiB
Java

// Applies Visual C++ 7.0 name mangling to the symbols within the selected
// address range (or the whole program if nothing is selected).
//
// Be aware that the mangling implementation is only partial.
//
// @category Symbol
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Enum;
import ghidra.program.model.data.IntegerDataType;
import ghidra.program.model.data.VoidDataType;
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.Collections;
import java.util.List;
public class MSVC7Mangle extends GhidraScript{
@Override
public void run() throws Exception {
final SymbolIterator iter = currentProgram.getSymbolTable()
.getPrimarySymbolIterator(currentSelection, true);
while (iter.hasNext() && !monitor.isCancelled()) {
final Symbol s = iter.next();
switch (s.getObject()) {
case Function f -> demangleFn(f);
case Data d -> demangleData(s);
default -> {}
}
}
}
private void demangleFn(final Function f) throws Exception {
// Gather everything needed for mangling
final List<String> name = Arrays.asList(f.getName(true)
.split("::"));
Collections.reverse(name);
final String callc = f.getCallingConventionName();
final DataType ret = f.getReturnType();
final DataType[] args = Arrays.stream(f.getSignature(true)
.getArguments())
.map(x -> x.getDataType())
.toArray(DataType[]::new);
// Construct mangled name
final String mangled =
"?" + String.join("@", name) + "@@" +
switch (callc) {
case "__cdecl" -> "YA";
case "__thiscall" -> isVirtual(f) ? "UAE" :
"QAE";
case "__fastcall" -> ""; // TODO
default -> throw new Exception(
"Need to specify calling convention"
);
} +
mangleType(ret) +
mangleArgs(args) +
"Z";
f.setName(mangled, SourceType.USER_DEFINED);
}
private void demangleData(final Symbol s) {
// TODO
printf("TODO: data symbol \"%s\"\n", s.getName(true));
}
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.
*/
final Reference[] refs = getReferencesTo(f.getEntryPoint());
// TODO
return false;
}
private String mangleType(final DataType t) throws Exception {
/* Mangle a data type in a function name */
return switch(t) {
case Enum e -> "W4" + e.getName() + "@@";
case IntegerDataType x -> "H";
case VoidDataType x -> "X";
default -> throw new Exception(
"Unhandled data type \"" + t.toString() + "\""
);
};
}
private String mangleArgs(final DataType[] args) throws Exception {
/* Mangle the arguments for a function */
if (args.length == 0) return "X";
else {
String encoded = "";
for (int i = 0; i < args.length; i++)
encoded += mangleType(args[i]);
return encoded + "@";
}
}
}