Files
OpenFUT/fifa17-recon/tools/auth_refs.md
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

27 KiB
Raw Blame History

FIFA 17 Blaze — Authentication (component 0x0001) login flow

Date: 2026-07-30 · Scope: what the client needs so it believes it is logged in.


0. Provenance legend

Flag Meaning
(A) binary Reflected/disassembled out of our own FIFA17.exe (live /proc/<pid>/mem, PID 19517). Authoritative for FIFA 17.
(A) wire Our own captured bytes (fifa17-recon/captures/blaze/session/).
(A) clean-room 3P Third-party clean-room reimplementations cloned in the scratchpad. grid-blaze states explicitly it is "a clean-room implementation based entirely on network analysis"; pamplona-future/catalyst-mitm are the same Beat-Revival lineage (packet captures + zeroKilo/jacobtread public TDF work). Mirror's Edge Catalyst = Blaze 15.1.1.0.5, FIFA 17 = Blaze 15.1.1.3.0 — same SDK generation.
(B?) unverified 3P refs/z7_*.txt in the scratchpad — pre-existing reference captures of unknown origin, not produced by us. Treated as a hint only; every z7 claim below was independently re-derived from (A). Where z7 disagrees with our binary, our binary wins (see §6).

No EA/FIFA leaked source was consulted. Nothing below is derived from the 2021 leak.


1. THE HEADLINE: the client is not stalling — it is logging out

Observed frame #9 (captures/blaze/session/auth_cmd0x46.bin):

00 00 00 00 | 00 00 | 00 01 | 00 46 | 00 00 10 | 00 | 00 00
payload=0     meta=0  comp=1  cmd=0x46 msgNum=16 MESSAGE

component 0x0001 (Authentication), command 0x0046 = 70 decimal, EMPTY payload.

Command 70 = logout. Evidence:

  1. (A) clean-room 3Ppamplona-future/src/blaze/components/authentication.ts:10-46 is a full BlazeSDK-15.1.1 Authentication command enum, and it lists logout = 70. That enum is independently corroborated on three other entries by our own data:
    • login = 10 ↔ z7 capture comp=0x0001 cmd=0x000A [LoginRequest]
    • getAuthToken = 36 ↔ z7 capture comp=0x0001 cmd=0x0024 [GetAuthTokenResponse]
    • listUserEntitlements2 = 29grid-blaze/src/main.rs:35 routes 1, 29 => list_entitlments
  2. (A) binary — FIFA 17's own Authentication RPC-name literal pool at 0x14389d6xx0x14389d928 contains exactly the same 33 RPC names as the pamplona enum (acceptLegalDocs … logout … upgradeAccount), i.e. FIFA 17 and MEC ship the same Authentication component definition. "logout" is at 0x14389d8d8.
  3. logout is the only Authentication RPC in that set that takes no request parameters, which matches the observed 0-byte payload. (getAuthToken is also parameterless but is 0x24, not 0x46.)

Confidence: high, cross-validated three ways, but not 100% binary-pinned — FIFA 17's getCommandName for Authentication is not emitted as the lea rax,[rip+str]; ret stub pattern that yielded the Util table, and the name pool has zero code xrefs (searched every lea/mov rip-rel and every aligned+unaligned 8-byte pointer, module-wide). So the id↔name mapping itself is inherited from (A) clean-room 3P, not re-derived from FIFA 17.

What this changes

The client never attempted login (cmd 10). It went:

preAuth → ping → 6× fetchClientConfig (all answered EMPTY) → logout → give up

So it is not waiting on an Authentication response we failed to send. It decided, before issuing any login, that it had nothing to log in with, and tore the Blaze session down. That is consistent with the two-layer gate: it had no Origin auth code to put in LoginRequest.AUTH, and/or the OSDK layer failed to initialise from the empty configs.

