Skip to content

Instantly share code, notes, and snippets.

@DRKV333
Created June 16, 2022 16:06
Show Gist options
  • Save DRKV333/4e8e88c91666b02e2be959e53af09d2a to your computer and use it in GitHub Desktop.
Save DRKV333/4e8e88c91666b02e2be959e53af09d2a to your computer and use it in GitHub Desktop.
A Ghidra script for renaming functions based on strings found in log4cxx ""LocationInfo" constructor calls.
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.PcodeOpAST;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SymbolType;
public class Log4cxxRenamer extends GhidraScript {
private Function locationInfo;
private DecompInterface decomp;
private Pattern sigPattern = Pattern.compile("(__\\w+) (?:\\w+::)*?(\\w+)(?:::(\\w+))?[^\\w\\s:]");
private SymbolTable symbolTable;
private static boolean isFunction(Symbol s) {
return s.getSymbolType() == SymbolType.FUNCTION;
}
private static boolean isCall(Reference r) {
final RefType type = r.getReferenceType();
if (type.isCall()) {
return !(type.isComputed() || type.isIndirect());
}
return false;
}
private Address convertAddressToRamSpace(Address address) {
String addressString = address.toString(false);
return currentProgram.getAddressFactory().getAddress(addressString);
}
private List<Namespace> getAllMatchingNamespaces(String name)
{
return getAllMatchingNamespaces(name, currentProgram.getGlobalNamespace(), new ArrayList<>());
}
private List<Namespace> getAllMatchingNamespaces(String name, Namespace namespace, List<Namespace> res)
{
if (!(namespace instanceof Function) && namespace.getName(false).equals(name))
res.add(namespace);
return getAllMatchingNamespaces(name, symbolTable.getSymbols(namespace), res);
}
private List<Namespace> getAllMatchingNamespaces(String name, SymbolIterator iter, List<Namespace> res)
{
while(iter.hasNext())
{
Object namespaceMaybe = iter.next().getObject();
if (!(namespaceMaybe instanceof Namespace))
continue;
Namespace namespace = (Namespace)namespaceMaybe;
getAllMatchingNamespaces(name, namespace, res);
}
return res;
}
private String getOrCreateStringAt(Address address) throws Exception
{
Data data = getDataAt(address);
if (data == null)
{
Data overlapping = getDataContaining(address);
if (overlapping != null)
{
println("Retracted overlapping data at: " + overlapping.getAddress());
removeData(overlapping);
}
println("Created data string at: " + address);
data = createAsciiString(address);
}
return (String)data.getValue();
}
private String traceToString(Throwable f)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
f.printStackTrace(pw);
return sw.toString(); // stack trace as a string
}
@Override
public void run() throws Exception
{
symbolTable = currentProgram.getSymbolTable();
decomp = new DecompInterface();
DecompileOptions options;
options = new DecompileOptions();
OptionsService service = state.getTool().getService(OptionsService.class);
if (service != null)
{
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null, opt, currentProgram);
}
decomp.setOptions(options);
decomp.toggleSyntaxTree(true);
decomp.toggleCCode(false);
decomp.openProgram(currentProgram);
Symbol locationInfoSymbol = symbolTable.getExternalSymbol("LocationInfo");
locationInfo = (Function)locationInfoSymbol.getObject();
//doThisAddress(currentAddress);
monitor.initialize(currentProgram.getReferenceManager().getReferenceCountTo(locationInfo.getEntryPoint()));
monitor.setProgress(0);
monitor.setCancelEnabled(true);
//getAllMatchingNamespaces("PacketDispatcher").forEach((Namespace n) -> println(n.getName(true)));
ReferenceIterator refs = currentProgram.getReferenceManager().getReferencesTo(locationInfo.getEntryPoint());
while (refs.hasNext())
{
if (monitor.isCancelled())
break;
try
{
Reference ref = refs.next();
if (!ref.getReferenceType().isCall())
continue;
doThisAddress(ref.getFromAddress());
}
catch (Exception e)
{
println("FATAL ERROR!!!");
println(traceToString(e));
}
monitor.incrementProgress(1);
}
decomp.dispose();
}
private Function prevFunc = null;
private void doThisAddress(Address address) throws Exception
{
println(address.toString());
Function func = getFunctionContaining(address);
if (func == null)
{
println("Not a function");
return;
}
if (prevFunc != null && prevFunc.getEntryPoint().equals(func.getEntryPoint()))
{
println("skipping duplicate.");
return;
}
prevFunc = func;
//String currentName = func.getName();
//if (!currentName.contains("FUN_"))
//{
// println("Already has name: " + currentName);
// return;
//}
DecompileResults res = decomp.decompileFunction(func, 3600, monitor);
if (res.getHighFunction() == null)
{
println("Failed to decompile!!!");
return;
}
Iterator<PcodeOpAST> pcodesAtCall = res.getHighFunction().getPcodeOps(address);
boolean hasCall = false;
while (pcodesAtCall.hasNext())
{
PcodeOpAST pcode = pcodesAtCall.next();
if (pcode.getOpcode() == PcodeOp.CALL)
{
Varnode locArg = pcode.getInput(3);
if (locArg.getDef().getOpcode() == PcodeOp.COPY)
locArg = locArg.getDef().getInput(0);
String signature = getOrCreateStringAt(convertAddressToRamSpace(locArg.getAddress()));
Matcher sigMatch = sigPattern.matcher(signature);
if (sigMatch.find())
{
String callConv = sigMatch.group(1);
String className = sigMatch.group(2);
String funcName = sigMatch.group(3);
if (funcName == null)
{
funcName = className;
className = null;
}
func.setCallingConvention(callConv);
func.setName(funcName, SourceType.USER_DEFINED);
if (className != null)
{
List<Namespace> possibleClasses = getAllMatchingNamespaces(className);
if (possibleClasses.size() > 1)
{
println("Multiple possible classes");
}
else if (possibleClasses.isEmpty())
{
func.setParentNamespace(symbolTable.createClass(currentProgram.getGlobalNamespace(), className, SourceType.USER_DEFINED));
println("Created class: " + className);
}
else
{
func.setParentNamespace(possibleClasses.get(0));
}
}
println("Renamed to " + callConv + " " + funcName + " " + (className == null ? "(No a method)" : className));
}
else
{
println("Sig not matches");
}
hasCall = true;
break;
}
}
if (!hasCall)
println("No CALL found");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment