0138a39f3e
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>
140 lines
6.2 KiB
Markdown
140 lines
6.2 KiB
Markdown
# 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:
|
|
|
|
1. **Hook redirect** — FIFA's `connect(127.0.0.1:3216)` is rewritten to `:3217` by 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).
|
|
2. **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.
|
|
3. **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`
|
|
(md5 `dab3952809c24122af228a3201b9357b`). 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` (md5 `fb7a5aae…`).
|
|
- **To restore the previous behavior for normal offline play:**
|
|
```bash
|
|
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 `aes` crate (+ a FIPS-197 unit test
|
|
`aes128_matches_fips197_and_round_trips`).
|
|
- A debug log line dumping raw session ciphertext (used during verification against
|
|
the capture; harmless to keep, or remove).
|
|
- `Cargo.toml` / `Cargo.lock`: added `aes = "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 a `PORT_LSX_NBO (3216) → PORT_LSX_TARGET_NBO (3217)` arm
|
|
in `redirect_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
|
|
|
|
1. **Build the hook** (redirect):
|
|
```bash
|
|
cd openfut-launcher/openfut-hook
|
|
cargo build --release --target x86_64-pc-windows-gnu
|
|
```
|
|
2. **Deploy it** (back up first — already backed up once):
|
|
```bash
|
|
SRC=openfut-launcher/openfut-hook/target/x86_64-pc-windows-gnu/release/openfut_hook.dll
|
|
cp -f "$SRC" "/mnt/games/FIFA 23/version.dll"
|
|
```
|
|
3. **Build + run the bridge on 3217:**
|
|
```bash
|
|
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 &
|
|
```
|
|
4. **Launch FIFA** (user does this — umu/Steam via
|
|
`/home/alex/Desktop/launch-fifa23-live-editor.sh`).
|
|
5. **Verify:** `ss -tnp | grep 3217` should show `ESTAB FIFA23.exe ↔ openfut-bridge`,
|
|
and `/tmp/bridge-lsx.log` should show multiple `LSX: 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)
|
|
|
|
1. **Add a `GetGameInfo GameInfoId=…` handler** to the bridge dispatcher (`src/lsx.rs`
|
|
`dispatch()`). 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.
|
|
2. **Watch how far FIFA advances** with the LSX session satisfied — the next step is
|
|
the Blaze/online layer (the old M2 `ONLINE_STATUS_EVENT` blocker). With LSX now
|
|
handled by our bridge, that picture may change; re-evaluate M2 with FIFA actually
|
|
reaching it.
|
|
3. **Port the seed fix into the hook's own `src/lsx.rs`** if the in-process LSX path is
|
|
ever revived — it has the same `compute_seed` bug.
|
|
4. **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.
|