Add the reverse-engineering work for FIFA 23 online integration: - tools/protossl-scan: read-only memory scanner that confirms the DirtySDK/ProtoSSL transport, cross-references strings to code (xref/xref-str), and disassembles the enclosing function (disasm). Located _ProtoSSLSendPacket at FIFA23.exe+0xEFA530 (plaintext TLS record assembler) via the "_ProtoSSLSendPacket" debug string xref. - openfut-hook: version.dll proxy (forwards all 17 real exports) that injects into FIFA 23 and installs an inline detour on _ProtoSSLSendPacket, plus connect/DNS (getaddrinfo) capture and local LSX/redirect listeners. Logs to C:\openfut\hook.log. - CLAUDE.md: bridge project context + the gate-sequence task roadmap. Findings so far: with the anadius offline emulator the game never attempts the online Blaze connection (local auth gate), so capture of live Blaze frames is blocked pending an online-session fake. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.6 KiB
OpenFUT Bridge — Claude Code project context
Read this first. It's the standing context for every task in this project. Each working session will give you ONE bounded task plus a verification clause; this file is the background that stays true across all of them.
What this project is
OpenFUT is an offline FIFA 23 FUT (Ultimate Team) emulator for single-player game preservation. EA permanently shut down FIFA 23's online servers in October 2025, which removed FUT. This project lets a legitimately-owned copy run FUT against a local emulated backend instead of EA's (dead) servers.
It has two repos:
- OpenFUT Core — the game-independent FUT economy backend (already largely built).
- OpenFUT Bridge — the FIFA 23 integration / reverse-engineering layer (this work).
Hard constraints (do not violate)
- Clean-room only. Everything is derived from observing the running client's own behaviour. NEVER use, reference, or reproduce leaked EA source code (e.g. the 2021 FIFA 21 / Blaze leak). If a task seems to need it, stop and say so. Clean-room provenance is the legal foundation of the whole project.
- Mark uncertainty, don't invent. Several things are genuinely unconfirmed:
the Fire2 packet framing, ProtoSSL non-blocking return values, and FIFA 23's
Blaze component/command IDs. When you don't know a value, leave a clearly
labelled
TODO/CONFIRMrather than guessing a confident-looking number. - Verify against the client. The dead servers mean responses are synthesized and tested against the live offline client, never captured from EA. The client is the oracle: a gate is "done" when the client advances to the next command.
Architecture
FIFA 23 client (offline, native Windows)
| ProtoSSLConnect / ProtoSSLSend / ProtoSSLRecv (in-process)
openfut_hook.dll — fakes the connection, captures requests, injects responses
| plain localhost TCP, length-prefixed frames (no TLS)
blaze_brain (Rust) — decodes requests, crafts Blaze responses <-- iteration surface
|
OpenFUT Core — supplies real FUT economy data for the FUT gates
Why in-process (not a localhost TLS server): FIFA 23's ProtoSSL uses modern TLS with cert pinning, so a fake server gets its cert rejected. Hooking ABOVE the crypto (ProtoSSLSend/Recv take/return plaintext) sidesteps TLS entirely.
The entry sequence (the gates to reach FUT)
The client runs these in order; each must be satisfied before the next fires:
- LSX (port 3216, plaintext loopback) — launcher→game bootstrap.
- Blaze redirector — returns a server host/port.
- Blaze preauth (UTIL) — config / component list.
- Blaze login (AUTHENTICATION) — persona, session key, UID.
- Blaze postauth (UTIL) — telemetry/ticker; "fully online".
- FUT entry check — FIFA-specific eligibility/entitlement wall.
- FUT hub load — club/squad over REST; hand off to OpenFUT Core.
Gates 2–5 are largely static/replayable (the client validates little). Gates 6–7 need internally-consistent data and connect to OpenFUT Core.
Environment
- Native Windows boot (chosen for predictable PE/hooking behaviour over Proton).
- FIFA 23, with EA Anticheat in its offline/neutralized state. Do NOT assume a task is safe to hook unless EAAC is confirmed neutralized.
- Rust is the primary language. Hook =
cdylib, Windows target. Brain = portable. - Self-hosted Gitea (git.aleshym.co, org Quaternions). Self-hosted CI runner.
How to work here
- One rung at a time. Don't run ahead into a gate that depends on an unconfirmed earlier answer.
- When decoding a frame, work from REAL captured bytes the user pastes in — not from a guessed layout. Ask for the bytes if they're not provided.
- End each change with how the user verifies it on the client.
OpenFUT Bridge — Task prompts (run in order)
Each task is its own Claude Code session. Paste the task, attach the named starting file(s), and don't move to the next until the verification clause passes. The project CLAUDE.md is assumed to be in context.
TASK 1 — Confirm the transport (read-only memory scan)
Goal: prove whether FIFA 23 uses EA DirtySDK / ProtoSSL before we build any hook around it. Read-only; no hooking, patching, or injection.
Do this:
Write a standalone Windows console program in Rust (target
x86_64-pc-windows-msvc or -gnu) that:
- Finds the running FIFA 23 process by name (and accepts a PID argument).
- Enumerates its modules and committed memory regions (e.g.
EnumProcessModules/VirtualQueryEx), readable regions only. - Reads memory with
ReadProcessMemoryin chunks (with small overlap so a marker spanning a chunk boundary is still found). - Searches for these ASCII markers and prints each hit with its absolute address:
protossl:,ProtoSSLSend,ProtoSSLRecv,ProtoSSLConnect,gosredirector,DirtySDK,blaze. - Prints a summary: which markers were found, and a clear verdict line ("ProtoSSL present" vs "no ProtoSSL markers found").
Keep it dependency-light (the windows crate is fine). Comment it for a Rust
beginner. It must only read.
Verification: with FIFA 23 running and sitting at the main menu, the program
prints whether the ProtoSSL markers are present. If protossl: and the
send/recv strings appear, the transport is confirmed and we proceed. If nothing
appears after the menu has loaded, STOP — we rethink the transport, don't build
the hook.
Note: reach the main menu before scanning; the networking code/strings may not be paged in at the title screen.
TASK 2 — Loadable DLL shell (prove we can get code in)
Goal: a version.dll proxy that loads into FIFA 23 and runs our code, before
any hook logic exists. This isolates "can we inject at all" from "is the hook
correct".
Do this:
Create a Rust cdylib (Windows target) that:
- Exports the functions a real
version.dllexports, forwarding each to the systemversion.dll(proxy/sideload pattern). List the needed exports and implement the forwarding. - In
DllMain, onDLL_PROCESS_ATTACH, spawns a thread (NOT work in DllMain itself — loader lock) that writes a line to a log file in a known path, e.g.C:\openfut\hook.log, with a timestamp. - Provide the
Cargo.toml(crate-type = ["cdylib"]) and the exact build command.
Clean-room: this is generic proxy/loader scaffolding, nothing EA-derived.
Verification: drop the built DLL next to the FIFA 23 executable, launch the
game, and confirm hook.log gets the timestamped line. Game still reaches the
menu normally. If it crashes or the line never appears, fix the proxy/forwarding
before continuing.
Caution: confirm EAAC is in its offline/neutralized state before loading the DLL. Do not load it into an anticheat-active session.
TASK 3 — One landing hook on ProtoSSLSend
Goal: prove we can hook a ProtoSSL function and see real outbound frames. Just one function, and it calls the original (passive observation).
Do this: Extend the Task 2 DLL:
- Add the
retourcrate. Resolve theProtoSSLSendaddress — first pass: use the absolute address Task 1 reported, converted to a module-relative offset plus the live module base. (We'll switch to a byte-pattern scan later for patch-resilience; leave a TODO for that.) - Install a
retourinline detour onProtoSSLSendwith signatureextern "C" fn(*mut c_void, *const u8, i32) -> i32(x64 = one calling convention;extern "C"is correct). - In the detour: log
lengthand the first ~32 bytes of the buffer as hex, then CALL THE ORIGINAL and return its result (do not block or alter traffic yet).
Mark the ProtoSSL return-value conventions and the eventual pattern-scan as
TODO/CONFIRM per the project rules.
Verification: launch the game, attempt to go online / load FUT, and confirm
hook.log shows ProtoSSLSend calls with non-trivial byte dumps — these are the
client's real outbound Blaze frames (still TLS-bound on the wire, but plaintext
here, which is the whole point). Seeing real frames = hooking works, and we now
have actual bytes to decode in later tasks. Paste a few of those captured frames
into the next session.
What to attach to each task
- Task 1: nothing (fresh program), or the Linux
protossl_scan.rsas a logic reference to port. - Task 2: nothing, or your existing
version.dllproxy shell if you have one. - Task 3: the Task 2 DLL, plus the
openfut_hook_sketch.rsas the structural reference, plus the address Task 1 reported.
Once Task 3 yields real captured frames, later tasks (the brain handlers per gate) should always include the actual pasted bytes — decoding real frames is far more reliable than guessing the Fire2 layout.