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 decompile function at addr (+ callers/callees list) // C decompile all callers of addr // K decompile all callees of addr // S exact NUL-delimited string: list refs, decompile referencing functions // P substring search in memory: list refs to containing string, decompile referencing functions // D disassembly range dump // X 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
findExact(String s) throws Exception { byte[] pat = ("\0" + s + "\0").getBytes("ISO-8859-1"); List
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
findSub(String s) throws Exception { byte[] pat = s.getBytes("ISO-8859-1"); List
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
findLE(long v) throws Exception { byte[] pat = new byte[]{(byte)v,(byte)(v>>8),(byte)(v>>16),(byte)(v>>24)}; List
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 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"); } }