# Ghidra headless script: decompile a target list of IL2CPP functions to C.
# @category il2cpp
# Reads ghidra/targets.txt (rva<TAB>name), creates+names+decompiles each,
# dumps all C to ghidra/decomp.c for offline grepping.
import time
from ghidra.app.decompiler import DecompInterface
from ghidra.util.task import ConsoleTaskMonitor
from ghidra.program.model.symbol import SourceType

ROOT = "/home/downloadpizza/sand_tools/ghidra"
base = currentProgram.getImageBase()

decomp = DecompInterface()
decomp.openProgram(currentProgram)
monitor = ConsoleTaskMonitor()

out = open(ROOT + "/decomp.c", "w")
n = 0
ok = 0
t0 = time.time()
fp = open(ROOT + "/targets.txt")
for line in fp:
    line = line.rstrip("\n")
    if not line:
        continue
    rva, name = line.split("\t", 1)
    # ascii-sanitize: obfuscated names use Cyrillic look-alikes -> replace
    name = "".join((ch if ord(ch) < 128 else "_") for ch in name)
    addr = base.add(int(rva))
    fn = getFunctionAt(addr)
    if fn is None:
        disassemble(addr)
        createFunction(addr, name)
        fn = getFunctionAt(addr)
    n += 1
    if fn is None:
        continue
    try:
        fn.setName(name, SourceType.USER_DEFINED)
    except:
        pass
    try:
        res = decomp.decompileFunction(fn, 25, monitor)
        if res is not None and res.decompileCompleted():
            c = res.getDecompiledFunction().getC()
            c = "".join((ch if ord(ch) < 128 else "_") for ch in c)
            out.write("\n// ==== %s @ +0x%x ====\n" % (name, int(rva)))
            out.write(c)
            ok += 1
    except Exception, e:
        pass
    if n % 1000 == 0:
        out.flush()
        print("processed %d ok %d  (%.0fs)" % (n, ok, time.time() - t0))
fp.close()
out.close()
print("DONE processed %d ok %d in %.0fs" % (n, ok, time.time() - t0))
