86 lines
3.8 KiB
Java
86 lines
3.8 KiB
Java
// Walk the call tree down from the export orchestrator and surface the page
|
|
// serializer: flag functions that call file-I/O APIs or use the 4096 page size,
|
|
// then decompile those candidates. Output -> ~/ghidra_proj/calltree.txt
|
|
import java.io.PrintWriter;
|
|
import java.util.*;
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.app.decompiler.*;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.scalar.Scalar;
|
|
|
|
public class CallTree extends GhidraScript {
|
|
static final String ROOT = "0x14119d2d0";
|
|
static final int MAX_DEPTH = 4;
|
|
|
|
boolean hasPageConst(Function f) {
|
|
InstructionIterator it = currentProgram.getListing().getInstructions(f.getBody(), true);
|
|
while (it.hasNext()) {
|
|
Instruction insn = it.next();
|
|
for (int op = 0; op < insn.getNumOperands(); op++)
|
|
for (Object o : insn.getOpObjects(op))
|
|
if (o instanceof Scalar) {
|
|
long v = ((Scalar) o).getUnsignedValue();
|
|
if (v == 0x1000L || v == 4096L) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
String ioCalls(Function f) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (Function c : f.getCalledFunctions(monitor)) {
|
|
String n = c.getName().toLowerCase();
|
|
if (n.contains("write") || n.contains("createfile") || n.contains("fopen")
|
|
|| n.contains("fwrite") || n.contains("mapviewof") || n.contains("setfilepointer")
|
|
|| n.contains("flushview") || n.contains("fclose"))
|
|
sb.append(c.getName()).append(" ");
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
@Override
|
|
public void run() throws Exception {
|
|
PrintWriter out = new PrintWriter("/home/ntr/ghidra_proj/calltree.txt");
|
|
FunctionManager fm = currentProgram.getFunctionManager();
|
|
Function root = fm.getFunctionContaining(currentProgram.getAddressFactory().getAddress(ROOT));
|
|
|
|
Map<Function, Integer> depth = new LinkedHashMap<>();
|
|
Deque<Function> q = new ArrayDeque<>();
|
|
depth.put(root, 0); q.add(root);
|
|
while (!q.isEmpty() && !monitor.isCancelled()) {
|
|
Function f = q.poll();
|
|
int d = depth.get(f);
|
|
if (d >= MAX_DEPTH) continue;
|
|
for (Function c : f.getCalledFunctions(monitor))
|
|
if (!depth.containsKey(c)) { depth.put(c, d + 1); q.add(c); }
|
|
}
|
|
out.println("call tree from " + root.getName() + " depth<=" + MAX_DEPTH + ": " + depth.size() + " funcs\n");
|
|
|
|
List<Function> candidates = new ArrayList<>();
|
|
for (Map.Entry<Function, Integer> e : depth.entrySet()) {
|
|
Function f = e.getKey();
|
|
boolean page = hasPageConst(f);
|
|
String io = ioCalls(f);
|
|
String flags = (page ? "[4096]" : "") + (io.isEmpty() ? "" : "[io:" + io.trim() + "]");
|
|
out.println(String.format("d%d %-12s size=%-6d %s %s",
|
|
e.getValue(), f.getEntryPoint(), f.getBody().getNumAddresses(), f.getName(), flags));
|
|
if (page || !io.isEmpty()) candidates.add(f);
|
|
}
|
|
|
|
DecompInterface di = new DecompInterface();
|
|
di.openProgram(currentProgram);
|
|
out.println("\n===== CANDIDATE DECOMPILES (" + candidates.size() + ") =====");
|
|
for (Function f : candidates) {
|
|
if (monitor.isCancelled()) break;
|
|
out.println("\n//===== " + f.getName() + " @ " + f.getEntryPoint() + " =====");
|
|
DecompileResults r = di.decompileFunction(f, 60, monitor);
|
|
if (r != null && r.decompileCompleted())
|
|
out.println(r.getDecompiledFunction().getC());
|
|
else
|
|
out.println("// decompile failed");
|
|
}
|
|
out.flush(); out.close();
|
|
println("wrote calltree.txt: " + depth.size() + " funcs, " + candidates.size() + " candidates");
|
|
}
|
|
}
|