| |
| |
| |
| |
| |
| |
| |
| import os |
| from ghidra.app.decompiler import DecompInterface |
| from ghidra.util.task import ConsoleTaskMonitor |
|
|
| args = getScriptArgs() |
| outdir = args[0] if args and len(args) > 0 else os.path.join(os.getcwd(), "ghidra_out") |
| try: |
| os.makedirs(outdir) |
| except: |
| pass |
|
|
| def _parse_offsets(s): |
| out = [] |
| if not s: |
| return out |
| for tok in s.replace(";", ",").split(","): |
| tok = tok.strip() |
| if not tok: |
| continue |
| try: |
| out.append(int(tok, 16) if tok.lower().startswith("0x") else int(tok, 0)) |
| except: |
| pass |
| return out |
|
|
| |
| offs_src = args[1] if args and len(args) > 1 else os.environ.get("GHIDRA_PATCH_OFFSETS", "") |
| TARGET_OFFSETS = _parse_offsets(offs_src) |
|
|
| prog = currentProgram |
| fm = prog.getFunctionManager() |
| mem = prog.getMemory() |
| imageBase = prog.getImageBase() |
|
|
| dec = DecompInterface() |
| dec.openProgram(prog) |
| monitor = ConsoleTaskMonitor() |
|
|
| |
| def off_to_addr(off): |
| for b in mem.getBlocks(): |
| try: |
| sis = b.getSourceInfos() |
| except: |
| sis = [] |
| for si in sis: |
| fb = -1 |
| try: |
| fb = si.getFileBytesOffset() |
| except: |
| try: |
| fb = si.getFileBytesOffsetFromVA() |
| except: |
| fb = -1 |
| try: |
| ln = si.getLength() |
| except: |
| ln = 0 |
| if fb is not None and fb != -1 and fb <= off < fb + ln: |
| return b.getStart().add(off - fb) |
| return None |
|
|
| |
| def addr_to_off(addr): |
| try: |
| info = mem.getAddressSourceInfo(addr) |
| if info is not None: |
| fo = info.getFileOffset() |
| if fo is not None and fo != -1: |
| return fo |
| except: |
| pass |
| return None |
|
|
| patch_report = [] |
| target_funcs = set() |
| for off in TARGET_OFFSETS: |
| addr = off_to_addr(off) |
| if addr is None: |
| patch_report.append("file 0x%06X -> (no VA mapping)" % off) |
| continue |
| f = fm.getFunctionContaining(addr) |
| fname = f.getName() if f else "(no function)" |
| fentry = f.getEntryPoint().toString() if f else "-" |
| patch_report.append("file 0x%06X -> VA %s in func %s @ %s" % (off, addr, fname, fentry)) |
| if f: |
| target_funcs.add(f) |
|
|
| if TARGET_OFFSETS: |
| with open(os.path.join(outdir, "patch_sites.txt"), "w") as fh: |
| fh.write("ImageBase: %s\n" % imageBase) |
| fh.write("\n".join(patch_report)) |
| fh.write("\n") |
|
|
| |
| def decompile(f): |
| try: |
| res = dec.decompileFunction(f, 60, monitor) |
| if res and res.decompileCompleted(): |
| return res.getDecompiledFunction().getC() |
| except Exception as e: |
| return "// decompile error: %s\n" % str(e) |
| return "// decompile failed\n" |
|
|
| |
| if target_funcs: |
| with open(os.path.join(outdir, "patch_functions.c"), "w") as fh: |
| for f in target_funcs: |
| entry = f.getEntryPoint() |
| fo = addr_to_off(entry) |
| fo_s = ("0x%X" % fo) if fo is not None else "?" |
| fh.write("\n/* ===== FUNCTION %s @ VA %s (file %s) ===== */\n" % (f.getName(), entry, fo_s)) |
| fh.write(decompile(f)) |
| fh.write("\n") |
|
|
| |
| |
| def caller_count(f): |
| try: |
| return len(f.getCallingFunctions(monitor)) |
| except: |
| return 0 |
|
|
| def callee_count(f): |
| try: |
| return len(f.getCalledFunctions(monitor)) |
| except: |
| return 0 |
|
|
| |
| |
| |
| funcs = list(fm.getFunctions(True)) |
| idx = open(os.path.join(outdir, "functions.txt"), "w") |
| allc = open(os.path.join(outdir, "all_decomp.c"), "w") |
| ranked = [] |
| count = 0 |
| for f in funcs: |
| ep = f.getEntryPoint() |
| nc = caller_count(f) |
| if f.isThunk(): |
| idx.write("%s\t%s\tcallers=%d\tcallees=0\tTHUNK\n" % (ep, f.getName(), nc)) |
| continue |
| cc = callee_count(f) |
| idx.write("%s\t%s\tcallers=%d\tcallees=%d\n" % (ep, f.getName(), nc, cc)) |
| ranked.append((nc, cc, ep.toString(), f.getName())) |
| allc.write("\n/* ===== %s @ %s (callers=%d callees=%d) ===== */\n" |
| % (f.getName(), ep, nc, cc)) |
| allc.write(decompile(f)) |
| count += 1 |
| idx.close() |
| allc.close() |
|
|
| ranked.sort(reverse=True) |
| with open(os.path.join(outdir, "by_callers.txt"), "w") as rh: |
| rh.write("# callers\tcallees\tVA\tname (rank targets from the top)\n") |
| for nc, cc, va, nm in ranked: |
| rh.write("%d\t%d\t%s\t%s\n" % (nc, cc, va, nm)) |
|
|
| print("EXPORT DONE: %d functions decompiled -> %s" % (count, outdir)) |
| print(" ranked candidates -> by_callers.txt") |
| for line in patch_report: |
| print(" " + line) |
|
|