#!/usr/bin/env python3
import json, re, subprocess, sys

BIN = '/tmp/dev_il2cpp.so'
d = json.load(open('/root/ants/il2dump/out/script.json'))
methods = {m['Address']: m['Name'] for m in d['ScriptMethod']}
addrs_sorted = sorted(methods.keys())
import bisect
def method_at(a):
    i = bisect.bisect_right(addrs_sorted, a) - 1
    if i < 0: return None
    base = addrs_sorted[i]
    return methods[base], a - base

sl = json.load(open('/root/ants/il2dump/out/stringliteral.json'))
straddr = {}
for e in sl:
    try:
        straddr[int(e['address'],16)] = e['value']
    except Exception:
        pass

with open(BIN, 'rb') as f:
    data = f.read()

def read_u32(off):
    if off < 0 or off+4 > len(data): return None
    return int.from_bytes(data[off:off+4], 'little')

def disas(start, stop):
    out = subprocess.run(['objdump','-d','--start-address=%#x'%start,'--stop-address=%#x'%stop, BIN],
                          capture_output=True, text=True).stdout
    lines = out.splitlines()
    ebx = None
    result = []
    pending_call_pop = None
    for idx, line in enumerate(lines):
        m = re.match(r'^\s*([0-9a-f]+):\s*((?:[0-9a-f]{2} )+)\s*(.*)$', line)
        annotation = ''
        if m:
            addr = int(m.group(1),16)
            mnem = m.group(3)
            # detect call <next-insn>; pop ebx idiom
            mm = re.match(r'call\s+([0-9a-f]+)', mnem)
            if mm and int(mm.group(1),16) == addr + len(m.group(2).split()):
                pending_call_pop = int(mm.group(1),16)
            mm2 = re.match(r'pop\s+%ebx', mnem)
            if mm2 and pending_call_pop is not None:
                ebx = pending_call_pop
                pending_call_pop = None
            mm3 = re.match(r'add\s+\$0x([0-9a-f]+),%ebx', mnem)
            if mm3 and ebx is not None:
                ebx = (ebx + int(mm3.group(1),16)) & 0xffffffff
                annotation += ' ; ebx=0x%x' % ebx
            # find ebx-relative operands like 0x1234(%ebx) or -0x1234(%ebx)
            for om in re.finditer(r'(-?0x[0-9a-f]+)\(%ebx\)', mnem):
                if ebx is None: continue
                off = int(om.group(1),16)
                target = (ebx + off) & 0xffffffff
                s = straddr.get(target)
                if s is not None:
                    annotation += ' ; [ebx+%s]=0x%x STR=%r' % (om.group(1), target, s)
                else:
                    val = read_u32(target)
                    if val is not None:
                        me = method_at(val) if val else None
                        annotation += ' ; [ebx+%s]=0x%x val=0x%x' % (om.group(1), target, val)
            # resolve call targets to method names
            mmc = re.match(r'call\s+([0-9a-f]+)', mnem)
            if mmc:
                tgt = int(mmc.group(1),16)
                nm = methods.get(tgt)
                if nm:
                    annotation += ' ; CALL %s' % nm
        result.append(line + annotation)
    return '\n'.join(result)

if __name__ == '__main__':
    start = int(sys.argv[1], 16)
    stop = int(sys.argv[2], 16)
    print(disas(start, stop))
