Files
funman300 3649c3d6f0 docs: Blaze handshake reference, connection-gate findings, closure write-up
- blaze-handshake.md: three-layer LSX/Blaze-core/UTAS model + six-step Blaze
  session ordering, tagged CONFIRMED/SHAPE/UNKNOWN.
- connection-gate-findings.md: accumulated RE of the online-connect gate.
- closure-and-preservation.md: the capstone. Records what was achieved
  (clean-room LSX/EbisuSDK emulator booting FIFA to an authenticated offline
  menu), the wall reached from three directions, the dial-initiation gate RE
  as the final chapter (Outcome B, circular), and the boundary. Clean-room
  throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:58:37 -07:00

9.6 KiB
Raw Permalink Blame History

Blaze Handshake — Reference & Milestone Map

This is the reference for the blaze_brain arc: emulating the EA Blaze backend well enough for FIFA 23 FUT to get past "FUT is connecting to the EA Servers…". It replaces the earlier in-process forcing arc, which concluded that the game's online dispatch scaffold cannot be filled by forcing local state — it fills only when a real Blaze exchange happens.

Every claim below is tagged so that on a re-read you can see instantly what is grounded in our own reverse engineering versus what is general public knowledge of Blaze's protocol shape versus what still needs a capture:

  • CONFIRMED — established by our own RE in this project (live memory reads, objdump, hook logs).
  • SHAPE — the general shape of EA's Blaze protocol from public community knowledge. The ordering and purpose are reliable; exact numeric IDs and field tags for FIFA 23's specific Blaze version are not implied by a SHAPE tag.
  • UNKNOWN — must be captured from the live client before it can be implemented.

Clean-room discipline: nothing here derives from leaked EA source. The Blaze protocol shape is reconstructed from public community reverse-engineering of EA titles and our own observations of this client.


The three layers that all say "connecting to EA"

"Connecting to EA" is not one thing. Three distinct systems wear that label, stacked on top of each other, and it is easy to confuse a stall in one for a stall in another. Understanding which layer the FUT spinner belongs to is the whole point of this document.

Layer 0 — LSX (Local Socket eXchange). CONFIRMED working. This is the Origin/EA-App desktop-client local API: entitlements, the local login handshake, and GetAuthCode. It is not Blaze — it is a localhost protocol the game uses to talk to whatever is standing in for the EA App. Our native bridge answers it on ports 3216/3217, and answering it is what got the client to the main menu with a working first-party login. Layer 0's output that matters to Blaze is a Nucleus auth code / access token: the credential the client will later present to the Blaze authentication component.

Layer 1 — the Blaze core session. This is the wall. Blaze is EA's online backend framework. Reaching it is a multi-step handshake (detailed below) that starts by asking a redirector where the real server is, connecting there over TLS, then negotiating configuration, authenticating with the Layer-0 auth code, and finally bringing the session fully online. Until this completes, nothing above it can work. Everything we have observed says the client currently never even starts this — see the transport finding below.

Layer 2 — FUT / UTAS. SHAPE. FIFA Ultimate Team specifically is served by the EASFC Blaze component and by UTAS, a separate HTTPS REST service (utas.*.fut.ea.com). Once a Blaze session exists, the FUT client authenticates to UTAS (POST /ut/auth) with the session credential to obtain a FUT session id, and only then does it load squad/club data. The FUT spinner in the screenshot lives at the top of this stack, but it cannot clear until Layer 1 is answered — so "minimal Blaze handshake" means Layer 1, and Layer 2 is a second, HTTP-shaped project that comes after.


Where the client currently stalls — the transport finding

CONFIRMED. Across a full menu-plus-FUT-attempt session we observed zero getaddrinfo calls and zero outbound sockets, and — via the ELEM_WATCH probe — the game's per-connection message-handler dispatch container at element[0]+0x40 stayed a clean, empty, default-constructed vector the entire time, including while the FUT spinner was on screen. connectState.ctor fired (the client builds connect-state objects) but registered nothing into that container.

The mechanical reading: that container is populated by an actual Blaze message exchange — handlers register as component notifications arrive during session bring-up (Layer-1 step 6 below). No Blaze reply → no handler registration → empty container → spinner spins forever. The spinner is the Layer-1 wall.

This leaves two possible transport situations, and Milestone 0 exists solely to decide which one we are in (see the Milestones section):

  • (a) the client expects Blaze on a hardcoded local endpoint and would dial it if a listener were there, or
  • (b) the client only dials the Blaze redirector after an in-process bootstrap (the dial handler 0x144f4d360 CONFIRMED) fires — in which case the transport wall and the bootstrap wall are the same wall.

