M1: gate is upstream + two-gate (state+token) finding; add callers mode

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>
This commit is contained in:
funman300
2026-06-29 18:34:35 -07:00
parent 49b3030e34
commit 55c9757e74
2 changed files with 203 additions and 8 deletions
+60
View File
@@ -63,3 +63,63 @@ This deletes the "reverse-engineer anadius's entitlement logic" problem.
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.