Cherry-pick of 4b1d5aa from wip/kit-selector-re, which forked before the TLS
work and cannot be rebased: that branch predates fifa17_tls.rs/patch_mem.rs and
carries a large unrelated lineage (probe/lsx/recv_hook). Only the kit_trace
commit's own contents are taken.
Traces the client-side FUT pre-match kit path in CardsDLL: the GetMatchKits_DP
gate (KITS_AVAILABLE), setAvailableKits (home/away list count), the kit-item
clone driver (item type/subid/teamid at FUN_1801c3480), and the local teamkits
DB clone. Read-only passive detours reusing season_trace's installers, which
this widens to pub(crate).
The one open unknown it answers: the selector requires item+0x60 == 4, a pile
value the server has never been observed to produce (/club emits 1,
/purchased 6).
Verified in the cross-built artifact that the roster TLS gate patch is intact
alongside the new trace (fifa17_tls markers present, KIT_* markers present).
Milestone B: move FIFA17 ProtoSSL certificate compatibility into version.dll so
the client-local contract is openfut.cfg + LSX + version.dll with no external
/proc-writing patcher. The proven external openfut-autopatch remains the oracle
and is NOT removed; this reaches behavioral parity for the fail-closed patches.
Patch set (ASLR-relocated at runtime; fail-closed byte-verified; one-shot):
- FIFA17.exe ProtoSSL cert gates (REQUIRED_FOR_TLS), preferred base 0x140000000:
GATE1 rva 0x6132548 0f85 76010000 (JNZ) -> 90*6 (NOP)
GATE2 rva 0x61361b0 48 89 5c (prologue) -> 31 c0 c3 (xor eax,eax; ret)
Applied as a pair only when BOTH read their known original, exactly like the
external patcher's cert_pass; polled until the STEAMPUNKS packer unpacks them.
- CardsDLL empty-My-Packs store crash-guard (REQUIRED_FOR_STORE_TLS, bug 6c),
preferred base 0x180000000: rva 0x14858 75 0f (JNZ) -> 7f 0f (JG). Applied once
CardsDLL maps (module-late).
Deliberately NOT ported: the external patcher's 8 unconditional STORE_PATCHES.
They carry no recovered original bytes (cannot be fail-closed) and are re-applied
every tick (would require the constant-rewrite loop this milestone forbids); the
external source records no rationale for them. Documented in the Vault ADR.
Architecture:
- patch_mem.rs: generic fail-closed primitive over a Mem trait — classify
(ORIGINAL/ALREADY_PATCHED/MISMATCH), apply_checked (read->classify->write only on
ORIGINAL->reread verify), VirtualQuery-guarded read + VirtualProtect/Flush write
(WinMem). Trait abstraction makes every outcome host-testable without FIFA.
- fifa17_tls.rs: FIFA17-specific patch table + bounded poll worker (250ms, 15min
cap, no busy-spin) started from fifa17::install() after the network redirect.
Never patches an absolute address; never blind-writes on mismatch; a write/verify
failure is reported, never pretended.
Phase 11: removed the season_trace CACHE_PACKNAMES_FAILED->SUCCESS force-success
bypass (a staging-only behavior-changer that was armed unconditionally in the
candidate); season_trace is now genuinely read-only passive tracing. sbc_dispatch
and store_entry remain the intended REPAIR_PROMOTED fixes.
Tests: 39 hook tests (26 baseline + 13 new: classify states, apply/idempotence,
no-blind-write on mismatch, unreadable-module wait, write-failure reporting, RVA/
live-addr relocation across bases, cert-gate pairing, patch-table integrity).
clippy --features fifa17 -D warnings clean; fmt clean; x86_64-pc-windows-gnu
cross-build. No network-config authority added (routing stays Milestone A).
Runtime validation (x64dbg site check + Windows/Linux retail) still outstanding.
FIFA 23 is not in development and was never a valid template for FIFA 17
(different game, different in-memory layout). Remove it as a build target and
as scaffolding, while preserving the per-game feature architecture so future
games plug in as new modules — never by copying retired reverse-engineering.
Hook (openfut-hook):
- Delete install_hooks_fifa23 and every FIFA23-only module: config, hooks,
transport_watch, ssl_patch, origin_spy, tls_bypass, dial_notification, probe
(+ probe feature), recv_hook (+ capture_baseline feature), plus the orphan
FIFA23 LSX/Origin files lsx.rs and ea_stub.rs. ~3.6k lines; git + Vault retain
the research.
- lib.rs is now game-generic: a per-game feature selects that game's module and
install_hooks dispatches to it. No game feature => compile_error!("select a
game, e.g. --features fifa17"). --features fifa17 remains the build invariant.
- Drop the crate-wide blanket (it existed only
to hide the compiled-but-unused FIFA23 modules). Replace with narrow, justified
#[allow(dead_code)] on the three FIFA17 SBC RE-scaffolding items it was masking,
so the candidate stays behavior-identical.
- connect_hook: the redirect is now always the config-driven path (openfut-common
target from openfut.cfg); the hardcoded-loopback rewrite and its dead consts are
gone. Removed the FIFA23-era transport_watch diagnostics from the shared
connect/WSAConnect/ConnectEx detours. Deleted unused iat::patch_iat_in.
Launcher:
- fifa_game_dir no longer defaults to a hardcoded '.../FIFA 23' Steam path; it is
empty by default, matching the launcher's own rule that it never invents a path
to somebody's game install (like openfut_server_host and game_profile).
- Generalise the remaining 'FIFA 23' doc literals in config.rs / setup.rs.
Proof: fifa17 clippy -D warnings clean; no-game build fails with the documented
compile_error; launcher 75 tests pass unchanged; launcher + hook cross-build
x86_64-pc-windows-gnu; cargo fmt --check clean; zero FIFA23 symbols/literals
remain. FIFA17 armed-module set unchanged (redirect + SBC/store/season).
Adds openfut-hook/src/season_trace.rs: read-only CardsDLL detours that log the
FIFA17 FUT offline-season entry native call sequence (no behavior change; each
wrapper logs then calls the original via a trampoline). Traces the FUT_Season
natives proven by the registration table FUN_18004e3f0:
GetUsersOfflineDivision 0x4eb50 (NOT LoadOfflineSeasons),
LoadOfflineSeasons 0x4ee10 + async impl 0x57560,
LoadCurrentOfflineSeason 0x4eb70 + impl 0x57230 + completion 0x578e0,
StartSeason 0x4f340, GetOfflineSeasonInfo 0x4e850.
Includes a near-trampoline installer (install_detour_reloc) that relocates a
single rip-relative disp32 so functions with rip-relative prologues can be
detoured (trampoline allocated within +/-1.5GiB of CardsDLL).
The FUT store flashes a Browse-Packs overview on first open: the store
screen ctor leaves screen+0x290 (CATEGORY_ID) at 0, and the resolver
FUN_1800147f0 treats 0 as list-all, so the first render draws the group
overview before the movie posts a tab ordinal.
Detour the store render FUN_18007dab0 (RVA 0x7dab0): when the incoming
category is 0, substitute the first present group ordinal (1) so the
first frame lands on a real tab. Provably crash-safe: it writes 1 only
after FUN_180014420(_, 1) (the resolver's own ordinal->group lookup,
whose first arg is dead) returns non-NULL, which is exactly the
resolver's non-crash precondition; the positive-invalid NULL deref at
0x14882 is thus unreachable. No group yet -> category left 0 -> Browse,
still safe.
Promoted like the SBC dispatch: build-armed (CLAMP_PROMOTED), no env.
Signature-gated on both the detoured render and the called lookup,
image-validated, installed under thread suspension, fail-closed. Only
the overview flash is addressed; the empty-My-Packs entry dialog is
movie-side (packed .apt) and out of CardsDLL reach (see Vault
Store Resolver Guard 2026-08-19). fmt/clippy -D warnings clean both
feature sets, 26 hook tests pass, x86_64-pc-windows-gnu release builds.
Modernize manual nul-terminated byte strings to C-string literals (c"...")
at all Win32 GetModuleHandleA/GetProcAddress/getaddrinfo call sites (byte-identical),
drop two redundant SOL_SOCKET-as-i32 casts, remove a needless return in the fifa17
install path, and add a # Safety section to DllMain. Scope the FIFA-23-path
dead-code/unused-import lints (unused only under the fifa17 feature, stripped by the
linker) with a documented crate-level cfg_attr allow. Cross-verified: both the
default and fifa17 builds now pass clippy -D warnings and compile; probe and
capture_baseline still build.
In-process, read-only probes and transport observation built while closing
the online/FUT route from both the memory and network sides.
- probe.rs / dial_notification.rs: menu-time ctx dump, connMgr enumerator,
synthetic dial-notification + direct-call dial trigger, and the
[element+0x40] container write-watchpoint. All env-gated, one-shot,
VirtualQuery-guarded; none alter game state by default.
- transport_watch.rs + connect/connectex/hooks/lib: M0 transport observation
(grep-friendly TRANSPORT_WATCH logging on the existing getaddrinfo/connect/
WSAConnect/ConnectEx detours) and an IPv6 (v4-mapped) EA-redirect so the
game's IPv6 :443 dials land on the bridge instead of the dead servers.
Findings: the game never initiates a Blaze connection offline; the dial
handler is registered by a self-registering, message-driven state machine
whose container stays empty with no Blaze exchange. See openfut-bridge
docs/closure-and-preservation.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hook-side tooling for the LSX/Blaze reverse-engineering effort:
- probe.rs (new, `probe` feature): passive logging detours on FIFA's online-flow
functions via the unhook/rehook pattern (no trampoline/relocation, works on
RIP-relative prologues). Deferred install waits for anadius64.dll to load, then
logs enter/return for GoOnline + GetInternetConnectedState (anadius) and the
OnlineStatusEvent/Login deserializers (FIFA23.exe). Revealed that our pushed LSX
events reach FIFA and parse OK, while GoOnline never fires — localizing the online
gate to FIFA's game-side event consumer.
- connect_hook.rs: redirect FIFA's LSX connect :3216 → :3217 so it lands on the
native openfut-bridge LSX server (slips past anadius's in-process :3216 intercept);
gated off under the `capture_baseline` feature.
- recv_hook.rs: boundary-safe trampolines + LSX peer filtering for the
capture_baseline path (log anadius's real LSX frames when the redirect is off).
Build the instrumented DLL with `--features probe` (or `--features capture_baseline`
for the anadius-baseline capture). Both features are off by default.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds connect_hook, connectex_hook, recv_hook, ssl_patch, tls_bypass, lsx, ea_stub,
and origin_spy modules to intercept EA's TLS and socket layers in addition to
getaddrinfo. Adds DLL-level logging to C:\openfut_hook.log for debugging. Also
patches windows-sys feature flags to include Cryptography and Threading APIs needed
by the new hooks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The DLL now reads openfut.cfg from its own directory on DLL_PROCESS_ATTACH
and uses the IP it contains as the redirect target instead of hardcoding
127.0.0.1. Falls back to 127.0.0.1 if the file is absent.
The launcher writes openfut.cfg alongside version.dll when deploying, and
the Setup tab exposes a "Redirect IP" field with an "Update" button that
rewrites openfut.cfg in-place without redeploying the DLL. Useful when
running the emulator on a different machine on the LAN.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds openfut-hook/, a Windows DLL (cdylib, x86_64-pc-windows-gnu) that
patches the IAT of FIFA 23 at load time to redirect getaddrinfo calls
for fut.ea.com / utas.*.fut.ea.com to 127.0.0.1, sending all FUT
traffic to the local bridge — no /etc/hosts changes needed.
Deployment: the launcher copies openfut_hook.dll into the FIFA 23 game
folder as version.dll (a DLL FIFA loads but delegates to system).
Proton picks up the local copy automatically when you set:
WINEDLLOVERRIDES="version=n,b" %command%
in Steam launch options.
Also updates cert install to try the Wine/Proton cert store (wine
certutil) before falling back to the Linux system CA store, and removes
all hosts file code from setup.rs / app.rs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>