#!/usr/bin/env python3 """SBC cache populate/arm probe + poke (FIFA 17 CardsDLL). READ-ONLY BY DEFAULT. The write path exists for the human's morning test but is NEVER reached unless you pass --arm AND --i-mean-it. Running with no args only READS /proc//mem (O_RDONLY) and prints what a poke WOULD do. Object graph (all static VAs, image base 0x180000000; add the live slide): A = FUT root singleton = *(0x1802e6398) (getter 0x18011a830) B = SBC TTL cache = A + 0x1f9d8 (vtable 0x1801fae70) B+0x08 collection ptr, B+0x20 QPC deadline, B+0x28 ready byte isValid = 0x180065d40 (B.vtable[+0x08]); clear = 0x180065d20 (B.vtable[+0x10]) HUB TTL cache = A + 0x1fd70 (same class, armed online) SBC set-data req mgr = A + 0x2a0 (vtable 0x18022be90, ctor 0x18016fac0) Populate path (normal, online): fetch sbs/sets -> req mgr A+0x2a0 -> response obj (factory 0x18016fca0, vt 0x18022be80) -> SAX drive 0x18016c330 -> top deser 0x18017b2b0 (which does service.[+0x9b0] to get the SBC manager, then) category deser 0x18017ab80 / set deser 0x18017ad60 -> finalizers 0x180160e00 / 0x180160e50 / 0x180161020 build the SBC manager's category array (stride 0xf0) + set array (stride 0x3570); select/rebuild 0x180160b80 -> generic cache commit copy-assigns a stack temp {collection, deadline, ready=1} into B (assign 0x1800c21a0), arming B+0x28 and pointing B+0x08 at the manager data. The UI renders by polling isValid(B) each frame and iterating *(B+0x08). Arming B+0x28 alone (see --arm-flag-only) opens the menu but draws EMPTY (collection NULL). A populated render needs *(B+0x08) to point at a real set/category collection. """ import os, struct, sys SLIDE_KNOWN = 0x6ffe7c140000 # informational; actual slide is read from maps A_SINGLETON = 0x1802e6398 B_OFF = 0x1f9d8 HUB_OFF = 0x1fd70 REQMGR_OFF = 0x2a0 CTRL_VA = 0x180180d00 def find_pid(): for d in os.listdir('/proc'): if d.isdigit(): try: if open('/proc/%s/comm' % d).read().strip() == 'FIFA17.exe': return int(d) except Exception: pass return None def main(): arm = '--arm' in sys.argv flag_only = '--arm-flag-only' in sys.argv confirm = '--i-mean-it' in sys.argv pid = find_pid() if not pid: print("FIFA17.exe not running -> nothing to read. (static analysis is authoritative)") return base = None for ln in open('/proc/%d/maps' % pid): if 'CardsDLL' in ln: base = int(ln.split('-')[0], 16); break if not base: print("CardsDLL not mapped yet (client not in Ultimate Team). Skip live step.") return slide = base - 0x180000000 fdr = os.open('/proc/%d/mem' % pid, os.O_RDONLY) rd = lambda va, n: os.pread(fdr, n, va) q = lambda va: struct.unpack('