#!/usr/bin/env python3 """Live read-only probe: did the pushed LSX event actually land? READ-ONLY. Never writes to the game. Safe to run against the live FIFA17.exe while the main session drives it. Watches, once per second: OriginMgr.m_isLoggedIn = *(u8)( *[0x1448acf50] + 0x13 ) Set to 1 by the Origin event dispatcher @0x146f1e060 case 2 (verified: `cmp DWORD PTR [r9],1` -> `mov BYTE PTR [rcx+0x13],1`), which is reached only from a server-pushed . 0 -> 1 means the push was dispatched. CAVEAT (verify: will-it-reach-login): this byte is a PROXY, not the decisive consumer. It is written on the SAME dispatcher line that then falls into the FE re-broadcast loop @0x146f1e116 -> callback 0x147350e30 (packs the event tagged 0xdea12004 and republishes on FIFA's FE event bus). Nothing downstream READS +0x13; the FE broadcast is what actually propagates. So treat 0 -> 1 as "the frame was accepted", and confirm real propagation with a gdb breakpoint on 0x147350e30 (bytes 48 83 ec 58). Also note LoginStatePCLogin's own gate is a DIFFERENT object ([0x144b86bf8]->vtbl+0x60), so this flag flipping does not guarantee GetAuthCode fires. OriginMgr.m_loginError = *(u32)( *[0x1448acf50] + 0x14 ) Cleared to 0 by the same code path. origin "online" byte = *(u8)[0x1448a3ac0] INIT-SET, NOT DIAGNOSTIC: OriginMgr::Initialize writes this to 1 unconditionally @0x146f340e1 (`mov BYTE PTR [rip+...],0x1`), so it does NOT reflect GetInternetConnectedState. Shown for reference only; do not read it as a live state field. FirstPartyAuthTokenRetriever slots retriever = *[0x1448a3b20] + 0x4e98 ; slots at +0x08 and +0x10 DoTick @0x146f199c0 walks these two; if both stay 0 no auth-code request was ever enqueued and RequestAuthCodeSync @0x1470db3c0 is never called. Non-zero here = GetAuthCode is imminent. Usage: python3 origin_login_probe.py [seconds] """ import glob import struct import sys import time ORIGIN_MGR_PP = 0x1448acf50 # -> OriginMgr* ORIGIN_ONLINE_BYTE = 0x1448a3ac0 # FIFA's separate "origin online" flag SDK_PP = 0x1448a3b20 # -> OriginSDK*, retriever at +0x4e98 RETRIEVER_OFF = 0x4e98 def find_pid(): for d in glob.glob("/proc/[0-9]*"): try: if open(d + "/comm").read().strip() == "FIFA17.exe": return int(d.rsplit("/", 1)[-1]) except OSError: pass return None class Mem(object): def __init__(self, pid): self.f = open("/proc/%d/mem" % pid, "rb") def rd(self, va, n): try: self.f.seek(va) b = self.f.read(n) return b if b and len(b) == n else None except OSError: return None def u8(self, va): b = self.rd(va, 1) return None if b is None else b[0] def u32(self, va): b = self.rd(va, 4) return None if b is None else struct.unpack(" 1 else 1e9 pid = find_pid() if pid is None: print("no FIFA17.exe running") return 1 print("pid", pid) m = Mem(pid) t0 = time.time() last = None while time.time() - t0 < limit: mgr = m.u64(ORIGIN_MGR_PP) logged = m.u8(mgr + 0x13) if mgr else None err = m.u32(mgr + 0x14) if mgr else None online = m.u8(ORIGIN_ONLINE_BYTE) sdk = m.u64(SDK_PP) r = (sdk + RETRIEVER_OFF) if sdk else None s1 = m.u64(r + 0x08) if r else None s2 = m.u64(r + 0x10) if r else None row = (logged, err, online, s1, s2) if row != last: print("[%s] OriginMgr=%s m_isLoggedIn=%s loginError=%s " "onlineByte=%s(init-set) | authSlots=%s,%s" % (time.strftime("%H:%M:%S"), hx(mgr), logged, hx(err), online, hx(s1), hx(s2))) # Fire on any non-1 -> 1 transition (including the very first sample # where OriginMgr was still null and last[0] was None), so a # None -> 1 flip is not silently missed. if last is not None and last[0] != 1 and logged == 1: print(" *** m_isLoggedIn -> 1 : the pushed event was " "DISPATCHED (proxy signal; confirm FE re-broadcast at " "0x147350e30). Watch for LSX GetAuthCode next. ***") if last is not None and not (last[3] or last[4]) and (s1 or s2): print(" *** auth-code request ENQUEUED into " "FirstPartyAuthTokenRetriever. ***") last = row time.sleep(1.0) return 0 if __name__ == "__main__": sys.exit(main())