Completed the refusing-modes workflow (ground truth + 4 per-mode investigations + adversarial verify each + synthesis). All four mode families -- Seasons, Draft, SBC/Objectives, Tournaments -- are NOT_SERVER_REACHABLE, HIGH confidence, all four adversarial refutations failed. Live re-confirmed on pid 24653 (slide proven via FNV control): every named mode-gating byte reads ENABLED=1 (IS_FRIENDLY_SEASON_ENABLED +0x1fd3a, IS_TOURNAMENT_QUIT_ENABLED +0x1fd3b, IS_DRAFT_MODE_ENABLED +0x1fd3d, plus the unnamed offline-draft-enable +0x1fd3e) yet the tiles stay greyed. The new lead this pass added -- do the six /hub mode sub-objects gate availability? -- is refuted: friendlySeason/offlineSeason/onlineSeason/draftSummary/tournament/ tournamentProgress carry only stats and display strings, no enabled/available/ unlocked atom. They are cosmetic, exactly like hub.tradePile. The one server-writable input that exists (friendlySeasonsEnabled -> +0x1fd3a via applier FUN_18011dc50) has its sole reader in the packed FIFA17.exe front-end via a vtable getter with no CardsDLL caller, and it is already 1. The refusal is decided in the Denuvo-packed Frostbite front-end, which has no server surface. docs/plan-2026-08-06-refusing-modes.md: full evidence chains, gate-byte table, the six sub-deser field maps, per-mode verdicts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lrx9to3pihN6Sm9sXgc8np
22 KiB
The refusing modes: Seasons, Draft, SBC/Objectives, Tournaments — where the greying is decided
Written 2026-08-06. One reconnaissance pass over the live gate-byte block and the
six /hub mode sub-deserializers, then four parallel per-mode investigations
(Seasons, Draft, SBC+Objectives, Tournaments), each followed by an independent
adversarial verification round. FIFA 17 was running throughout as pid 24653,
sitting at the FUT hub, and was read strictly read-only. No server was restarted,
no server code was changed, no memory was poked, and FIFA was never launched or
killed.
Slide for every live read: live = static - 0x180000000 + 0x6ffffc140000, i.e.
slide 0x6ffe7c140000, re-derived from /proc/24653/maps and proved by
tools/gate_byte_probe.py reporting CONTROL FNV MATCH against the FNV hasher
prologue at 0x180180d00. CardsDLL is mapped from /mnt/games/FIFA 17/ CardsDLL_Win64_retail.dll; the on-disk copy read with objdump is
/tmp/fut/cardsdll.dll, image base 0x180000000. Every address below is
live-verified.
This document answers one question the brief posed: is the refusal of these four mode families decided by a server-reachable input we are failing to send (a hub mode sub-object, a massinfo member, a settings/config field, or a dedicated endpoint), or is it decided in the Denuvo-packed FIFA17.exe / Frostbite front-end with no server surface at all?
1. Headline — final verdicts (after adversarial verify)
Every mode was independently re-derived by a second agent that attempted to refute the first. All four refutations failed. All four verdicts stand.
| Mode | Atoms | Final verdict | Confidence | Verify |
|---|---|---|---|---|
| FUT Seasons (offline + online + friendly) | friendlySeason 0x131, offlineSeason 0x1ec, onlineSeason 0x1f6 |
NOT_SERVER_REACHABLE | HIGH | agrees (SAME) |
| FUT Draft (offline + online) | draftSummary 0xe4 |
NOT_SERVER_REACHABLE | HIGH | agrees (SAME), strengthened |
| SBC + Objectives | objectivesForCurrentUser 0x1e3, allObjectivesForCurrentGameSpaceId 0x15 |
NOT_SERVER_REACHABLE | HIGH | agrees (SAME), prior chain corrected |
| FUT Tournaments | tournament 0x328, tournamentProgress 0x32c |
NOT_SERVER_REACHABLE | HIGH | agrees (SAME) |
There is no server fix for any of the four. Every server-reachable input that
touches these modes is either cosmetic (a hub stat list feeding a caption/count),
an output value the client emits and never branches on, or a settings byte that
is already live=1 while the tile stays greyed. The decision lives in the packed
front-end. This is the same shape as the transfer-market finding of the same day —
except there the switch (userInfo.feature.trade) was ours to flip; here no such
switch exists on the wire.
2. Ground truth
The named gate-byte block (FutDataManagerImpl, live pid 24653)
Names were resolved by finding the config serializer at 0x18006ccd0, which pairs
each IS_*_ENABLED string key (.rdata 0x1801fc118..) with a getter vtable slot,
then decoding each slot's accessor stub (0f b6 81 <disp32> c3) to its model
displacement. All values read live, slide-proven.
| Name | Displacement / slot | Live value |
|---|---|---|
| (unnamed) | +0x1fd24 |
0 |
| (unnamed) | +0x1fd2c |
1 |
| (unnamed) | +0x1fd2d |
1 |
| IS_TRADING_ENABLED | +0x1fd2e (slot+0x270) |
1 |
| (unnamed) | +0x1fd30 |
1 |
| (unnamed) | +0x1fd37 |
1 |
| IS_FRIENDLY_SEASON_ENABLED | +0x1fd3a (slot+0x2b0) |
1 |
| IS_TOURNAMENT_QUIT_ENABLED | +0x1fd3b (slot+0x2b8) |
1 |
| IS_PROCESSING_STATE_ENABLED | +0x1fd3c (slot+0x2c0) |
1 |
| IS_DRAFT_MODE_ENABLED | +0x1fd3d (slot+0x2c8) |
1 |
| (unnamed) | +0x1fd3e (offline-draft-enable) |
1 |
| IS_STORY_MODE_REWARD_ENABLED | +0x1fd3f (slot+0x2d8) |
1 |
| IS_RETURNING_USER_REWARDS_SCREEN_ENABLED | +0x1fd40 (slot+0x2f0) |
0 |
| (unnamed) | +0x1fd41 |
0 |
| (unnamed) | +0x1fd42 (allowGracePeriod, SBC) |
0 |
| (unnamed) | +0x1fd43 |
0 |
| objectives-enable (corrected — see §5.3) | +0x1fd44 |
1 |
| packOpeningAnimation | +0x1fd45 |
1 |
| (unnamed) | +0x1fd46 |
1 |
| (unnamed) | +0x1fd47 |
0 |
| (unnamed) | +0x1fd48 |
1 |
| IS_STORE_ENABLED | computed getter slot+0x280 @0x18011c600 (not a byte field) |
(computed) |
Every named gate byte that governs a refusing mode reads ENABLED=1 live.
The only 0-valued *_ENABLED byte, IS_RETURNING_USER_REWARDS_SCREEN_ENABLED,
does not gate any of the four mode families. This re-confirms the brief's prior
ground truth: the gate-byte layer does not explain the refusals.
The six /hub mode sub-deserializers (/hub parser = FUN_180139610)
The hub parser reads 18 atoms via a running-sum sub/dec ladder; six dispatch to the refusing modes. Each nested sub-deser was read in full. None carries an enable/available/unlocked boolean.
| Atom | Name | Sub-deser VA | Fields (all cosmetic/data) |
|---|---|---|---|
0x131 |
friendlySeason | 0x1801392a0 |
creationTime, dataVersion, opponentPersonaId, opponentUserPoints, round, seasonId, userPoints, defId (8 ints) |
0x1ec |
offlineSeason | 0x18013c3a0 |
divisionId, gamesPlayed, points, progressDataVersion, totalGames (strings) |
0x1f6 |
onlineSeason | 0x18013c3a0 (shared) |
divisionId, gamesPlayed, points, progressDataVersion, totalGames (strings) |
0xe4 |
draftSummary | 0x180138d60 |
draftState (str-enum), gamesWon (int) |
0x328 |
tournament | 0x18013dc00 |
id, assetName, imageFormat, silhouetteName, timeUntilEnd, tournamentType, AMATEUR, live_offline, offerState (display) |
0x32c |
tournamentProgress | 0x18013df20 |
data, tutorialClientData (free-form std::map) |
The recurring trap: several of these desers write a per-field byte
(offline/onlineSeason [r14+0xa]=1; tournament [rdi+0x162]=1) that an early
naive pass could mistake for a JSON enable flag. Every such write is a
parser-local "field present" marker, written identically for every field —
not a JSON-sourced availability input. This is the same class of mistake that
made hub.tradePile look like a gate before it was shown to be a mere count.
3. FUT Seasons — NOT_SERVER_REACHABLE (HIGH)
Atoms: friendlySeason 0x131, offlineSeason 0x1ec, onlineSeason 0x1f6.
Gate byte: IS_FRIENDLY_SEASON_ENABLED +0x1fd3a, live=1.
Evidence chain. The decisive site is the gate byte +0x1fd3a. A whole-.text
grep finds exactly two references:
- Writer
0x18011dd2d:mov byte[rdi+0x1fd3a],alinside settings applierFUN_18011dc50, preceded bycmp dword[rbx+0x58],1 / sete al— the byte is(settings.field+0x58 == 1), sourced from config keyfriendlySeasonsEnabled. This is the only writer. - Reader
0x18011c500:movzx eax,byte[rcx+0x1fd3a]; ret— a standalone vtable getter stub (slot+0x2b0). Its absolute address appears in the file exactly once, at the vtable, and grep finds no call/jmp to0x18011c500anywhere in CardsDLL.text. Its only consumer is the packed FIFA17.exe front-end via vtable dispatch.
The one server-writable input (friendlySeasonsEnabled → +0x1fd3a) is already 1
live, and the tile is still greyed — so the front-end does not gate on this byte
alone; it reads additional non-server state.
- Hub sub-objects carry no enable flag.
offline/onlineSeasonshare deser0x18013c3a0, which FNV-hashes string keys and for each stores a division/games/ points/version stat;friendlySeason 0x1801392a0is 8 numeric stats. The[r14+0xa]=1write is the "field present" marker. These feed a caption/count. - Settings/massinfo:
friendlySeasonsEnabledis the sole season key the applier consumes →+0x1fd3a, already covered. No massinfo member carries a season enable. There is noonlineSeasonEnabled/offlineSeasonEnabledconfig key or gate byte anywhere in the DLL — verify enumerated all 24 gate-region getter stubs and the only season getter is+0x1fd3a. - Dedicated endpoint:
/seasonand/season/userroutes exist inutas_server.py(guarded byFUT_MODES) but the client has never requested them — 0 season hits acrosscaptures/, 486 real ProtoHttp requests over ~30 boots, none for/season. And the tile greys at hub load, before any/seasonrequest could fire. - Front-end: the only season-enable identifiers in the whole DLL are the config
input
friendlySeasonsEnabledand the output getter nameIS_FRIENDLY_SEASON_ENABLED. The viewmodel names (futonlineseasonsviewmodel,futofflineseasonsviewmodel,futfriendlyseasons*viewmodel) live in the Denuvo-packed FIFA17.exe.
Authority boundary. friendlySeasonsEnabled is a server-writable input,
but it is already at ENABLED with its only reader off-DLL (client). Offline/
online seasons have no server surface at all — no config key, no gate byte, no
getter. The grey/refuse decision is client-side.
4. FUT Draft — NOT_SERVER_REACHABLE (HIGH, strengthened by verify)
Atoms: draftSummary 0xe4. Gate byte: IS_DRAFT_MODE_ENABLED +0x1fd3d,
live=1.
Evidence chain. Cross-ref of displacement 0x1fd3d returns exactly two real
sites (a lea to 0x1801fd3d8 and an instruction at address 0x18011fd3d are
coincidental, not xrefs):
- Accessor stub
0x18011c4b0:movzx eax,[rcx+0x1fd3d]; ret(getter vtable.rdata 0x18021c568). - Writer
0x18011dd5a:mov [rdi+0x1fd3d],alin applierFUN_18011dc50,al = (settings[rbx+0x5c]==1)= parsedenableDraftMode.
There is no cmp/test/branch on this byte anywhere. Its only CardsDLL consumer
is the config serializer 0x18006ccd0, which walks the IS_*_ENABLED key table and
call [rax+0x2c8] to emit the value outward. So IS_DRAFT_MODE_ENABLED is an
output the client serializes, not an input any logic branches on.
The verifier strengthened this by finding a consumer the first pass missed: a
flux "DESTINATION" navigation emitter around 0x1800b2700. At 0x1800b2711 it
loads getter slot +0x2c8 (draft-enable, +0x1fd3d) into sil and slot +0x2d0
(offline-draft-enable, +0x1fd3e) into [rsp+0x21]. All six GOTO_DRAFT_DISABLED
emit sites (0x1800b2cb2, 0x1800b2dc9, 0x1800b333b/347, 0x1800b349f/4a7) are
guarded by test sil,sil / cmp [rsp+0x21],0 and route to GOTO_DRAFT_DISABLED
only when those bytes are 0, else to GOTO_DRAFT_OFFLINE/ONLINE. Both bytes are
live=1, so this emitter — the closest thing to a nav decision inside CardsDLL —
already produces the ENABLED destinations, yet the tile is still greyed.
- Hub sub-object
draftSummary 0xe4, member deser0x180138d60: exactly two atoms —draftState 0xe3(STRING → enum decoder0x180138cc0, a resume-state enum: INVALID + 2..8) andgamesWon 0x13a(INT). Wrapper0x18013980cloopsONLINE 0x1f1/SINGLE_PLAYER 0x29d, each →0x180138d60. No enable atom;draftStateis the continue-state read after entry, not a tile gate. - Settings/massinfo: atoms
enableDraftMode 0xf9/enableOfflineDraftMode 0xfa/enableSinglePlayerDraftMode 0xffland on sibling emit-only bytes+0x1fd3d/+0x1fd3e/+0x1fd3cvia the same applier — none branched on. - Dedicated endpoints:
GET /squad/mode/draft/state(deser0x180147070) andPOST /purchase/mode/N/draft(deser0x18014c260) are already routed in utas — but these are the post-click entry/session flow (render the draft screen, buy entry after the tile is pressed), not a tile-availability query. - Front-end: token strings (
USER_HAVE_DRAFT_TOKENS 0x1802055f8,GOTO_DRAFT_DISABLED 0x180209aa8, etc.) are bare key-nameleaemitters with no greying branch. Decision is in the packed FIFA17.exe.
Authority boundary. The two server-writable inputs (enableDraftMode,
enableOfflineDraftMode) are already at their enabled value, and every
CardsDLL consumer of them (config serializer and the navigation emitter) already
treats draft as enabled. The persistent greying is decided client-side on
non-server state.
5. SBC + Objectives — NOT_SERVER_REACHABLE (HIGH, prior chain corrected)
Atoms: objectivesForCurrentUser 0x1e3, allObjectivesForCurrentGameSpaceId 0x15. No IS_OBJECTIVES/IS_SBC gate-byte name exists — the task premise that
these are governed by no named FutDataManagerImpl gate byte is confirmed.
5.1 Hub sub-object = cosmetic list
In FUN_180139610 both objectives atoms share one arm: objectivesForCurrentUser 0x1e3 (0x180139794) and allObjectivesForCurrentGameSpaceId 0x15
(0x1801397ad) both jump to 0x1801398fe, guarded by the parser-local marker
cmp BYTE [rsp+0x21],0x1, calling sub-deser 0x18013a7f0. That deser parses a
nested objectives 0x1e2 array of records (element parser 0x18006c9b0) with
no enabled/available/unlocked atom — it feeds the "MANAGER TASKS N/M" tile
count/caption, the same cosmetic class as hub.tradePile.
5.2 No dedicated endpoint at the hub
The live log across 26+ hub sessions shows the client requests only /hub and
/settings; it never calls /sbs/* (grep count 0) or any /objectives
endpoint. utas_server.py has no /sbs route. No FutGetObjectivesServerResponse
class exists — objectives are ManagerQuests, client-driven. The sbs/* structs
that exist serve challenge content after entry, never polled at the hub.
5.3 The correction (verify fixed the first pass's chain)
The first pass mis-traced objectives to settings field [0x1c] → model +0x1fd28
(default 60). The verifier re-derived the settings jump table (dispatch
0x18013ca1e, byte-idx 0x18013ced4, jtbl 0x18013ce90) and found the truth:
enableObjectives 0xfdandenableObjectivesAsManagerTasks 0xferoute to handler0x18013cabd= clear-only-on-zero into settings field[0x70]; applier0x18011ddc7(cmp [rbx+0x70],1; sete al; mov [rdi+0x1fd44],al) maps it to model gate byte+0x1fd44— which is inside the named gate block (not outside, as the first pass claimed), reads 1 (ENABLED) live, and has exactly one reader DLL-wide: a getter stub0x18011c570returning the byte to the front-end with no internal gating use.- The first pass's
+0x1fd28(default 60) is actuallysquadBuildingSetsGracePeriodMinutes 0x2d0, a numeric grace-period param — behavioral, not availability. - SBC side:
enableSquadBuildingSetsFeature 0x100falls in the dispatch gap (0x100-0x18=0xe8 > 0xe7 → DEFAULT/no handler), as dosquadBuildingSetsClientData 0x2cfandsquadChallenge 0x2d1. Only numeric SBC params have handlers (allowGracePeriod 0x18 → +0x1fd42,allowUntradeable 0x19 → +0x206f8,gracePeriodMinutes 0x2d0 → [0x1c]/+0x1fd28). No SBC availability model byte exists.
So the single server-controllable objectives-enable input (+0x1fd44) is already at
1 yet the tile refuses, and SBC has no server enable surface whatsoever.
Authority boundary. Objectives-enable is a server-writable byte already ON,
read only by the client. SBC availability has no server surface — its enable
key is in the settings dispatch gap and lands on no byte. Decision is client-side
(futmanagerquestsviewmodel; providers FUT_MQ_QUESTS_DATA_DP /
FUT_SQUAD_QUESTS_DP) in the packed FIFA17.exe.
6. FUT Tournaments — NOT_SERVER_REACHABLE (HIGH)
Atoms: tournament 0x328, tournamentProgress 0x32c. Gate byte:
IS_TOURNAMENT_QUIT_ENABLED +0x1fd3b, live=1 — but this governs quitting a
tournament, not tile availability, and no tournamentEnabled atom exists in
docs/fut_atoms.tsv.
Evidence chain.
- Hub sub-objects, both cosmetic.
tournament 0x328deser0x18013dc00writes only display fields: id[rdi+0x150], round[rdi+0x160], timeUntilEnd[rdi+0x158], silhouette-int[rdi+0x15c], string blobs[rdi]/[rdi+0xa8](assetName/silhouette/type), animageFormat=="dds"render bool[rdi+0x163](strcmp vs.rdata 0x180219400), and atournamentTypeenum[rdi+0x154]decoded tolive_offline 0x195/live_online 0x196/offline 0x1e8/online 0x1f0— a categorization, not availability. The[rdi+0x162]=1write is a record-completeness marker (all core fields present), not a JSON enable.tournamentProgress 0x32cdeser0x18013df20builds a std::map (ctor0x1801e5210) of string keysdata 0xc9/tutorialClientData ~0x353— free-form clientData, no enable atom. (The earlier0x28a = returningUserRewardsScreenEnabledlabel was a running-sum mis-decode; the true sum is0xc9+0x28a=0x353 tutorialClientData.) - Massinfo/settings.
tournamentCoins 809 → +0x30andteamOfTournamentWinner 776 (bool) → +0x34appear only in the FutDestroyMatch reward deser0x180121b60— a match payout reached only after you are inside a tournament match; a reward count/trophy flag, not a tile gate. The settings applier switch0x18013c6d0has 42 arms; the only tournament arm istournamentQuitEnabled 0x32D → +0x1fd3b(quit, live=1). - Gate byte
+0x1fd3b: getter stub0x18011c660is the vtable emit accessor the config serializer0x18006ccd0pairs with the JSON key to write it out — the client emits it, does not read it as a server input. Writer0x18011dd3d,al = sete(cmp settings[rbx+off],1), defaults to 1. Already 1, wrong feature. - Dedicated endpoint, never called.
tournament_list(deser0x180169ef0) andtournament_user(deser0x180147cb0) exist inutas_server.pybut grep overcaptures/and the live/tmp/utas_server.log(3224 lines) finds zero ProtoHttp requests for any/tournamentpath across all boots — same as/season. The responses are never consumed. - Front-end. No CardsDLL response deserializer writes any "tournament
available/unlocked" field.
eligibilities 0xf1/unlocks 0x35c/available 0x3eare SBC/store vocab perdocs/ENDPOINT_MAP.md, not wired to tournaments. Decision is in the packed FIFA17.exe.
Authority boundary. The only server-touchable tournament byte
(IS_TOURNAMENT_QUIT_ENABLED) is an emitted output governing a different
feature, already 1. Everything else is cosmetic hub data or post-entry reward data.
Tile availability is decided client-side.
7. What changed vs the prior conclusion
The prior workflow examined only the FutDataManagerImpl gate bytes and
concluded "no server fix" for these modes. This workflow re-opened the question by
chasing the hub-atom lead — the six mode sub-deserializers we do not currently
populate — plus massinfo members, settings arms, and dedicated endpoints.
The hub-atom lead does not change the conclusion for any mode. Per mode:
- Seasons: the hub
friendlySeason/offline/onlineSeasonsub-objects are numeric stat blobs (division/games/points), cosmetic likehub.tradePile. The[r14+0xa]=1byte is a "field present" marker, not a JSON enable. No change — still NOT_SERVER_REACHABLE. - Draft:
draftSummarycarries onlydraftState+gamesWon; verify additionally found the in-DLL navigation emitter already routes to the enabled destination on current live state. No change — verdict strengthened. - SBC/Objectives: the objectives hub arm is a cosmetic list feeding "MANAGER
TASKS N/M". Verify corrected the prior chain — the real objectives-enable byte is
+0x1fd44(inside the gate block, live=1), and SBC's enable key falls in a dispatch gap with no byte at all. No change to the verdict; the correction only hardens it. - Tournaments: both hub sub-objects are display/clientData only. No change.
Net: examining the hub atoms was the right next step, and it closed the lead rather than opening a fix. Every server-reachable surface for these four modes is now accounted for and none is an availability input. The prior "no server fix" conclusion holds, now on much broader evidence.
8. Client-vs-server authority boundaries (explicit)
| Surface | Who writes it | Who reads it | Is it a mode-availability gate? |
|---|---|---|---|
Gate bytes +0x1fd3a/3b/3d/44 etc. |
server (settings applier FUN_18011dc50) |
client (getter stubs, off-DLL vtable dispatch) + config serializer 0x18006ccd0 (emit) |
No — all live=1, never branched on inside CardsDLL |
Hub mode sub-objects (0x131/1ec/1f6/e4/328/32c) |
server (/hub body) |
CardsDLL parsers → cosmetic captions/counts | No — no enable atom in any of the six desers |
[r14+0xa]=1, [rdi+0x162]=1, [rsp+0x21]==1 markers |
CardsDLL parser (local) | same parser | No — "field present" bookkeeping, never JSON-sourced |
Settings config keys (friendlySeasonsEnabled, enableDraftMode, enableObjectives, tournamentQuitEnabled) |
server (/settings) |
applier → gate bytes → client | No — inputs already at enabled; readers are off-DLL |
SBC enable (enableSquadBuildingSetsFeature 0x100) |
— | — | No surface — falls in the settings dispatch gap, lands on no byte |
| Offline/online season enable | — | — | No surface — no config key, no gate byte, no getter |
/season, /tournament, /sbs/* endpoints |
server (utas, routed) | never requested at hub | No — client never polls them; tile greys before any request |
DestroyMatch reward fields (tournamentCoins, teamOfTournamentWinner) |
server (post-match) | reward payout | No — reached only inside a match |
| The greying/refusal decision itself | — | client (Denuvo-packed FIFA17.exe / Frostbite viewmodels) | This is the gate — and it has no server surface |
The single load-bearing fact across all four modes: every server-writable enable input that exists is already at ENABLED live, its only reader is the client, and the tile refuses anyway. No response body we can send flips a state the front-end has already decided.