55c9757e74
Read-only M1 investigation results appended to connection-gate-findings.md: - Confirmed ProtoSSLConnect is never reached on the "connecting" abort (gate is upstream/in-process) via the existing connect/DNS hooks. - Mapped the surface: redirector host table (gosredirector.* per env), the redirector request config (X-BLAZE-ERRORCODE, Authorization:, <errorCode>), and the enum-name serialization tables (ONLINE_ACCESS, ONLINE_STATUS_EVENT). - Key answer: the online-connect path requires a Nucleus auth token (the redirector request carries an Authorization header) => TWO gates (state + token), not a single boolean flip. - The exact connection-state function is behind C++ vtable indirection; pinning it needs Ghidra. protossl-scan stays the live-process bridge. protossl-scan: add `callers` mode (find call/jmp sites to a function) and restrict xref/callers scans to the app modules (FIFA23.exe/anadius64.dll) for speed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
126 lines
7.0 KiB
Markdown
126 lines
7.0 KiB
Markdown
# 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.
|
|
|
|
### Recommendation
|
|
Load `FIFA23.exe` into **Ghidra** to (a) name the connection-state getter
|
|
precisely and (b) confirm the gate mechanism, then return to `protossl-scan` +
|
|
the hook to map those addresses onto the live process and install the M2 hook.
|