import ghidra.app.script.GhidraScript; import ghidra.program.model.address.*; import ghidra.program.model.mem.*; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.app.decompiler.*; import java.util.*; import java.util.regex.*; import java.io.*; // Pass 3 (read-only): main loop + turn-processing driver: decompile candidates, callers-up chains for SE* creators. public class SpineRecon3 extends GhidraScript { DecompInterface decomp; Memory mem; PrintWriter out; ReferenceManager rm; SymbolTable st; Set toDump = new LinkedHashSet(); static final long[] DECOMP = {0x0089f5b0L,0x0089dfb0L,0x00898b00L,0x00789710L,0x007b18b0L,0x007ad0f0L,0x0078f6a0L,0x0081ff40L,0x007dc6c0L,0x007cf540L, 0x007dc640L,0x007da9a0L,0x007c5850L,0x007c5b00L,0x00789500L,0x00752500L,0x00815fd0L,0x007dd2f0L,0x008d2290L,0x0088c7b0L,0x0089c950L,0x00898ae0L,0x00898af0L, 0x00761310L,0x00789330L,0x007c0600L,0x007d4400L,0x0076c610L,0x00769340L,0x00781cf0L,0x0089a3b0L,0x0089a640L,0x0089a480L,0x00899790L,0x0089eec0L,0x0089f1b0L}; static final long[] UPCHAIN = {0x007da9a0L,0x00752500L,0x00789500L,0x007c5850L,0x007c5b00L,0x007dc640L,0x007c8d90L,0x007b18b0L,0x007ad0f0L,0x0083dbf0L,0x0078fac0L,0x007cbe80L,0x007bfe60L,0x00794770L,0x00784640L,0x0089f5b0L,0x00898b00L}; String cstr(Address a, int max) { try { byte[] b = new byte[max]; int got = mem.getBytes(a, b); int i = 0; while (i < got && b[i] != 0 && (b[i]&0xff) >= 0x20 && (b[i]&0xff) < 0x7f) i++; if (i >= 1 && i < got && b[i] == 0) return new String(b, 0, i, "ISO-8859-1"); } catch (Exception e) {} return null; } String decompRes(Function f) { try { DecompileResults res = decomp.decompileFunction(f, 240, monitor); if (res == null || !res.decompileCompleted()) return "[decompile failed]"; String c = res.getDecompiledFunction().getC(); Matcher m = Pattern.compile("&?(DAT|PTR_s_|s_[A-Za-z0-9_]*|PTR_DAT|u_[A-Za-z0-9_]*)_([0-9a-f]{8})").matcher(c); StringBuffer sb = new StringBuffer(); while (m.find()) { Address a = toAddr(Long.parseLong(m.group(2), 16)); String s = cstr(a, 100); String rep = m.group(0); if (s != null && s.length() <= 96) rep = "\"" + s + "\""; m.appendReplacement(sb, Matcher.quoteReplacement(rep)); } m.appendTail(sb); return sb.toString(); } catch (Exception e) { return "[exception " + e.getMessage() + "]"; } } void dumpFunc(Function f) throws IOException { String n = String.format("%08x", f.getEntryPoint().getOffset()); File fl = new File("/tmp/spine/" + n + ".c"); if (fl.exists()) return; PrintWriter w = new PrintWriter(new FileWriter(fl)); w.println("// " + f.getName() + " @ " + f.getEntryPoint() + " size=" + f.getBody().getNumAddresses()); StringBuilder cs = new StringBuilder(); for (Function c : f.getCallingFunctions(monitor)) cs.append(c.getName() + "@" + c.getEntryPoint() + " "); w.println("// CALLERS: " + cs); StringBuilder ce = new StringBuilder(); for (Function c : f.getCalledFunctions(monitor)) ce.append(c.getName() + " "); w.println("// CALLEES: " + ce); w.println(decompRes(f)); w.close(); } String funcDesc(Function f) { StringBuilder cs = new StringBuilder(); int n = 0; for (Function c : f.getCallingFunctions(monitor)) { if (n++ < 6) cs.append(c.getName() + " "); } return f.getName() + "@" + f.getEntryPoint() + " sz=" + f.getBody().getNumAddresses() + " callers(" + n + "):[" + cs.toString().trim() + "]"; } // strings referenced by a function, in address order String strsOf(Function f) { TreeMap refs = new TreeMap(); AddressIterator ai = rm.getReferenceSourceIterator(f.getBody(), true); while (ai.hasNext()) { Address from = ai.next(); for (Reference r : rm.getReferencesFrom(from)) { Address to = r.getToAddress(); if (!mem.contains(to)) continue; String sv = cstr(to, 90); if (sv != null && sv.length() >= 4) refs.put(from, sv); } } StringBuilder sb = new StringBuilder(); for (String s : refs.values()) sb.append("\"" + s + "\" "); return sb.toString(); } void callersUp(Function f, int depth, String indent, Set seen) { if (depth == 0 || f == null || seen.contains(f)) return; seen.add(f); for (Function c : f.getCallingFunctions(monitor)) { if (c.getName().startsWith("Unwind")) continue; out.println(indent + "<- " + funcDesc(c) + " strs: " + strsOf(c)); callersUp(c, depth - 1, indent + " ", seen); } } @Override public void run() throws Exception { mem = currentProgram.getMemory(); rm = currentProgram.getReferenceManager(); st = currentProgram.getSymbolTable(); decomp = new DecompInterface(); decomp.openProgram(currentProgram); new File("/tmp/spine").mkdirs(); out = new PrintWriter(new FileWriter("/tmp/spine/recon3.txt")); out.println("==== 1. CALLER CHAINS (with strings) ===="); for (long fa : UPCHAIN) { Function f = getFunctionAt(toAddr(fa)); if (f == null) continue; out.println("ROOT " + funcDesc(f) + " strs: " + strsOf(f)); callersUp(f, 4, " ", new HashSet()); } out.println("\n==== 2. CALLEES of key drivers with strings ===="); for (long fa : new long[]{0x0089f5b0L,0x0089dfb0L,0x007b18b0L,0x007dc640L,0x007db780L,0x007c8d90L,0x0089d610L,0x00898b00L,0x00888e80L}) { Function f = getFunctionAt(toAddr(fa)); if (f == null) continue; out.println("DRIVER " + funcDesc(f)); for (Function c : f.getCalledFunctions(monitor)) { if (c.getBody().getNumAddresses() < 60) continue; out.println(" -> " + funcDesc(c) + " strs: " + strsOf(c)); } } for (long d : DECOMP) { Function f = getFunctionAt(toAddr(d)); if (f != null) toDump.add(f); } out.println("\n==== 3. DUMPED ===="); for (Function f : toDump) { out.println(f.getName() + " @ " + f.getEntryPoint() + " sz=" + f.getBody().getNumAddresses()); dumpFunc(f); } out.close(); decomp.dispose(); println("done"); } }