fifa17-recon: offline FUT squad-shell working + full card-system RE
Milestone: FIFA 17 Ultimate Team boots end-to-end on our offline backend
past every EA gate into the hub and a live Squads editor (correct 4-4-2,
5-star squad, no freezes).
Key findings this session:
- userMassInfo MUST stay {} (any content desyncs the massinfo parser
0x180174630 -> tokenizer busy-loop freeze). Deliver the squad via
GET /squad/0 (fetched on Squads-tab entry) instead.
- Player cards render generic because the card view-model (0x1800d7920)
reads identity/rating/face from a resolved record at item+0x10, filled
by a lookup (0x18011cca0) in the FUT item-definition std::map at
CardsDb+0x160c0 -- which is EMPTY offline -> default blank record.
- Version advertising (itemDbVersion/checkServerDbVersion) is proven inert
(JSON fields routed to the skip handler). Owned items don't auto-trigger
a definition fetch. In-place map overwrite is dead (map stays empty).
- Definition-serving endpoints (item/resource, defid, item?idList) built +
ready; the fetch trigger lives in the packed FIFA17.exe.
New: docs/CARD_SYSTEM.md (findings + ordered next-steps plan for real
player cards: patch-POC, dbdata extractor, drive FIFA17.exe fetch, or
live-memory store injection). Plus tools: fut_seed.py (squad ladder +
definition serving), fifadrive.sh, vgamepad.py, and the login-RE toolset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# trace_login.gdb -- observe FIFA17's Origin login-event path while we push a
|
||||
# <Login IsLoggedIn="true"> Event. Answers the 3-question ladder from the
|
||||
# repack-reverse workflow (REPACK_INTEL.md sec.3):
|
||||
# Q1 does the pushed frame even REACH the sender matcher / dispatcher?
|
||||
# Q2 what sender STRING does the matcher strcmp compare against? (dump the table entry)
|
||||
# Q3 does dispatcher case-2 execute and does m_isLoggedIn actually flip?
|
||||
#
|
||||
# Substitute PIDHERE (trace_login.sh does this) and run:
|
||||
# gdb -batch -x trace_login.gdb 2>&1 | tee /tmp/trace_login.log
|
||||
# Requires kernel.yama.ptrace_scope=0. All VAs are the Wine-flat-mapped FIFA17.exe
|
||||
# (base 0x140000000); stable across launches.
|
||||
set pagination off
|
||||
set confirm off
|
||||
set width 0
|
||||
attach PIDHERE
|
||||
set architecture i386:x86-64
|
||||
|
||||
# CRITICAL for Wine: pass its scheduling signals silently or gdb halts on the
|
||||
# first SIGUSR1 and (-batch) detaches within seconds -> zero hits.
|
||||
handle SIGUSR1 nostop noprint pass
|
||||
handle SIGUSR2 nostop noprint pass
|
||||
handle SIGPIPE nostop noprint pass
|
||||
handle SIG32 nostop noprint pass
|
||||
handle SIG33 nostop noprint pass
|
||||
handle SIG34 nostop noprint pass
|
||||
handle SIG35 nostop noprint pass
|
||||
|
||||
# helper: print a register both as pointer and as a best-effort C string
|
||||
define pstr
|
||||
printf " %s = %#lx", $arg1, $arg0
|
||||
# try to read it as a string; if it faults, gdb prints nothing extra
|
||||
printf " ascii="
|
||||
x/s $arg0
|
||||
end
|
||||
|
||||
# ---- Q2: the <Event> sender matcher / inline strcmp @0x147102880 ----
|
||||
# The docstring: reads sender attr via vtbl+0x70 -> rax; test rax,rax; je fail;
|
||||
# then inline strcmp of that against the handler's registered service name.
|
||||
# We do not know a-priori which regs hold the two pointers, so dump the usual
|
||||
# candidates as strings; whichever are char* reveal BOTH sides of the compare.
|
||||
break *0x147102880
|
||||
commands
|
||||
silent
|
||||
printf "\n>> [Q2] sender matcher 0x147102880 hit\n"
|
||||
pstr "rax" $rax
|
||||
pstr "rcx" $rcx
|
||||
pstr "rdx" $rdx
|
||||
pstr "rsi" $rsi
|
||||
pstr "rdi" $rdi
|
||||
pstr "r8 " $r8
|
||||
pstr "r9 " $r9
|
||||
continue
|
||||
end
|
||||
|
||||
# ---- Q1/element: the <Login> element attribute parser @0x147138660 ----
|
||||
# Reaching here proves the Event matched sender AND element == "Login".
|
||||
break *0x147138660
|
||||
commands
|
||||
silent
|
||||
printf "\n>> [Q1] <Login> parser 0x147138660 ENTERED (sender+element matched)\n"
|
||||
continue
|
||||
end
|
||||
|
||||
# ---- Q3: dispatcher case-2 (OriginEventT::Login) ----
|
||||
# 0x146f1e09e: cmp DWORD PTR [r9],1 (r9 -> parsed IsLoggedIn value)
|
||||
break *0x146f1e09e
|
||||
commands
|
||||
silent
|
||||
printf "\n>> [Q3] dispatcher case-2 reached: IsLoggedIn(parsed)=%d OriginMgr(rcx)=%#lx\n", *(int*)$r9, $rcx
|
||||
printf " current m_isLoggedIn byte [rcx+0x13] = %d\n", *(unsigned char*)($rcx+0x13)
|
||||
continue
|
||||
end
|
||||
# 0x146f1e0ab: mov BYTE PTR [rcx+0x13],1 (SET logged-in = TRUE)
|
||||
break *0x146f1e0ab
|
||||
commands
|
||||
silent
|
||||
printf ">> [Q3] ARM set m_isLoggedIn = 1 *** SUCCESS PATH ***\n"
|
||||
continue
|
||||
end
|
||||
# 0x146f1e0b8: mov BYTE PTR [rcx+0x13],0 (SET logged-in = FALSE / bind failed)
|
||||
break *0x146f1e0b8
|
||||
commands
|
||||
silent
|
||||
printf ">> [Q3] ARM set m_isLoggedIn = 0 (IsLoggedIn parsed as false / bind failed)\n"
|
||||
continue
|
||||
end
|
||||
|
||||
printf "\n=== trace_login armed. Push a Login event now (heartbeat or navigate). Ctrl-C in gdb to detach. ===\n"
|
||||
continue
|
||||
Reference in New Issue
Block a user