sots-re/ghidra/scripts/LoaderRecon.java

77 lines
9 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.*;
// Loader recon: request-driven decompile dump. Reads /tmp/ldr/req.txt lines:
// A <hex> decompile function at addr (+ callers/callees list)
// C <hex> decompile all callers of addr
// K <hex> decompile all callees of addr
// S <string> exact NUL-delimited string: list refs, decompile referencing functions
// P <substr> substring search in memory: list refs to containing string, decompile referencing functions
// D <hex> <hex> disassembly range dump
// X <hex> list refs (data/code) to addr, with containing function names
public class LoaderRecon extends GhidraScript {
DecompInterface decomp; Memory mem; ReferenceManager rm;
static final String OUT = "/tmp/ldr/out";
String cstr(Address a) { try { byte[] b = new byte[100]; mem.getBytes(a, b); int i = 0; while (i < 100 && b[i] != 0 && (b[i]&0xff) >= 0x20 && (b[i]&0xff) < 0x7f) i++; if (i >= 1 && i < 100 && b[i] == 0) return new String(b, 0, i, "ISO-8859-1"); } catch (Exception e) {} return null; }
String subst(String c) { 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); String rep = m.group(0); if (s != null && s.length() <= 80 && !m.group(1).equals("PTR_DAT")) rep = "\"" + s + "\""; m.appendReplacement(sb, Matcher.quoteReplacement(rep)); } m.appendTail(sb); return sb.toString(); }
String decompRaw(Function f) { try { DecompileResults res = decomp.decompileFunction(f, 900, monitor); if (res == null || !res.decompileCompleted()) return "[decompile failed] " + res.getErrorMessage(); return res.getDecompiledFunction().getC(); } catch (Exception e) { return "[exception " + e.getMessage() + "]"; } }
List<Address> findExact(String s) throws Exception { byte[] pat = ("\0" + s + "\0").getBytes("ISO-8859-1"); List<Address> out = new ArrayList<>(); Address start = mem.getMinAddress(); while (true) { Address a = mem.findBytes(start, pat, null, true, monitor); if (a == null) break; out.add(a.add(1)); start = a.add(1); } return out; }
List<Address> findSub(String s) throws Exception { byte[] pat = s.getBytes("ISO-8859-1"); List<Address> out = new ArrayList<>(); Address start = mem.getMinAddress(); while (true) { Address a = mem.findBytes(start, pat, null, true, monitor); if (a == null) break; Address st = a; try { while (mem.getByte(st.subtract(1)) != 0) st = st.subtract(1); } catch (Exception e) {} if (!out.contains(st)) out.add(st); start = a.add(1); } return out; }
List<Address> findLE(long v) throws Exception { byte[] pat = new byte[]{(byte)v,(byte)(v>>8),(byte)(v>>16),(byte)(v>>24)}; List<Address> out = new ArrayList<>(); Address start = mem.getMinAddress(); while (out.size() < 400) { Address a = mem.findBytes(start, pat, null, true, monitor); if (a == null) break; out.add(a); start = a.add(1); } return out; }
Set<Long> done = new HashSet<>();
PrintWriter idx;
void dumpFunc(Function f) throws Exception {
if (f == null || done.contains(f.getEntryPoint().getOffset())) return; done.add(f.getEntryPoint().getOffset());
String t = String.format("%08x", f.getEntryPoint().getOffset());
PrintWriter w = new PrintWriter(new FileWriter(OUT + "/" + t + ".c"));
w.println("// " + f.getName(true) + " @ " + f.getEntryPoint() + " size=" + f.getBody().getNumAddresses());
w.println("// SIG: " + f.getSignature().getPrototypeString() + " conv=" + f.getCallingConventionName() + " stackPurge=" + f.getStackPurgeSize());
StringBuilder calls = new StringBuilder(); for (Function cf : f.getCalledFunctions(monitor)) calls.append(cf.getName(true) + "@" + cf.getEntryPoint() + " "); w.println("// CALLS: " + calls);
StringBuilder callers = new StringBuilder(); int n = 0; for (Function cf : f.getCallingFunctions(monitor)) { if (n++ < 60) callers.append(cf.getName(true) + "@" + cf.getEntryPoint() + " "); } w.println("// CALLERS(" + n + "): " + callers);
w.println(subst(decompRaw(f))); w.close(); idx.println("decompiled " + t + " " + f.getName(true)); idx.flush();
}
void refs(Address a, String label, boolean decomp) throws Exception {
ReferenceIterator ri = rm.getReferencesTo(a); int n = 0;
while (ri.hasNext()) { Reference r = ri.next(); Function f = getFunctionContaining(r.getFromAddress()); idx.println("REF " + label + " @" + a + " from " + r.getFromAddress() + " " + r.getReferenceType() + " in " + (f == null ? "?" : f.getName(true) + "@" + f.getEntryPoint())); if (decomp && f != null && n++ < 25) dumpFunc(f); }
idx.flush();
}
void dumpDisasm(Address a, Address b, PrintWriter w) { InstructionIterator it = currentProgram.getListing().getInstructions(new AddressSet(a, b), true); while (it.hasNext()) { Instruction in = it.next(); String s = in.toString(); Matcher m = Pattern.compile("0x(00[9a][0-9a-f]{5})").matcher(s); StringBuffer sb = new StringBuffer(); while (m.find()) { String c = cstr(toAddr(Long.parseLong(m.group(1), 16))); m.appendReplacement(sb, Matcher.quoteReplacement(c != null ? "\"" + c + "\"" : m.group(0))); } m.appendTail(sb); w.println(in.getAddress() + " " + sb); } }
@Override public void run() throws Exception {
mem = currentProgram.getMemory(); rm = currentProgram.getReferenceManager();
decomp = new DecompInterface(); decomp.openProgram(currentProgram); new File(OUT).mkdirs();
idx = new PrintWriter(new FileWriter(OUT + "/index.txt"));
BufferedReader br = new BufferedReader(new FileReader("/tmp/ldr/req.txt")); String line;
while ((line = br.readLine()) != null) {
line = line.trim(); if (line.isEmpty() || line.startsWith("#")) continue;
char op = line.charAt(0); String arg = line.substring(1).trim();
idx.println("== " + line);
try {
if (op == 'A') dumpFunc(getFunctionAt(toAddr(Long.parseLong(arg, 16))));
else if (op == 'C') { Function f = getFunctionAt(toAddr(Long.parseLong(arg, 16))); if (f != null) for (Function cf : f.getCallingFunctions(monitor)) dumpFunc(cf); }
else if (op == 'K') { Function f = getFunctionAt(toAddr(Long.parseLong(arg, 16))); if (f != null) for (Function cf : f.getCalledFunctions(monitor)) dumpFunc(cf); }
else if (op == 'S') { for (Address a : findExact(arg)) refs(a, "\"" + arg + "\"", true); }
else if (op == 'P') { for (Address a : findSub(arg)) { String s = cstr(a); idx.println("STR @" + a + " = " + s); refs(a, "\"" + s + "\"", true); } }
else if (op == 'N') { for (Symbol sy : currentProgram.getSymbolTable().getSymbols(arg)) { idx.println("SYM " + sy.getName(true) + " @" + sy.getAddress()); refs(sy.getAddress(), sy.getName(true), true); } }
else if (op == 'F') { Pattern pt = Pattern.compile(arg); for (Function f : currentProgram.getFunctionManager().getFunctions(true)) { if (pt.matcher(f.getName(true)).find()) { idx.println("FN " + f.getName(true) + " @" + f.getEntryPoint() + " sig=" + f.getSignature().getPrototypeString()); dumpFunc(f); } } }
else if (op == 'B') { long v = Long.parseLong(arg, 16); for (Address a : findLE(v)) { Function f = getFunctionContaining(a); idx.println("IMM " + arg + " @" + a + " in " + (f == null ? "data" : f.getName(true) + "@" + f.getEntryPoint())); } }
else if (op == 'V') { for (Address s : findSub(".?AV" + arg)) { String nm = cstr(s); Address td = s.subtract(8); idx.println("TD " + nm + " @" + td);
for (Address colp : findLE(td.getOffset())) { Address col = colp.subtract(0xc); try { if (mem.getInt(col) != 0) continue; } catch (Exception e) { continue; }
int off = mem.getInt(col.add(4)); idx.println(" COL @" + col + " offset=" + off);
for (Address vp : findLE(col.getOffset())) { Address vt = vp.add(4); idx.println(" VTABLE @" + vt + " (offset " + off + ")");
for (int i = 0; i < 40; i++) { long fp = mem.getInt(vt.add(i*4)) & 0xffffffffL; Function f = getFunctionAt(toAddr(fp)); if (f == null) { Symbol sy = getSymbolAt(toAddr(fp)); if (fp < 0x401000L || fp > 0x990000L) break; idx.println(" [" + i + "] " + toAddr(fp) + " (no function)"); continue; } idx.println(" [" + i + "] " + f.getName(true) + " @" + f.getEntryPoint()); if (i < 24) dumpFunc(f); }
} } } }
else if (op == 'X') {
refs(toAddr(Long.parseLong(arg, 16)), "addr", false); }
else if (op == 'D') { String[] p = arg.split("\\s+"); PrintWriter dw = new PrintWriter(new FileWriter(OUT + "/disasm_" + p[0] + ".txt")); dumpDisasm(toAddr(Long.parseLong(p[0], 16)), toAddr(Long.parseLong(p[1], 16)), dw); dw.close(); }
} catch (Exception e) { idx.println("ERR " + line + ": " + e); }
}
idx.close(); decomp.dispose(); println("done");
}
}