// Names unnamed functions from embedded "Class::Method" strings (VPROF / assert // labels). A function is renamed only when the string is referenced from that // one function and the function references no other such string, which avoids // mislabeling callers that inlined someone else's assert. //@category Analysis import java.util.*; import java.util.regex.Pattern; import ghidra.app.script.GhidraScript; import ghidra.program.model.data.StringDataInstance; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.program.util.DefinedDataIterator; import ghidra.app.util.NamespaceUtils; public class RecoverNamesFromStrings extends GhidraScript { private static final Pattern QUALIFIED = Pattern.compile("^[A-Za-z_]\\w*(::[A-Za-z_~]\\w*)+$"); @Override protected void run() throws Exception { Listing listing = currentProgram.getListing(); ReferenceManager refs = currentProgram.getReferenceManager(); Map> namesByFunc = new HashMap<>(); Map> funcsByName = new HashMap<>(); for (Data d : DefinedDataIterator.byDataInstance(currentProgram, StringDataInstance::isString)) { String s = StringDataInstance.getStringDataInstance(d).getStringValue(); if (s == null || !QUALIFIED.matcher(s).matches()) { continue; } for (Reference r : refs.getReferencesTo(d.getAddress())) { Function f = listing.getFunctionContaining(r.getFromAddress()); if (f != null) { namesByFunc.computeIfAbsent(f, k -> new HashSet<>()).add(s); funcsByName.computeIfAbsent(s, k -> new HashSet<>()).add(f); } } } int renamed = 0; for (Map.Entry> e : namesByFunc.entrySet()) { Function f = e.getKey(); if (e.getValue().size() != 1 || f.getSymbol().getSource() != SourceType.DEFAULT) { continue; } String full = e.getValue().iterator().next(); if (funcsByName.get(full).size() != 1) { continue; } int split = full.lastIndexOf("::"); Namespace ns = NamespaceUtils.createNamespaceHierarchy(full.substring(0, split), currentProgram.getGlobalNamespace(), currentProgram, SourceType.ANALYSIS); f.setParentNamespace(ns); f.setName(full.substring(split + 2), SourceType.ANALYSIS); renamed++; } println("Renamed " + renamed + " functions from " + funcsByName.size() + " qualified-name strings"); } }