Commit Graph

7 Commits

Author SHA1 Message Date
funman300 a5e5628039 tools(re): one-command native proof for resident kit records 2026-08-24 18:00:25 +00:00
funman300 2fc335c37d tools(re): interpret FIFA17 atom mappers and enumerate the resident item map
Two tools, both gated on positive controls because the previous pass
produced a confidently wrong negative result.

atom_mapper_emu.py interprets the atom -> field-id dispatch functions
instead of pattern-scanning them. Its selftest encodes the two decoder
traps that caused earlier mistakes - ModRM rm=5 with mod!=0 is [rbp+disp]
rather than RIP-relative, and a constant may reach its use through a
register - plus the live-verified controls that the item mapper maps atom
568 'players' to field id 1 and atom 11 'actives' to 0. The mandatory
manager atom 424 control still fails as a coverage limit: only 2 of the 52
resolver callers are pure dispatch chains, so the tool refuses to support
any absence claim about the squad mapper.

The live probes enumerate the resident item map at owner+0x160c8, whose
layout came from the lower_bound at 0x180119640: key = wire instance id at
node+0x20, record at node+0x28, count at owner+0x160e8. probe_map2 reaches
22 nodes against a count field of 22, so the enumeration validates itself,
and probe_hunt searches all writable memory with its own in-run positive
control. Result: all four club staff are resident, both kit ids are absent
everywhere, and a lookup miss returns the static sentinel 0x1802c2a28
whose +0x10 is NULL - which is exactly the KIT_SCAN symptom.
2026-08-24 17:24:57 +00:00
funman300 25b7089c54 tools(re): walk the record pool embedded in the club-model owner
Records are 0x180 bytes starting at owner+0x162d8, below the embedded
manager subobject at owner+0x1f9d8. Walking that pool answers whether the
client ever builds a record for an item it was served, independently of
whether the record is filed into a collection.

Result on the live client with the kit selector open: 21 records exist -
18 squad players with category 1, plus one cardtype 10 and two cardtype 4
staff with category 0 - and no cardtype 7 record anywhere, despite two kit
items having been served three times in the same boot.
2026-08-24 17:00:31 +00:00
funman300 8983998707 tools(re): dump resident record fields to settle the kit category gate
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.
2026-08-24 16:54:04 +00:00
funman300 96610ccb16 tools(re): remove the port-8081 DNAT that breaks FIFA 17's roster TLS
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.
2026-08-24 16:36:56 +00:00
funman300 704482be84 tools(re): watch resident club-item population for route correlation
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.
2026-08-24 16:13:36 +00:00
funman300 0997dd3b60 tools(re): census FIFA 17's resident club-item vector
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.
2026-08-24 16:08:02 +00:00