84 lines
6.1 KiB
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");
|
|
}
|
|
}
|