feat: add ProtoSSL RE tooling and FIFA 23 in-process hook
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>
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# 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/CONFIRM` rather 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:
|
||||
|
||||
1. **LSX** (port 3216, plaintext loopback) — launcher→game bootstrap.
|
||||
2. **Blaze redirector** — returns a server host/port.
|
||||
3. **Blaze preauth** (UTIL) — config / component list.
|
||||
4. **Blaze login** (AUTHENTICATION) — persona, session key, UID.
|
||||
5. **Blaze postauth** (UTIL) — telemetry/ticker; "fully online".
|
||||
6. **FUT entry check** — FIFA-specific eligibility/entitlement wall.
|
||||
7. **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:
|
||||
1. Finds the running FIFA 23 process by name (and accepts a PID argument).
|
||||
2. Enumerates its modules and committed memory regions (e.g. `EnumProcessModules`
|
||||
/ `VirtualQueryEx`), readable regions only.
|
||||
3. Reads memory with `ReadProcessMemory` in chunks (with small overlap so a
|
||||
marker spanning a chunk boundary is still found).
|
||||
4. Searches for these ASCII markers and prints each hit with its absolute address:
|
||||
`protossl:`, `ProtoSSLSend`, `ProtoSSLRecv`, `ProtoSSLConnect`,
|
||||
`gosredirector`, `DirtySDK`, `blaze`.
|
||||
5. 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:
|
||||
1. Exports the functions a real `version.dll` exports, forwarding each to the
|
||||
system `version.dll` (proxy/sideload pattern). List the needed exports and
|
||||
implement the forwarding.
|
||||
2. In `DllMain`, on `DLL_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.
|
||||
3. 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:
|
||||
1. Add the `retour` crate. Resolve the `ProtoSSLSend` address — 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.)
|
||||
2. Install a `retour` inline detour on `ProtoSSLSend` with signature
|
||||
`extern "C" fn(*mut c_void, *const u8, i32) -> i32` (x64 = one calling
|
||||
convention; `extern "C"` is correct).
|
||||
3. In the detour: log `length` and 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.rs` as a logic
|
||||
reference to port.
|
||||
- Task 2: nothing, or your existing `version.dll` proxy shell if you have one.
|
||||
- Task 3: the Task 2 DLL, plus the `openfut_hook_sketch.rs` as 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.
|
||||
Reference in New Issue
Block a user