The kit clone driver FUN_1801c3480 gates on record+0x60 == 4, and no
instruction stores that immediate, so the value had to be read off a
genuinely resident record. This dumps cardtype, cardsubtypeid, itemState,
category, teamid and teamkittypetechid for both the player vector and the
club-item vector, resolving the store through the same chain as the census.
Result: all 18 resident players carry category 1 and the five club-item
slots are null, which identifies +0x60 as a per-collection tag rather than
item data.
A client-local nat rule redirecting dport 8081 to the plain-HTTP staging
UTAS host captures the roster/squad-update TLS connection, which is
https://winter15.gosredirector.ea.com:8081/fifa17/fut/rosterupdate.xml.
The staging host completes the TCP handshake then closes on the
ClientHello, so the client retries and shows "An error occurred
downloading the FUT squad update."
Proven by capturing on the server while probing from the client: a
connection addressed to :8081 arrives as dport 8299, while an :8443
control in the same capture yields 75 packets and a clean handshake.
The script deletes only nat rules whose destination port is 8299, then
verifies the endpoint presents CN = winter15.gosredirector.ea.com.
Read-only watcher that waits for FIFA17.exe, re-resolves the club-item store
each tick (the manager is reallocated per login), and emits one timestamped line
per CHANGE. Pair it with
journalctl -u openfut-staging-host -o short-iso
to answer "after which response does a resident club item first appear?" by wall
clock, without having to reverse the constructor first.
Re-resolving per tick matters: the store is reached through
[CardsDLL+0x2e6398] -> vtable[0x4e8], and that getter is a `lea`, so the manager
is an embedded subobject whose address moves with the owner. The tool also
re-checks the module base against a known immediate every time it attaches and
refuses to report from a wrong base.
Verified against the currently live client: club=0/5 players=18/23.
Staging can host the session: every bootstrap route probed answers 200
(userMassInfo, squad/active, squad/list, club/stats/*, hub, user, club arms,
item idList, clubUser, season/list, watchList, purchased/items, settings,
clientdata) with the single exception of /statistics/tournament, which 502s
because staging's Python upstream is deliberately dead and which is not on the
club bootstrap path.
Read-only /proc/PID/mem census of the client's resident club-item store, which
is what the pre-match kit selector actually consumes. No writes.
Resolves the whole chain from static RE rather than guessing offsets:
[CardsDLL+0x2e6398] -> owner object (FUN_18011a830 is a plain
global read)
owner->vtable[0x4e8] -> lea rax,[rcx+0x1f9d8]; ret, i.e. mgr is an
EMBEDDED subobject, not a pointer
mgr+0x108 .. mgr+0x110 -> club-item vector, stride 24
element+0x10 -> the item record (FUN_1800d73d0)
CardsDLL is located by its NEAREST PRECEDING NAMED mapping, because Wine maps PE
sections anonymously and the Wine heap is also rwx, so permissions do not
discriminate code from heap. The base is then sanity-checked against a known
immediate (mov edx,0x7575 at 0x180026fea) and the tool aborts rather than
reporting from a wrong base.
Field offsets are the ones already proven, and nothing else is interpreted:
+0x4c cardtype, +0x50 cardsubtypeid, +0x5c itemState, +0x60 category,
+0x94 teamid, +0xba teamkittypetechid (u16).
FIRST RESULT, live on the client parked at the pre-match kit selector:
players mgr+0x0d8: 23 slots, 18 non-null, all (cardtype 1, subtype 0)
club items mgr+0x108: 5 slots, 0 non-null
Five slots, every item pointer NULL. Five is the club's active club-item set --
home kit, away kit, badge, stadium, ball -- so the client knows it should hold
five and holds none. That is why FUN_1800d73d0 returns the 0x1802c2a28 sentinel
whose +0x10 is NULL, and why the tiles are untextured.