Files
OpenFUT-Bridge/docs/connection-gate-findings.md
T
funman300 550b23bb26 M1 COMPLETE: gate named = GetInternetConnectedState @ anadius64.dll+0x27790
Found without Ghidra, via anadius's LSX command-registration table (handler list
is offset-by-one from the names; verified by +0x27060 = GetProfile). The gate is
GetInternetConnectedState @ anadius64.dll+0x27790: its LSX "connected" attribute
= (byte[+0xCAB1B]||byte[+0xCAB1A]) ? str(+0xAF530) : str(+0xADE64); both flags
default 0 -> reports offline. That is why the client aborts at "connecting".

M1 answers: (1) ProtoSSLConnect not reached (gate upstream); (2) connection-state
function named; (3) single actionable gate (token already provided by anadius
FakeAuth / GoOnline passes). M2 (next session) = make GetInternetConnectedState
report connected, then watch for the real Blaze connect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 19:27:20 -07:00

10 KiB

Connection-gate findings (clean-room)

Status: observed behaviour only — derived from running the client and reading the repack's own files. No EA source used. Unconfirmed values are marked TODO/CONFIRM rather than guessed.

Components (observed)

Component Role
FIFA23.exe Game. Statically links EA's Ebisu SDK (online) and DirtySDK/ProtoSSL (transport). Loads at fixed base 0x140000000 — no ASLR seen across launches.
_ProtoSSLSendPacket @ FIFA23.exe+0xEFA530 DirtySDK TLS record assembler — plaintext in, encrypts in place. Already located + hooked.
anadius64.dll anadius EA-app/Origin LSX server emulator (ASLR'd). Provides offline DRM / entitlements / persona so the game launches; deliberately keeps it offline.
FakeEAACLauncher Anti-cheat bypass only. Irrelevant to the online gate.

Observed online-startup flow

  1. Ebisu SDK opens LSX socket(s) to anadius; a challenge/response handshake completes — captured XML: <Challenge> / <ChallengeResponse> / <ChallengeAccepted>, sender="EALS", ContentId 16115019.
  2. River/PIN telemetry <session type=boot> is emitted.
  3. No further socket traffic. Clicking Ultimate Team → "connecting to the EA Servers…" → zero network attempts (no Blaze DNS, no connect, nothing on send/recv/WSASend/WSARecv) → falls back to offline.

Conclusion: the decision is upstream and in-process

  • The online/offline verdict is delivered through anadius's in-process detoured Ebisu calls, not socket messages. (No GetAuthCode/GoOnline/ entitlement query appears on any socket, sync or async.)
  • The game therefore never reaches DirtySDK/ProtoSSL for Blaze — it aborts before any ProtoSSLConnect(gosredirector…). The gate is an Ebisu-SDK connection-state / online-session check upstream of the transport.
  • ONLINE_ACCESS is a Blaze::Nucleus::EntitlementType (enum value 1). anadius's GoOnline LSX handler (anadius64.dll+0x2BB90) is a success stub; anadius64.dll+0xB6B1 is a large entitlement/profile builder that does reference ONLINE_ACCESS. Reverse-engineering that entitlement-status logic is the wrong fight (see strategy).

Strategy (decided)

Do not make anadius report "online." anadius exists to keep the game offline, and it can't be removed without breaking launch/DRM. Instead:

Let the game run its real online flow and answer that flow ourselves via the hook + brain. Target the Ebisu connection-state check the FUT-entry path polls; make the game pass it so it issues the real ProtoSSLConnect to the Blaze redirector → our redirect + ProtoSSL hook capture the real frames.

This deletes the "reverse-engineer anadius's entitlement logic" problem.

Next RE step (converges with the ProtoSSL hook)

  1. Locate and read-only hook ProtoSSLConnect (same DirtySDK module and technique that found _ProtoSSLSendPacket). Confirms the game never initiates the Blaze connection (pins the gate strictly upstream) and gives us the exact symbol that will flip from "never called" to "called" on success.
  2. Locate the Ebisu connection-state getter the FUT-entry / "connecting" UI polls. Candidate string anchors (observed): ONLINE_STATUS_EVENT, GoOnline result handling, the "connecting to the EA Servers" UI trigger.
  3. Determine the minimal intervention to make that getter report connected (out-hook it in our version.dll, winning over anadius's detour) so the game proceeds to ProtoSSLConnect.
    • TODO/CONFIRM: whether out-hooking the getter is sufficient, or secondary checks (auth-token presence) also gate the attempt.

M1 results (read-only investigation)

Method: protossl-scan (xref / disasm / callers, app-module-restricted) against the live client, plus the existing connect/DNS/LSX hooks. Observed behaviour only — no EA source.

Point 1 — is ProtoSSLConnect reached on the "connecting" abort? NO — gate is upstream. CONFIRMED.

  • The existing connect / GetAddrInfoW / getaddrinfo hooks show the client makes zero network attempts during the "connecting to the EA Servers" abort: no DNS for any gosredirector.* host, no connect to any external address.
  • The DirtySDK/ProtoSSL transport is therefore never reached — the abort is entirely upstream and in-process.
  • ProtoSSLConnect has no debug string and sits behind the DirtySDK API, so an explicit hook on it isn't needed to prove this; the behavioural evidence is conclusive. TODO/CONFIRM: locate ProtoSSLConnect by signature later, as a positive M2 trip-wire (it should fire once we flip the gate).

Surface mapped (clean-room)

  • Redirector host table (FIFA23.exe .rdata, pointer table @ +0x83FC858): spring18.gosredirector.{sdev,stest,scert}.ea.com + production spring18.gosredirector.ea.com.
  • Redirector request/response config (same region): X-BLAZE-ERRORCODE, Authorization:, <errorCode> — the redirector request carries an Authorization header (a Nucleus token).
  • Enum-name tables (serialization only, NOT decision code): ONLINE_ACCESS (Blaze::Nucleus::EntitlementType = 1), ONLINE_STATUS_EVENT, … These are reflection tables keyed by enum value; string-xref of them is a dead end for the decision (the decision compares values, not strings).

Point 2 — name the connection-state function: PARTIAL.

  • The exact connection-state decision is behind heavy C++ vtable indirection (the redirector config's two code pointers resolved to a virtual-dispatch thunk @ FIFA23.exe+0x27BD4C0 and a ret 0 stub @ +0x4F3CBC0). The memory-scan toolkit (string / pattern / xref / callers) cannot efficiently navigate this.
  • Pinning the exact function needs a static disassembler with decompilation (Ghidra / IDA) on FIFA23.exe. protossl-scan remains the bridge to the live process (mapping static addresses to the ASLR'd runtime, confirming hits, installing hooks).
  • TODO/CONFIRM: name the connection-state getter by module+offset via Ghidra.

Point 3 — gate count: TWO gates (state + token). Evidence-backed.

  • The online-connect path reads/requires an auth (Nucleus) token: the redirector request carries an Authorization: header, and the SDK surface has GetAuthCode / FakeAuth / <GameToken>. So it is not a single connection-state boolean flip — even with the state forced "online", the client needs a valid token to build the redirector request.
  • Conclusion for M2: plan for two gates — (a) the connection-state decision, and (b) supplying an auth token the client accepts.
  • TODO/CONFIRM the exact abort point (no-token vs state-says-offline vs both) — needs Ghidra-level control-flow tracing.

Dynamic probe result (read-only) — GoOnline is NOT the gate

A read-only detour on anadius's GoOnline handler (anadius64.dll+0x2BB90) shows the game does call GoOnline during the "connecting" attempt (incl. on the Ultimate Team click) and anadius returns success — yet no Blaze connection follows (still zero external CONNECT/DNS).

Therefore the gate is downstream of GoOnline: the game decides to go online and the request is accepted, then it aborts at the auth-token step (GetAuthCode) and/or while waiting for the "online established" status callback (ONLINE_STATUS_EVENT), and times out into offline.

This sharpens the two-gate picture: GoOnline passes; the real blocker is the token / online-status step. TODO/CONFIRM which (token-missing vs status-never-fires) — via a GetAuthCode probe and/or Ghidra.

Recommendation / next

Name the downstream gate. Two complementary routes:

  • Dynamic: probe anadius's GetAuthCode handler (does it return a token or fail?) — distinguishes token-gate from status-callback.
  • Static (Ghidra): xref the GoOnline / GetAuthCode / ONLINE_STATUS_EVENT strings in FIFA23.exe to find the FUT online-flow code that issues GoOnline then waits for the token/status, and decompile it. FIFA isn't ASLR'd, so Ghidra addresses (image base 0x140000000) map 1:1 to our recorded offsets.

M1 COMPLETE — the gate is GetInternetConnectedState

Found without Ghidra, by reading anadius's LSX command-registration function (anadius64.dll+0x14C0), which lists every command-name → handler inline. NB the lea rax,[handler] is offset by one from the lea rdx,[name] in that listing (verified empirically: +0x27060 decompiles to GetProfile — it builds a GetProfileResponse for persona "fun"). Corrected handler map:

anadius LSX command handler (anadius64.dll + …)
GetProfile 0x27060
GetInternetConnectedState 0x27790
GoOnline 0x2BB90
GetAuthCode 0x2BBC0

The gate, named: GetInternetConnectedState @ anadius64.dll+0x27790

It serializes an LSX InternetConnectedState response with a connected attribute whose value is:

connected = (byte[+0xCAB1B] != 0 || byte[+0xCAB1A] != 0) ? str(+0xAF530)
                                                          : str(+0xADE64)

Both flag bytes default to 0, so it reports the offline value (+0xADE64). That is precisely why the client sits at "connecting to the EA Servers" and falls back offline.

Answers to the three M1 points

  1. ProtoSSLConnect reached on the abort? NO — gate upstream. Confirmed.
  2. The connection-state function: GetInternetConnectedState @ anadius64.dll+0x27790. Decision = the two-flag branch above.
  3. Gate count: ONE actionable gate. The auth token is already satisfied — GoOnline (+0x2BB90) is called during the attempt and returns success, and anadius provides a fake auth code; the blocker is purely the connection-state. So M2 = make GetInternetConnectedState report connected (then the game proceeds with its existing token).

M2 directions (do NOT do this session)

  • Check whether the flags at +0xCAB1A / +0xCAB1B are settable via anadius config / a hidden option (cheapest flip).
  • Else out-detour GetInternetConnectedState in our version.dll to force the +0xAF530 ("connected") path.
  • Then watch for the client to attempt the real Blaze connect (our connect redirect + ProtoSSL hook capture the first plaintext — milestone M3).
  • TODO/CONFIRM: exact text of the offline/connected value strings (+0xADE64 / +0xAF530); whether any secondary check gates the attempt after the state flips.