"""DIMENSION 4 (duplicates), pass 4: the item class, and the one unresolved atom-serialisation site. Open items from pass 3: (a) the single UNRESOLVED FUN_180180cd0 call site, 0x1801438e2 in FUN_180143760, takes its atom from [RBP+0x60]. Until it is characterised, "no request ever serialises 0xeb/0xec/0xed/0x16f" is not airtight. (b) which class implements GetCardDuplicate (DAT_1802def18 vtable slot 0x18). (c) the item-model manager: DAT_1802e6398 is written by FUN_18011d780. Get the concrete vtable so slots 0x160 / 0x7d8 / 0xa08 can be named, and so the item constructor (which must zero item+0x10) can be found. CONTROL for the vtable walk: slot 0x08 of any of these vtables must decode to a real function in .text, and we print the raw qwords so a bogus vtable is visible. """ import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store/dup4_out.txt" def w(fh, s=""): fh.write(str(s) + "\n") def dump(fh, a, label): s = dec(a) w(fh, "") w(fh, "#" * 70) w(fh, "# %s %#x len(src)=%d FULL" % (label, a, len(s))) w(fh, "#" * 70) w(fh, s) try: fh = open(OUT, "w") dump(fh, 0x180143760, "(a) dynamic-atom serialiser") w(fh, "") w(fh, "=" * 70) w(fh, "(b) xrefs to DAT_1802def18 (the script card-info provider pointer)") w(fh, "=" * 70) for frm, typ, fn, ent in xrefs_to(0x1802DEF18): w(fh, " %#x %s in %s @ %#x" % (frm, typ, fn, ent)) dump(fh, 0x18011D780, "(c) manager singleton installer FUN_18011d780") w(fh, "") w(fh, "=" * 70) w(fh, "(c2) candidate manager vtables referenced from FUN_18011d780") w(fh, "=" * 70) f = func(0x18011D780) seen = set() if f is not None: it = listing.getInstructions(f.getBody(), True) while it.hasNext(): ins = it.next() for r in ins.getReferencesFrom(): t = int(r.getToAddress().getOffset()) if 0x180000000 <= t < 0x181000000 and t not in seen: seen.add(t) try: q0 = qword(t) q1 = qword(t + 8) except Exception: continue f0 = fm.getFunctionAt(addr(q0)) if 0x180000000 <= q0 < 0x181000000 else None f1 = fm.getFunctionAt(addr(q1)) if 0x180000000 <= q1 < 0x181000000 else None if f0 and f1: w(fh, " possible vtable %#x : [0]=%#x %s [8]=%#x %s" % (t, q0, f0.getName(), q1, f1.getName())) for off in (0x160, 0x7D8, 0xA08, 0xA40, 0x5B8): try: q = qword(t + off) except Exception: continue ff = fm.getFunctionAt(addr(q)) if 0x180000000 <= q < 0x181000000 else None w(fh, " +%#05x -> %#x %s" % (off, q, ff.getName() if ff else "(not a function)")) fh.close() print("WROTE", OUT) except Exception: traceback.print_exc()