Files
OpenFUT/fifa17-recon/tools/trace_login.gdb
T
funman300 6ddd5e9d47 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
2026-08-01 20:24:30 -07:00

90 lines
3.1 KiB
Plaintext

# 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