sots-re/ghidra/scripts/SpineRecon3.java

84 lines
6.1 KiB
Java

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<Function> toDump = new LinkedHashSet<Function>();
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<Address,String> refs = new TreeMap<Address,String>();
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<Function> 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<Function>()); }
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");
}
}