Implement the LSX (EA App bootstrap, TCP :3216→:3217) server in the bridge
and clear the loading-screen crash gate. FIFA 23 now completes the full LSX
handshake + session against OpenFUT and advances to the online/Blaze phase
("<persona> is connecting to the EA Servers…") — verified live, not a cached
session (the displayed persona "fun" comes from our GetProfile).
Root cause of the invariant loading-screen crash: get_config() omitted the
PROGRESSIVE_INSTALLATION and PROGRESSIVE_INSTALLATION_EVENT services (Name="PI").
FIFA builds its service registry from GetConfig and resolves that handle during
online-init; the missing service left a null handle it dereferenced at
FIFA23.exe+0x133dfc5 (mov rax,[r13+0x18], r13=NULL) ~2s after LSX — identical
across every prior run. Found via FIFA's own CrashReport_*.json (stable RIP) +
diffing anadius64.dll's service-registration table against ours.
Also in this change:
- process_frame(): handle FIFA's pipelined (batched) LSX messages per read,
eliminating the ~15s handshake hang.
- Real anadius values (RE'd from anadius64.dll + anadius.cfg): PersonaId/UserId/
persona, locale list, GetGameInfo fixed `GameInfo="<value>"` attribute,
GetAllGameInfo 14-attr schema, GetSetting `Setting=` attribute.
- IsProgressiveInstallationAvailable served from sender="PI".
- docs/connection-gate-findings.md: full RE trail; relocate stale openfut-hook
copy under docs/ (canonical hook lives in openfut-launcher).
Next gate: Blaze/online (M2), ridden over ProtoSSL in-process.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.2 KiB
Handoff — LSX handshake complete end-to-end
Date: 2026-07-02
Scope of the session: openfut-bridge (the delivery test) that expanded into
openfut-launcher/openfut-hook (the fix). Nothing was committed — everything below is
uncommitted working-tree state.
Read docs/connection-gate-findings.md (bottom two sections) for the full technical
narrative. This file is the short "where we are / what to do next" version.
TL;DR
FIFA 23 now completes the LSX handshake and holds a sustained encrypted session against our own bridge, live — the first EA-side step our code services end-to-end. Three changes made it work:
- Hook redirect — FIFA's
connect(127.0.0.1:3216)is rewritten to:3217by our hook. The existing in-process offline shim keys on port 3216 specifically, so a different port slips past it and lands on our host bridge (loopback is shared with the Wine/Proton prefix — same netns). - AES key =
000102030405060708090a0b0c0d0e0f. The[0,1,2,…,15]sequence that looked like a placeholder is the actual session key; confirmed by round-tripping against a captured live session. - Session-seed fix —
compute_seed()must take the raw ASCII bytes of the first two chars of our response hex, not parse them as a hex pair.
Last live run: FIFA held one ESTAB connection to the bridge and drove 20 EbisuSDK
requests with no crash.
Current deployment state (IMPORTANT)
- The redirect hook is currently deployed as
/mnt/games/FIFA 23/version.dll(md5dab3952809c24122af228a3201b9357b). While this is in place, FIFA routes LSX to our bridge and requires the bridge running on 3217 or LSX will not complete. - Backup of the previous hook:
/mnt/games/FIFA 23/version.dll.pre-lsx-redirect.bak(md5fb7a5aae…). - To restore the previous behavior for normal offline play:
cp "/mnt/games/FIFA 23/version.dll.pre-lsx-redirect.bak" "/mnt/games/FIFA 23/version.dll" - The bridge process from this session may or may not still be running. Check:
pgrep -a openfut-bridge/ss -tlnp | grep 3217.
Uncommitted changes (nothing is committed)
openfut-bridge
src/lsx.rs:- Seed fix in
compute_seed()(the load-bearing change). - AES-128 hand-roll replaced with the
aescrate (+ a FIPS-197 unit testaes128_matches_fips197_and_round_trips). - A debug log line dumping raw session ciphertext (used during verification against the capture; harmless to keep, or remove).
- Seed fix in
Cargo.toml/Cargo.lock: addedaes = "0.8".docs/connection-gate-findings.md: corrected classification + new "LSX complete" section.docs/HANDOFF-lsx-2026-07-02.md: this file.captures/lsx-handshake-capture-2026-07-02.log: a captured live handshake plus the raw session ciphertext, kept as a reference sample for verifying the implementation.
openfut-launcher/openfut-hook
src/connect_hook.rs: added aPORT_LSX_NBO (3216) → PORT_LSX_TARGET_NBO (3217)arm inredirect_if_ea, plus result logging on the redirect path.
There is other pre-existing uncommitted work in these trees from before this session (e.g. bridge TLS/proxy changes, core changes). Do not sweep it into a commit without checking with the user. Confirm scope before committing anything.
How to reproduce the working LSX session
- Build the hook (redirect):
cd openfut-launcher/openfut-hook cargo build --release --target x86_64-pc-windows-gnu - Deploy it (back up first — already backed up once):
SRC=openfut-launcher/openfut-hook/target/x86_64-pc-windows-gnu/release/openfut_hook.dll cp -f "$SRC" "/mnt/games/FIFA 23/version.dll" - Build + run the bridge on 3217:
cd openfut-bridge && cargo build --release LSX_ADDR=127.0.0.1:3217 RUST_LOG=openfut_bridge=debug \ ./target/release/openfut-bridge > /tmp/bridge-lsx.log 2>&1 & - Launch FIFA (user does this — umu/Steam via
/home/alex/Desktop/launch-fifa23-live-editor.sh). - Verify:
ss -tnp | grep 3217should showESTAB FIFA23.exe ↔ openfut-bridge, and/tmp/bridge-lsx.logshould show multipleLSX: dispatch …lines (not one garbage message).
Key facts / constants
| Thing | Value |
|---|---|
| EA App LSX port (FIFA connects here) | 127.0.0.1:3216 |
| Redirect target (our bridge LSX) | 127.0.0.1:3217 (LSX_ADDR) |
| LSX AES-128-ECB key | 000102030405060708090a0b0c0d0e0f |
| Our greeting key (challenge plaintext) | cacf897a20b6d612ad0c05e011df52bb |
| Session seed rule | first two ASCII chars of our ChallengeAccepted response hex, as raw bytes (e.g. "0f…" → 0x3066), NOT the parsed hex pair |
| Existing shim interception | in-process, keyed on port 3216; loopback is shared (FIFA netns == host netns) |
Small verification helpers (keyscan.py, seedscan.py) live in the session
scratchpad — trivial to recreate from connection-gate-findings.md if needed.
Next steps (in priority order)
- Add a
GetGameInfo GameInfoId=…handler to the bridge dispatcher (src/lsx.rsdispatch()). It currently falls back to the generic<Ok/>("unknown request type"). FIFA tolerated that, but may need real responses to progress. This is the immediate iteration surface now that we can see FIFA's real requests. - Watch how far FIFA advances with the LSX session satisfied — the next step is
the Blaze/online layer (the old M2
ONLINE_STATUS_EVENTblocker). With LSX now handled by our bridge, that picture may change; re-evaluate M2 with FIFA actually reaching it. - Port the seed fix into the hook's own
src/lsx.rsif the in-process LSX path is ever revived — it has the samecompute_seedbug. - Decide on the redirect's permanence — right now it's a manual DLL swap. If LSX-via-bridge becomes the intended path, the launcher's setup/deploy flow should own it.
Open question for the user
We never captured what FIFA showed on screen during the successful run (advanced to
a menu? sat at "connecting"? waited on GetGameInfo?). Ask, or observe on the next
launch — it tells you whether LSX is fully satisfied or whether GetGameInfo is the
next thing to address.