Files
steam/tools/ghidra/RecoverNamesFromStrings.java
SalastilandClaude Opus 5.5 3b4786ff4d Decompile Steam client (stage 1)
Ghidra 12.1.2 headless decompilation of Steam client version 1788652215:
- decomp/linux64_steamclient.so, ubuntu12_32_steamclient.so, ubuntu12_32_steam:
  per-class pseudo-C (RTTI class recovery + Class::Method string naming),
  functions.tsv and strings.tsv
- decomp/protos: 107 .proto files and descriptors.pb extracted from steamclient.so
- decomp/steamui: prettified Library UI JS; steamclient_js_api.txt lists the
  SteamClient.* backend API it calls
- tools/: decompile/recover/export scripts and proto extractor

GCC exception-handler analysis is disabled: it truncated ~47% of function
bodies on these binaries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:16:11 -04:00

63 lines
2.3 KiB
Java

// 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<Function, Set<String>> namesByFunc = new HashMap<>();
Map<String, Set<Function>> 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<Function, Set<String>> 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");
}
}