Layer 1 — the minimal Blaze handshake ordering

Blaze is a framed binary protocol. Each message is a header — length, component id, command id, message type (request / response / notification / error), error code, and a message/sequence id — followed by a TDF-encoded body (EA's tag/type/value binary serialization). SHAPE. The exact numeric component/command ids and TDF field tags for FIFA 23's Blaze version are UNKNOWN until captured.

These are the six steps a responder must satisfy, in order, to bring a session online:

  1. Redirector::getServerInstance SHAPE — the client asks the redirector for the address of the real Blaze server, naming the FIFA 23 service/SKU. The responder returns a ServerInstanceInfo TDF containing a host:port (point it at ourselves) plus an SSL flag. Without a valid address the client has nowhere to go.
  2. connect + TLS CONFIRMED (bypass) — the client opens a TLS connection to the returned address. Our ProtoSSL/cert-verify bypass is already in place, so our listener can terminate TLS.
  3. Util::preAuth SHAPE — the client sends its config/version/locale; the responder returns server config: the component list, ping-site list, telemetry settings (disable), and a misc config bundle. The client uses the component list to know what exists; an empty or malformed response here stalls it.
  4. Authentication::login CONFIRMED (GetAuthCode feeds here) + SHAPE — the client authenticates, presenting the Nucleus auth code from Layer 0 (SSO). The responder returns the session: BlazeId, session key, persona, entitlements. A failure here is what surfaces as "EA Servers are down."
  5. Util::postAuth SHAPE — after auth, the client finalizes the session; the responder returns post-auth config (UserManager, association lists, telemetry, ticker/PIN).
  6. UserSessions::updateNetworkInfo / updateHardwareFlags, then a UserSessionExtendedDataUpdate notification CONFIRMED (container is Blaze-downstream) + SHAPE (which command) — the client publishes its network/ hardware info and the server pushes extended session data. The client flips to "online" on receipt of the extended-data notification — and this is the exact moment the element[0]+0x40 container we watched would begin filling, as handlers register for the arriving component notifications.

At step 6 the top-level "connected to EA" clears, and Layer 2 (UTAS) can begin.

Why step 6 is the Milestone-1 success signal

The container-fill and the "online" flip are the same event: handlers register because Blaze notifications arrived. That gives blaze_brain a precise, already-built success detector. The first real win is not "full FUT works" — it is a single line in the hook log:

ELEM_WATCH: [elem+0x40] CHANGED! …

That line means the client accepted our Blaze replies and started registering handlers — i.e. steps 16 were convincing enough to bring the session online. The ELEM_WATCH probe that emits it is already written and deployed; it is our instrumentation for blaze_brain regardless of anything else.


Milestones

  • M0 — transport reachability (this task). Read-only. Confirm whether the client attempts any Blaze-flavored transport (situation a) or none at all (situation b). Gates every decision below. See the M0 section in the hook docs / launch notes.
  • M1 — minimal session bring-up. Stand up a listener at the M0-observed endpoint; answer getServerInstance (redirect to ourselves), then preAuth / login / postAuth with minimal valid TDFs and a single hardcoded persona. Success signal: ELEM_WATCH: CHANGED fires and the spinner advances.
  • M2 — session online. Handle the step-6 session notifications until the top-level "connected to EA" state is reached and stays.
  • M3 — FUT / UTAS. Implement the UTAS REST layer (POST /ut/auth and the /ut/game/... endpoints already sketched in endpoint-map.md) so FUT itself loads.

M1 is only reachable if M0 says the client actually dials a listener. If M0 comes back "no traffic" (situation b), M1 in its "answer over a wire" shape is blocked until the dial trigger is solved, and the strategic options are re-opened rather than a next task being obvious.


Honest UNKNOWNs (capture before implementing)

  • Numeric component and command ids for FIFA 23's Blaze version. The names and ordering above are SHAPE-reliable; the wire ids are UNKNOWN.
  • TDF field tags and body layouts for each request/response. UNKNOWN — every response body must be shaped from a real capture (or careful trial against the client's parser).
  • The redirector/Blaze transport itself — host, port, and whether it is remote, loopback, or a pipe. This is exactly M0's question and is UNKNOWN until M0 runs.
  • Whether the client dials at all without the in-process bootstrap firing — the situation (a) vs (b) question. UNKNOWN until M0.

Until M0 answers the transport question, the ordering above is designable but untestable: there is nothing to answer to.