(A) binary corroboration of layer 1: FIFA17.exe contains the localisation key TXT_NOT_LOGIN_TO_EBISU at 0x1439633e8 (Ebisu = EA's internal codename for Origin), sitting immediately beside TXT_ORIGIN_GAME_VERSION_OUT_OF_DATE, OSDK_PRESENCE_OFFLINE, OSDK_OL_STATE_NONE. The UI flow events checkOriginConnected / OriginIsOnline / OriginIsOffline live at 0x143b4cb58. This is almost certainly the on-screen message, and it is produced by the Origin/LSX layer, not by Blaze.


2. Authentication component — command ids

Component id 0x0001. Ids (A) clean-room 3P (pamplona enum); every name (A) binary-confirmed present in FIFA 17's RPC name pool unless noted.

Cmd RPC Cmd RPC
10 (0x0A) login 54 disableOptIn
11 (0x0B) trustedLogin 60 (0x3C) expressLogin
20 updateAccount 70 (0x46) logoutobserved
21 upgradeAccount 90 (0x5A) getPersona
29 (0x1D) listUserEntitlements2 100 listPersonas
30 getAccount 101 expressCreateAccount
31 grantEntitlement 230 createWalUserSession
32 listEntitlements 241 acceptLegalDocs
34 getUseCount 242 getEmailOptInSettings
35 decrementUseCount 246 getTermsOfServiceContent
36 (0x24) getAuthToken 260 getOriginPersona
38 getPasswordRules 270 checkEmail
39 grantEntitlement2 280 getPersonaNameSuggestions
43 modifyEntitlement2 290 guestLogin
44 consumecode
45 passwordForgot
47 getPrivacyPolicyContent
48 listPersonaEntitlements2
51 checkAgeReq
52 getOptIn
53 enableOptIn

FIFA 17 additionally ships types with no id in the MEC enum — GetUserAccessTokenRequest/Response, GetUserXblTokenRequest/Response, StressLoginRequest, CheckLegalDocRequest/Response, GetSuggestionsRequest — so FIFA's component is a superset. Ids for those are unknown.

Error-code encoding (A) binary: Blaze error codes are (index << 16) | componentId. Confirmed by the 0x000N7802 constant block at 0x146de08xx sitting alongside the USER_ERR_* name stubs (component 0x7802 = UserSessions). So an Authentication error is 0xNNNN0001.


3. login (1/10) request + response TDF — (A) binary, authoritative

Reflected from FIFA 17's own TDF type descriptors. Fields MUST be emitted in ascending packed-tag order, which for AZ tags is plain alphabetical with ' ' (pad) sorting first — the member tables below are already in that order.

Request — Blaze::Authentication::LoginRequest @ 0x14487ca10 (3 members)

Tag Member Type
AUTH authCode string ← the Origin/Nucleus auth code
EXTB externalBlob blob
EXTI externalId uint64

z7's LoginRequest also carried ACHT{SHID,SKID}FIFA 17 has no ACHT member. z7 is a different title/version. Ignore it.

Response — Blaze::Authentication::LoginResponse @ 0x14487d170 (5 members)

Tag Member Type
ANON isAnonymous bool
NTOS needsLegalDoc bool
SESS userLoginInfo struct UserLoginInfo
SPAM isOfLegalContactAge bool
UNDR isUnderage bool

Important divergence from the MEC emulators. grid-blaze/src/models/authentication.rs:23-50 and pamplona-future/.../authentication.ts:78-128 both emit CNTX, ERRC and a top-level SKEY in the login payload. FIFA 17's LoginResponse has none of those. CNTX/ERRC are the Blaze error metadata block (see grid-blaze/src/packet.rs:62-71 ErrorBody, and its own // TODO: move ErrorBody to metadata; pamplona sets metadataSize: 75 on the login reply). Emit exactly the 5 members above in the payload; leave metadata empty on success. The session key lives at SESS.KEY, not at top level.

SESSBlaze::Authentication::UserLoginInfo @ 0x14487cb00 (8 members)

Tag Member Type
1CON isFirstConsoleLogin bool
BUID blazeUserId int64
FRST isFirstLogin bool
KEY sessionKey string ← the forged session key
LLOG lastLoginDateTime int64
MAIL email string
PDTL personaDetails struct PersonaDetails
UID userId int64

(1 = 0x31 → packed 0x11, which is below A = 0x21, so 1CON correctly sorts first.)

PDTLBlaze::Authentication::PersonaDetails @ 0x14487cab0 (6 members)

Tag Member Type
DSNM displayName string
LAST lastAuthenticated uint32
PID personaId int64
PLAT clientPlatform enum ClientPlatformType
STAS status enum PersonaStatus::Code
XREF extId uint64

getAuthToken (1/36) response — GetAuthTokenResponse @ 0x14487d080

Single member: AUTH authToken : string. (Matches z7 exactly.)

listUserEntitlements2 (1/29) response — Blaze::Authentication::Entitlements @ 0x14487d4e0

Single member NLST : list<Entitlement>.

Blaze::Authentication::Entitlement @ 0x14487d490 (16 members, tag order): DEVI deviceUri(str), GDAY grantDate(str), GNAM groupName(str), ID id(u64), ISCO isConsumable(bool), PID personaId(i64), PJID projectId(str), PRCA productCatalog(enum), PRID productId(str), STAT status(enum EntitlementStatus::Code), STRC statusReasonCode(enum), TAG entitlementTag(str), TDAY terminationDate(str), TYPE entitlementType(enum EntitlementType::Code), UCNT useCount(u32), VER version(u32).

(A) binary enum literals: EntitlementType::Code = ONLINE_ACCESS, TRIAL_ONLINE_ACCESS, SUBSCRIPTIONS, PARENTAL_APPROVAL (@0x1438991e8). EntitlementStatus::Code includes ACTIVE/USED/UNUSED/BANNED/DISABLED.


4. Forging a session with no Nucleus — the recipe

(A) clean-room 3P for the shape of the forgery, (A) binary for every tag.

4.1 What a fake session consists of

grid-blaze/src/routes/authentication.rs:17-133 is the canonical minimal pattern:

  1. Ignore whatever is in LoginRequest.AUTH (it never validates it against Nucleus — it swaps in Discord OAuth; an offline emulator just skips validation entirely).
  2. Mint a User { user_id, persona_id, username } from local storage / config.
  3. Store it on the session (session.data.set_user).
  4. Push the UserAuthenticated notification (see §4.3).
  5. Reply with LoginResponse.

4.2 Session key

Both 3P emulators use a canned literal; the client does not verify it:

  • grid-blaze uses simply "0" for both SKEY and SESS.KEY.
  • pamplona-future uses a realistic-looking "0540000031e5dde8_wT9NlhYTUidv3EMiZo7kaRMYV0x3$x72YrtOC*QU1v".

Real Blaze session keys look like <16 hex>_<44 random base64-ish chars>. Recommend generating that shape once per session and reusing the same string in LoginResponse.SESS.KEY and in the UserAuthenticated notification's KEY — they must match.

4.3 The UserAuthenticated notification — (A) binary, exact

Component 0x7802 (30722, UserSessions), command 0x0008, msgType = NOTIFICATION (2), msgNum = 0.

Payload type is Blaze::UserSessionLoginInfo @ 0x14486f920, 16 members:

Tag Member Type
1CON isFirstConsoleLogin bool
ALOC accountLocale uint32
BUID blazeUserId int64
CGID connectionGroupObjectId ObjectId (triple)
DSNM displayName string
FRST isFirstLogin bool
KEY sessionKey string
LAST lastAuthenticated uint32
LLOG lastLoginDateTime int64
MAIL email string
NASP personaNamespace string
PID personaId int64
PLAT clientPlatform enum
UID userId int64
USTP userSessionType enum UserSessionType
XREF extId uint64

This is a hard confirmation of the z7 reference: z7_userauth_notif.txt (comp=0x7802 cmd=0x0008 [UserSessions::UserAuthenticated]) carries exactly these 16 tags. It also corrects both MEC emulators, which call notification 30722/8 updateHardwareFlags — that is the name of command 8, not notification 8. The payload they build is right; the name is wrong.

4.4 UserSessionExtendedDataUpdate(A) binary

Blaze::UserSessionExtendedDataUpdate @ 0x1448703e0, 3 members: DATA (struct UserSessionExtendedData), SUBS (bool), USID (int64). This is what pamplona/grid-blaze mislabel validateSessionKey and send as notification 30722/1.

Blaze::UserSessionExtendedData @ 0x14487039012 members (tag order): ADDR address(NetworkAddress, union), BPS bestPingSiteAlias(str), CTY country(str), CVAR clientData(variable), DMAP dataMap(map<int64,uint32>), HWFG hardwareFlags, ISP iSP(str), PSLM latencyList(list<int32>), QDAT qosData, TZ timeZone(str), UATT userInfoAttribute(u64), ULST blazeObjectIdList(list<ObjectId>).

Two FIFA-17-specific deltas vs the MEC emulators: FIFA has PSLM (they don't), and FIFA has BPS as a top-level string member (they wrap it inside the ADDR union as BPS ). Follow the FIFA layout.

Blaze::Util::NetworkQosData (QDAT) @ 0x14486e680: BWHR u32, DBPS u32, NAHR u32, NATT enum NatType, UBPS u32.

4.5 Persona identity must match the Origin emu

From stp-origin_emu.ini [Globals]: PersonaId=33068179, PersonaName=CAGE, Language=en_US. Use BUID = PID = 33068179, DSNM = "CAGE", NASP = "cem_ea_id" (namespace confirmed by our own PreAuthResponse being accepted), PLAT = 4 (pc), USTP = 0. Mismatch trips AUTH_ERR_INVALID_PERSONA / AUTH_ERR_USER_DOES_NOT_MATCH_PERSONA / AUTH_ERR_PERSONA_NOT_FOUND — all present in the binary at 0x146e0ed91, 0x146e0eebf, 0x146e0ecf8.


5. Util::fetchClientConfig (9/1) — what to return

Request: Blaze::Util::FetchClientConfigRequest = { CFID: string }. Response: Blaze::Util::FetchConfigResponse @ 0x1448752e0 = single member CONF : map<string,string>. (Note: not wrapped in an extra struct — the extra nesting only exists inside PreAuthResponse, where CONF is a FetchConfigResponse struct whose own single member is also called CONF. Easy to get wrong.)

5.1 The BlazeSDK section (returned inside PreAuthResponse.CONF.CONF)

Our capture shows the client asks for this inside the preAuth request: FCCR { CFID = 'BlazeSDK' }. (A) binary — these are the config keys FIFA 17 actually parses (string literals present in the exe; the "absent" ones are MEC-only and are silently ignored by FIFA 17):

Key Present in FIFA17.exe Owner (from adjacent literals)
connIdleTimeout 0x1438a0a58 ConnectionManager
defaultRequestTimeout 0x1438a0a40 ConnectionManager
pingPeriod 0x1438a0a30 ConnectionManager
autoReconnectEnabled 0x1438a0a68 ConnectionManager
maxReconnectAttempts 0x1438a0a80 ConnectionManager
enableQosFirewallTest 0x1438a09f0 ConnectionManager/QosManager
enableQosBandwidthTest 0x1438a0a08 ConnectionManager/QosManager
nucleusConnect 0x14389fef8 LoginStateMachineImpl
nucleusConnectTrusted 0x14389fdf8 LoginStateMachineImpl
associationListSkipInitialSet 0x143b6eb88 AssociationListAPI
userManagerMaxCachedUsers UserManager
voipHeadsetUpdateRate VoIP
nucleusPortal absent MEC-only
nucleusProxy absent MEC-only
bytevaultHostname / bytevaultPort / bytevaultSecure absent MEC-only
xblTokenUrn, xboxOneStringValidationUri, xlspConnectionIdleTimeout absent MEC/Xbox-only

How nucleusConnect is used — (A) binary. The LoginManagerImpl / LoginStateMachineImpl string cluster at 0x14389fd500x14389fef8 reads, in order:

LoginManagerImpl · LoginData · LoginStateMachineImpl · LoginStateMachine
nucleusConnectTrusted · "%s/connect/token" · "grant_type=client_credentials"
recvBuf · "NEXUS_S2S " · "\"access_token\" : \"" · headers
LoginStateBase::buffer
LoginStateAuthenticated::mTermsOfServiceBuffer
LoginStateAuthenticated::mPrivacypolicyBuffer
nucleusConnect

So the client builds <nucleusConnect>/connect/token, POSTs grant_type=client_credentials, and scrapes "access_token" : " out of the JSON reply. Point nucleusConnect / nucleusConnectTrusted at our own HTTPS listener and serve a canned OAuth token JSON — that is the Blaze-side half of defeating auth. There is a NEXUS_S2S header value involved. LoginStateAuthenticated also buffers ToS + privacy-policy text, which is why NTOS (needsLegalDoc) in LoginResponse should be 0.

Recommended BlazeSDK map (keep it minimal — FIFA 17 ignores unknown keys):

associationListSkipInitialSet = 1
autoReconnectEnabled          = 0
connIdleTimeout               = 90000000
defaultRequestTimeout         = 30000000
enableQosBandwidthTest        = false
enableQosFirewallTest         = false
maxReconnectAttempts          = 0
nucleusConnect                = https://accounts.ea.com      <- repoint to us
nucleusConnectTrusted         = https://accounts2s.ea.com    <- repoint to us
pingPeriod                    = 20000000
userManagerMaxCachedUsers     = 0

5.2 The OSDK_* sections — (A) binary

OSDK_CORE, OSDK_CLIENT, OSDK_NUCLEUS, OSDK_WEBOFFER, OSDK_ABUSE_REPORTING, OSDK_TICKER are literals at 0x143962be8..0x143962c40, and they sit inside the ResourceLoader / NETRESOURCE / LoadResourceFromMultiUrl / netres string cluster. Adjacent source path: .../extern/OSDK/8.01.03.00-fifa.01/source/common/presencedownloadmanagerabstract.cpp → the game embeds OSDK 8.01.03.00-fifa.01.

Meaning: these sections are game-tuning key/value maps consumed by FIFA's OSDK layer (a ResourceLoader with states LOADING/LOADED/NOT_FOUND), not Blaze plumbing. The key namespace is OSDK_*; confirmed examples of real config keys (as opposed to the many OSDK_* localisation ids):

OSDK_PRESENCE_DELAY, OSDK_PRESENCE_POLL, OSDK_ABUSE_NUM_TYPES, OSDK_ANTIGRIEFING_MAX_COUNT, OSDK_ARENA_ENABLED, OSDK_ARENA_CHALLENGE_SCHEDULE_URL, OSDK_ARENA_REGISTER_EMAIL_URL, OSDK_CLUBS_MAX_SEARCH_RESULT, OSDK_CLUBS_LOAD_MEMBER_PAGE_SIZE, OSDK_CLUBS_MAX_USERS_FOR_GAME, OSDK_CLUBS_LEADERBOARD_CLUB_MAX, OSDK_CLUBS_INCOME_SEARCH_MAX.

There is no nucleusConnect-equivalent in OSDK_NUCLEUS that we could find; the Nucleus URLs are BlazeSDK-level (§5.1). OSDK_NUCLEUS most likely holds Nucleus tuning (poll intervals, retry counts).

Assessment: returning an empty CONF map for the OSDK_* sections is probably not what killed us — these are tuning values that fall back to defaults. Answering them non-empty is cheap insurance, but the real blocker is layer 1 (Origin/LSX GetAuthCode / OriginIsOnline). Also note OSDK_XMS_ABUSE_REPORTING (which the client requested) is not in the literal block — so the section list is built dynamically.

5.3 IdentityParams

Not requested by FIFA 17 in our capture, but both MEC emulators and z7 answer it identically: display = console2/welcome, redirect_uri = http://127.0.0.1/success.


6. Post-login RPC order

(A) wire (ours) for everything up to logout. Beyond that, (A) clean-room 3P + z7, since we have never got past it.

Observed by us (FIFA 17):

1  9/7   Util::preAuth            (req carries FCCR{CFID='BlazeSDK'}, CINF{CLNT='FIFA17', BSDK='15.1.1.3.0'})
2  9/2   Util::ping
   -- reconnect --
3  9/1   Util::fetchClientConfig   CFID=OSDK_CORE
4  9/1                             CFID=OSDK_CLIENT
5  9/1                             CFID=OSDK_NUCLEUS
6  9/1                             CFID=OSDK_WEBOFFER
7  9/1                             CFID=OSDK_ABUSE_REPORTING
8  9/1                             CFID=OSDK_XMS_ABUSE_REPORTING
9  1/70  Authentication::logout    <-- gave up here

Expected happy path (compiled from grid-blaze/src/main.rs:32-44, pamplona-future/.../util.ts:41-74, and the z7 msgNum ordering):

9/7    Util::preAuth              -> PreAuthResponse
9/1    Util::fetchClientConfig    -> FetchConfigResponse   (xN)
1/10   Authentication::login      -> LoginResponse
       << NOTIFY 30722/8  UserAuthenticated (UserSessionLoginInfo)
9/8    Util::postAuth             -> PostAuthResponse
       << NOTIFY 30722/1  UserSessionExtendedDataUpdate
       << NOTIFY 30722/2  (UserAdded — DATA + USER)
1/29   Authentication::listUserEntitlements2 -> Entitlements{NLST}
9/28   Util::setClientState       -> empty reply     (req: MODE=1, STAT=0)
1/36   Authentication::getAuthToken -> {AUTH: "<token>"}
9/10   Util::userSettingsLoad     -> UserSettingsResponse
25/6   AssociationLists::getLists -> GetListsResponse{LMAP}
30722/20 UserSessions::updateNetworkInfo -> empty reply
9/22   Util::setClientMetrics     -> empty reply
9/2    Util::ping                 (every pingPeriod)

Ordering caveat: in grid-blaze the UserAuthenticated notification is pushed from inside the login handler, before the login reply is written (routes/authentication.rs:128-132); pamplona writes the reply first, then the notification (authentication.ts:54-57). Both apparently work. postAuth pushes its two notifications around its reply (util.ts:60-64: extendedDataAttribute → reply → extendedData).

Util::postAuth (9/8) — (A) binary

Request Blaze::Util::PostAuthRequest @ 0x1448757c0: DSUI dirtySockUserIndex(int32), UDID uniqueDeviceId(string) — only 2 members. (z7 shows a third MAC field; FIFA 17 does not have it. Another z7 mismatch.)

Response Blaze::Util::PostAuthResponse @ 0x144875810: TELE, TICK, UROP.

  • TELE = GetTelemetryServerResponse @ 0x144875470, 15 members: ADRS(str) ANON(bool) DISA(str) EDCT(bool) FILT(str) LOC(u32) MINR(bool) NOOK(str) PORT(u32) SDLY(u32) SESS(str) SKEY(str) SPCT(u32) STIM(str) SVNM(str).
  • TICK = GetTickerServerResponse @ 0x1448754c0, 3 members: ADRS(str) PORT(u32) SKEY(str).
  • UROP = UserOptions @ 0x144875770, 2 members: TMOP(enum TelemetryOpt) UID(int64).

Bug in our current responder

Blaze::Util::PingResponse @ 0x144875560 has exactly one member: STIM (serverTime, uint32). blaze_responder_v2.py sends STIM and TIME. TIME is not a member of FIFA 17's PingResponse (it is MEC's). Harmless-ish, but drop it.


7. Association lists (25/6) — for completeness

(A) clean-room 3P only (grid-blaze/src/models/association_lists.rs). Response GetListsResponse{ LMAP: list<AssociationList> }; each entry is INFO{ BOID(ObjectId) FLGS(u8) LID{LNM(str) TYPE(u8)} LMS(u32) PNAM(str) PRID(u8) PRMS(u32) }, OFRC, TOCT. MEC ships friendList(type 1), followList(type 5), communicationBlockList(type 4). FIFA's list names are not verified — do not assume.


  1. Fix layer 1 first. The client logs out before trying to log in, and the on-screen string is TXT_NOT_LOGIN_TO_EBISU. Make the in-process LSX server on 127.0.0.1:4216 answer OriginIsOnline / GetInternetConnectedState as online, and GetAuthCode with any non-empty code. Without that there is nothing to put in LoginRequest.AUTH and Blaze work is unreachable.
  2. Implement Util::fetchClientConfig returning a non-empty CONF map ({CONF: map<string,string>}) for every CFID, even if only a couple of keys — removes it as a variable.
  3. Put nucleusConnect / nucleusConnectTrusted in the preAuth CONF map pointing at our own listener, and serve POST <base>/connect/token returning {"access_token" : "<anything>"}.
  4. Implement Authentication::login (1/10) → the 5-member LoginResponse above, then push UserAuthenticated (30722/8, 16 members) with the same session-key string, using PersonaId 33068179 / "CAGE".
  5. Implement Util::postAuth (9/8), Authentication::listUserEntitlements2 (1/29) returning one ONLINE_ACCESS entitlement (PJID/offer id 1027460, TYPE=ONLINE_ACCESS, STAT=ACTIVE), Util::setClientState (9/28) empty, Authentication::getAuthToken (1/36) → {AUTH}.
  6. Keep handling Authentication::logout (1/70) with an empty reply — but treat receiving it as a failure signal in the responder log, not a normal step.

9. File index

Cloned reference repos (scratchpad):

  • /tmp/.../scratchpad/grid-blaze/src/routes/authentication.rs — login handler + entitlements
  • /tmp/.../scratchpad/grid-blaze/src/models/authentication.rs — AuthResponse/Entitlement serialisers
  • /tmp/.../scratchpad/grid-blaze/src/routes/util.rs, src/models/util.rs — preAuth/postAuth/fetchClientConfig
  • /tmp/.../scratchpad/grid-blaze/src/models/user_sessions.rs — the 4 session notifications
  • /tmp/.../scratchpad/grid-blaze/src/main.rs:32-44 — full route table
  • /tmp/.../scratchpad/grid-blaze/src/packet.rs — Fire2 framing (matches our corrected layout)
  • /tmp/.../scratchpad/pamplona-future/src/blaze/components/authentication.ts:10-46the command-id enum
  • /tmp/.../scratchpad/pamplona-future/src/blaze/components/util.ts:17-39 — Util command enum
  • /tmp/.../scratchpad/pamplona-future/src/blaze/components/user-sessions.ts:21-50 — UserSessions command enum
  • /tmp/.../scratchpad/catalyst-mitm/blaze/interceptor.ts — redirector request XML shape
  • /tmp/.../scratchpad/tdf/src/{writer,reader,tag,types}.rs — reference TDF codec

Our own artefacts:

  • /home/alex/Documents/OpenFUT/fifa17-recon/captures/blaze/session/session_full.log — the live session
  • /home/alex/Documents/OpenFUT/fifa17-recon/captures/blaze/session/auth_cmd0x46.bin — the logout frame
  • /home/alex/Documents/OpenFUT/fifa17-recon/tools/preauth_schema_reflection.md — prior reflection write-up + Util command table

Tooling written/used this pass (scratchpad):

  • reflect2.py — TDF type-descriptor walker (raw <va> / index <pat> / byname)
  • authscan.py, stubrange.py, allstubs.txtlea/ret command-name stub recovery
  • findstr2.py, nameblk.py, allstr.py, xref.py, rvatab.py, notifid.py — string/xref/constant hunting
  • clusters.json — all 124 name-stub clusters (includes the UserSessions notification-name cluster ServerDraining, UserAdded, UserAuthenticated, UserRemoved, UserSessionExtendedDataUpdate, UserUnauthenticated @ 0x146de19c0)

Descriptor VAs (FIFA17.exe, base 0x140000000): LoginRequest 0x14487ca10 · LoginResponse 0x14487d170 · UserLoginInfo 0x14487cb00 · PersonaDetails 0x14487cab0 · GetAuthTokenResponse 0x14487d080 · ExpressLoginRequest 0x14487d0d0 · Entitlement 0x14487d490 · Entitlements 0x14487d4e0 · AccountInfo 0x14487c810 · PersonaInfo 0x14487c7c0 · UserSessionLoginInfo 0x14486f920 · UserSessionLogoutInfo 0x14486f970 · UserSessionExtendedData 0x144870390 · UserSessionExtendedDataUpdate 0x1448703e0 · Util::PostAuthRequest 0x1448757c0 · Util::PostAuthResponse 0x144875810 · Util::FetchConfigResponse 0x1448752e0 · Util::PingResponse 0x144875560 · Util::PreAuthResponse 0x144875600