# -*- coding: utf-8 -*- """ q_pack_inv_9 -- DIMENSION 1 pass 9: the unopenedPacks-total setter and its readers. The FUT model singleton's vtable was resolved OUT OF THE LIVE PROCESS (read-only, pid resolved by exact /proc/*/comm, slide proven against the FNV hasher prologue): DAT_1802e6398 -> object -> vtable = static 0x18021c2a0 slot +0x4e0 = 0x18011e120 <- called by userInfo deser FUN_18013ec10 @0x18013f223 with (preOrderPacks + recoveredPacks) slot +0x210 = 0x18011e100, +0x4e8 = 0x18011c1f0 (likely the matching getter) Static .rdata pointer-run scanning had FAILED to find this table in passes 7 and 8; the live read settled it. See d1_live_vtable.txt. HYPOTHESIS 0x18011e120 writes the total into one member of the model; the readers of that member offset are the consumers we are looking for. CONTROLS * vtable(0x18021c2a0) slot +0x4e0 must equal 0x18011e120 in the STATIC image too. If the static image disagrees with the live read, the slide or the object type is wrong and nothing below can be trusted. * class_deser("FutSquadSaveServerResponse") must still be 0x180171a60. """ import struct import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres" VT = 0x18021C2A0 try: got = sorted(set(x[0] for x in class_deser("FutSquadSaveServerResponse"))) print("CONTROL FutSquadSaveServerResponse -> %s %s" % ([hex(g) for g in got], "PASS" if 0x180171A60 in got else "FAIL")) s4e0 = qword(VT + 0x4E0) print("CONTROL static vtable %#x slot +0x4e0 = %#x (live said 0x18011e120) %s" % (VT, s4e0, "PASS" if s4e0 == 0x18011E120 else "FAIL")) for off in (0x210, 0x4E0, 0x4E8, 0x250, 0x218, 0x220, 0x160, 0x940, 0x480, 0x1F8): t = qword(VT + off) f = fm.getFunctionAt(addr(t)) print("\n===== vtable +%#05x -> %#x %s" % (off, t, f.getName() if f else "?")) s = dec(t) print("len=%d\n%s" % (len(s), s)) open(OUT + "/d1_model_vt_%03x_%x.txt" % (off, t), "w").write(s) print("\n########## FULL MODEL VTABLE 0x18021c2a0 ##########") for i in range(0, 0x9C0 // 8): t = qword(VT + i * 8) f = fm.getFunctionAt(addr(t)) print(" +%#05x %#x %s" % (i * 8, t, f.getName() if f else "")) print("\nDONE q_pack_inv_9") except Exception: traceback.print_exc()