From 6270c372081d76423fe253d556c85d43d320a9f3 Mon Sep 17 00:00:00 2001 From: funman300 Date: Sun, 2 Aug 2026 21:43:52 -0700 Subject: [PATCH] fifa17-recon: store-enable live poke (online-readiness gate) The FUT store 'not available' is FIFA's online-mode readiness gate (FUT::CompetitionManager), not a config flag. tools/store_enable_poke.py finds CardsDLL's live base and patches the 3 gate methods (0x1800f7fb0 IS_EASTORE_SERVICE_READY, 0x1800fb850 IS_STORE_ENABLED, 0x180100500 IS_COIN_PURCHASABLE) to 'mov eax,1; ret'. Reversible (saves originals; 'restore' subcommand; FIFA restart clears it). Needs ptrace_scope=0 + FIFA running. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o --- fifa17-recon/tools/store_enable_poke.py | 76 +++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 fifa17-recon/tools/store_enable_poke.py diff --git a/fifa17-recon/tools/store_enable_poke.py b/fifa17-recon/tools/store_enable_poke.py new file mode 100644 index 0000000..4bf8d23 --- /dev/null +++ b/fifa17-recon/tools/store_enable_poke.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Force the FIFA 17 FUT store OPEN by patching CardsDLL's online-readiness gate. + +The "store is not available" screen is gated by FIFA's online-mode manager +(FUT::CompetitionManager). Three CardsDLL methods report store-enabled and only +return true once that manager reaches the online "service ready" state -- an +online/Blaze wall we can't reach offline. This pokes them to return true. + +Reversible: originals are saved to /tmp/orig_.bin; `restore` puts them back; +a FIFA restart also clears the patch (live-memory only, per session). + +Needs ptrace_scope=0 (tools/root_arm.sh) and FIFA running. Run as YOUR action: + !python3 tools/store_enable_poke.py # patch (open the store) + !python3 tools/store_enable_poke.py restore # undo + +Static gate methods (CardsDLL image base 0x180000000), patched to `mov eax,1; ret`: + 0x1800f7fb0 IS_EASTORE_SERVICE_READY (mgr[0x6d4]==0x1fbd0) + 0x1800fb850 IS_STORE_ENABLED (online-mode state stack non-empty) + 0x180100500 IS_COIN_PURCHASABLE (commerce config) +""" +import sys, glob, os + +IMG_BASE = 0x180000000 +GATES = { + 0x1800f7fb0: "IS_EASTORE_SERVICE_READY", + 0x1800fb850: "IS_STORE_ENABLED", + 0x180100500: "IS_COIN_PURCHASABLE", +} +RET_TRUE = bytes.fromhex("b801000000c3") # mov eax,1 ; ret +DLL_MATCH = "CardsDLL" + + +def find_pid(): + for d in glob.glob('/proc/[0-9]*'): + try: + if open(d + '/comm').read().strip() == 'FIFA17.exe': + return int(d.split('/')[-1]) + except Exception: + pass + raise SystemExit("FIFA17.exe not running") + + +def cardsdll_base(pid): + for line in open(f'/proc/{pid}/maps'): + if DLL_MATCH in line: + return int(line.split('-')[0], 16) # lowest mapping = module base + raise SystemExit("CardsDLL not mapped in FIFA process") + + +def main(): + restore = len(sys.argv) > 1 and sys.argv[1] == "restore" + pid = find_pid() + base = cardsdll_base(pid) + print(f"FIFA pid={pid} CardsDLL base={base:#x} ({'RESTORE' if restore else 'PATCH'})") + mem = f'/proc/{pid}/mem' + for va, name in GATES.items(): + live = base + (va - IMG_BASE) + origf = f'/tmp/orig_{live:x}.bin' + if restore: + if not os.path.exists(origf): + print(f" {name}: no saved original, skip"); continue + data = open(origf, 'rb').read() + else: + with open(mem, 'rb') as f: + f.seek(live); orig = f.read(len(RET_TRUE)) + open(origf, 'wb').write(orig) + data = RET_TRUE + with open(mem, 'r+b') as f: + f.seek(live); f.write(data) + f.seek(live); chk = f.read(len(data)) + print(f" {name:26s} @ {live:#x} -> {chk.hex()}") + print("Done." + ("" if restore else " Now open the FUT Store in-game.")) + + +if __name__ == "__main__": + main()