docs: Blaze handshake reference, connection-gate findings, closure write-up

- blaze-handshake.md: three-layer LSX/Blaze-core/UTAS model + six-step Blaze
  session ordering, tagged CONFIRMED/SHAPE/UNKNOWN.
- connection-gate-findings.md: accumulated RE of the online-connect gate.
- closure-and-preservation.md: the capstone. Records what was achieved
  (clean-room LSX/EbisuSDK emulator booting FIFA to an authenticated offline
  menu), the wall reached from three directions, the dial-initiation gate RE
  as the final chapter (Outcome B, circular), and the boundary. Clean-room
  throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-07-03 15:58:37 -07:00
parent 61b138db89
commit 3649c3d6f0
3 changed files with 639 additions and 0 deletions
+162
View File
@@ -1056,3 +1056,165 @@ ctx, endpoint map) sits in its initial empty/dormant state because no connection
trigger the dial. The true trigger — "start the online connection / dial gosredirector" — is
upstream and never fires; that remains the M2 frontier. Forcing was valuable (safe; corrected
the function's role; proved the endpoint map empty) but did not find a callable shortcut.
---
## 2026-07-02 (late): the go-online wall is a DORMANT TICK, not a false state
Built `openfut-poke` (openfut-bridge/src/bin/openfut-poke.rs) — /proc/<pid>/mem
inspect/arm/restore. Two live forcing experiments + deep DirtySDK RE pinned the
mechanism precisely. **Writes to /proc/pid/mem work same-uid under ptrace_scope=1.**
### The app-side gate (recap)
`fn 0x144f4d360` is the online-state handler; it calls `NetConnStatus('conn')`
(`0x140ef17f0`) and only proceeds (`0x144f4d47c je 0x144f4d590` → redirector resolve +
dial) when the result == `+onl` (0x2b6f6e6c).
### Two forcing experiments — BOTH negative, same reason
1. HARD poke: patched the gate je→nop;jmp (0f 84 → 90 e9). No dial.
2. SOFT poke: held `NetConnStatus('conn')` field `[X+0x48]=+onl` (X=*(0x149fe5e50),
live 0xafbc00d0) for 4 min via 100ms re-poke. No dial.
Reason: FIFA online bring-up is EVENT-DRIVEN. `fn 0x144f4d360` runs only when DirtySDK
FIRES a state-change notification; forcing what a reader SEES never generates that
notification. Only the NetConn tick does, on a real ~con→+onl transition.
### Why `NetConnStatus('conn')` = ~con — traced to the bottom
- The low-level query `0x140f02640('conn')` with rcx=0 delegates to `0x140f03550`,
which returns **`+onl` UNCONDITIONALLY** for 'conn' (any selector≠'addr' hits the
switch; 'conn' → `mov eax,0x2b6f6e6c`). **So the network-stack layer already reports
online — it is NOT the blocker.**
- `[X+0x48]='~con'` is the conn-module ('dflt') state-machine's CACHED status, promoted
to +onl by the tick `0x140f05430` (writes ~con/~cln/-srv/-skt/-err/-act/+onl).
- Live conn-module fields (X=0xafbc00d0): phase [X+0x10]=1, [X+0x48]='~con',
[X+0x20]=0 (promotion hinge), [X+0x68]=0xba802510, [X+0x18/0x28/0x30/0x38]=0.
Walking the tick's phase-1 path with these values: lower query=+onl → iface check
0x140ef1730=0(ok) → [X+0x20]==0 branch → [X+0x68]!=0 → falls straight to
`0x140f0566d: mov [X+0x48],'+onl'` and phase→2. **Every promotion precondition is
already satisfied. One tick WOULD promote AND fire the notification.**
- Passive watch: 90s, ZERO changes to status/phase. **The tick is DORMANT.**
### Root cause
The conn module is fully primed to go online but **its state-machine tick
`0x140f05430` is never called.** Callers of the tick: it's passed as a CALLBACK
(`lea rcx,[0x140f05430]; mov rdx,module; call 0x140f16b40 / 0x140f16ae0`) from the
conn-module connect/update entry points `0x140f047a0` and `0x140f049a0` (7 sites). No
static fn-pointer registration. So the tick only runs when those entry points run, i.e.
when the connect flow is STARTED (NetConnConnect/Update). It never starts offline →
module sits primed-but-unpumped. This is the SAME root as the hard-poke finding, now
proven at the DirtySDK layer: **nothing ever STARTS the online connect.**
### Actionable next step (needs in-process execution, i.e. the hook — not /proc/mem)
`/proc/mem` can't call a function safely. The surgically-correct force is to run ONE
legitimate tick from inside the process: have `openfut_hook.dll` call `0x140f05430(X)`
(or the connect entry `0x140f047a0`), where X=*(0x149fe5e50). Per the live trace this
promotes ~con→+onl AND fires the state-change notification `fn 0x144f4d360` waits on —
coherent, not a fake. Alternative: climb from 0x140f047a0/0x140f049a0 to the real
"start online" trigger (NetConnConnect caller) and see why it never fires offline.
See [[project_hard_poke_result]].
### Refinement: the tick is REGISTERED in NetConnIdle but the pump is dormant
The tick 0x140f05430 is handed to two helpers at its 7 call sites:
- `0x140f16ae0` = NetConnIdleAdd (register) — a jmp thunk into the Anadius hot-patch
region 0x1555ea650; result checked with `jns` (success).
- `0x140f16b40` = NetConnIdleDel (deregister) — searches the callback table
0x149fe6300..0x149fe6708 (16B {fn,arg} entries) and zeroes the matching entry.
LIVE read of that table: the conn tick **IS registered** —
`0x149fe6300: fn=0x140f05430 arg=0xafbc00d0` — plus 9 other module entries
(0x1450c2ec0 ×7, 0x140f18110). So the tick is in the idle pump list, ready. Yet 90s
passive watch shows zero promotion, and walking the tick with live fields shows it WOULD
promote. Conclusion: **the NetConnIdle pump loop that iterates this table is not being
driven** (the app doesn't call NetConnIdle until the online flow is kicked off). The
conn-module internal entries (0x140f047a0/0x140f049a0/0x140f05430/0x140ef1790) only call
each other; the external trigger that starts the pump is higher up the stack (unmapped).
### Cleanest coherent force (for a hook, next session)
Since the tick is registered and every promotion precondition is met, the minimal
coherent action is to run ONE tick in-process: from openfut_hook.dll, on the game's
main thread, call `0x140f05430(rcx = *(0x149fe5e50))`. Per the live trace this writes
[X+0x48]=+onl, advances phase→2, and fires the state-change notification that
`fn 0x144f4d360` reacts to → redirector resolve + dial. If a foreign-thread call races
the game, prefer registering our own idle callback or calling on an existing DirtySDK
callback. Open question to resolve first: what normally drives the NetConnIdle pump, and
why it never starts offline — that is the true upstream trigger.
### The pump gate is 'open' = [X+0xcd] — and it's already SATISFIED
The idle pump `0x140f16a50` (iterates the callback table, calls each fn(arg,elapsed))
is gated at its top:
```
140f16a5c: mov ecx,'open'(0x6f70656e); call 0x140ef17f0 (NetConnStatus)
140f16a66: test eax,eax; je 0x140f16ada ; if 'open'==0 -> skip ALL callbacks
```
`NetConnStatus('open')` (handler at 0x140ef1dc8) returns the BYTE `[NetConn+0xcd]`
(0 if NetConn null). **Live: [X+0xcd] = 1 → 'open'=1 → the gate is OPEN.** So the pump
is NOT gated off. Combined with the tick being registered and dormant, the only
remaining explanation is that **`0x140f16a50` (NetConnIdle's core pump) is itself never
called** — FIFA's frame loop isn't invoking NetConnIdle while offline at the menu.
**Net chain (app gate → root):**
1. fn 0x144f4d360 dials only if NetConnStatus('conn')=='+onl' (event/notification driven).
2. NetConnStatus('conn') = conn-module cached [X+0x48] = '~con'.
3. Tick 0x140f05430 would promote ~con->+onl (all live preconditions met) + fire notify.
4. Tick is registered in NetConnIdle table 0x149fe6300, pumped by 0x140f16a50.
5. Pump gated on 'open'=[X+0xcd]=1 -> OPEN (not the blocker).
6. => Pump 0x140f16a50 is simply never called. Frame loop doesn't drive NetConnIdle offline.
**Cleanest force (hook, next session):** from openfut_hook.dll on the game thread, call
`0x140f16a50()` (NetConnIdle core) periodically — no args; it reads globals. It pumps
all idle callbacks incl. the conn tick, which promotes to +onl and fires the
notification fn 0x144f4d360 reacts to -> redirector resolve + dial. This is fully
coherent (runs the game's own logic). Equivalent narrower option: call
`0x140f05430(rcx=*(0x149fe5e50))`. Confirm by watching [X+0x48] flip to '+onl' and a
dial to 127.0.0.1:8443. Open question (minor): who is *supposed* to call 0x140f16a50 and
why it stops offline — but forcing the pump sidesteps it.
### RESOLVED: NetConnIdle is pumped ONLY by online paths — bootstrap circularity
Callers of the pump 0x140f16a50 (NetConnIdle core), all online-connection code:
- 0x14508a500 / 0x14508ae90 : pump-with-timeout WAIT LOOPS (cmp cnt,5; jae skip; then
1000ms wait via 0x1410f7cc0) — "pump NetConnIdle while waiting for connect".
- 0x145057a00 : pumps only if [rax+0x55d]!=0 (Nucleus/online region ~0x145057670).
- 0x14279d5c0 : online-init; sets [rsi+0x368]=1 then pumps; singleton 0x14acd02d0.
- 0x1427f55f0 : pump loop, also calls 0x144f46080 (adjacent to redirector resolver);
singleton 0x14acd02c8.
- 0x14162c6f0 : thunk (public NetConnIdle jmp).
So NetConnIdle is NOT a continuous frame pump — it's driven only by active online
connect/init/wait loops. Offline at the menu those never run => pump never fires =>
conn tick dormant => [X+0x48] stuck '~con'. This is a BOOTSTRAP CIRCULARITY: the online
flow that pumps NetConnIdle only starts once the app decides to go online, which needs
conn=+onl, which needs the tick pumped. Nothing breaks the circle while offline. This is
the same "online flow never starts" root as the hard/soft pokes, now proven from the
BOTTOM of the DirtySDK stack.
### THE actionable fix (external pump from the hook) — highest-confidence next step
Break the circle from outside: from openfut_hook.dll, on the game's main thread, call
`0x140f16a50()` periodically (e.g. each frame or every ~16ms), exactly like the game's
own wait-loops (0x14508a500) do. It takes no args (reads globals), 'open' gate
[X+0xcd]=1 is already satisfied, and it will pump the conn tick -> [X+0x48]=+onl ->
phase 2 -> fires the state-change notification fn 0x144f4d360 reacts to -> redirector
resolve (our override 127.0.0.1:8443) + dial. Fully coherent (runs the game's own code).
Verify: watch [X+0x48] flip to '+onl' and a socket to 127.0.0.1:8443.
Risk notes: call on a game thread (not a foreign thread) to avoid racing DirtySDK state;
NetConnIdle is re-entrant-guarded by the game's own usage so periodic calls are safe.
Fallback if pumping causes issues: call the single conn tick 0x140f05430(rcx=*(0x149fe5e50)).
### Threading check + implementation status
The game's own pump-wait loop (0x14508a560): `NetConnStatus('nste') -> cmp retry<5 ->
call 0x140f16a50 (pump) -> wait 1000ms -> repeat`, with NO lock/critical-section
bracketing the pump call. So NetConnIdle is designed for single-threaded loop use.
Implication: pumping from our background thread is LOW-RISK while offline (nothing else
pumps, so no race); the only residual risk is after promotion when the game's own online
loops begin — acceptable for a forcing experiment, mitigate later with a game-thread
detour if needed.
IMPLEMENTED (ready to build, OFF by default): openfut-hook `probe::install_force_netconn_pump()`
(openfut-launcher/openfut-hook/src/probe.rs) — spawns a thread, waits for the NetConn
global, calls the pump (base+0xf16a50) ~every 100ms, logs conn[+0x48], stops at +onl.
Mirrors the existing `install_force_connect`. Enable by uncommenting the call in
`install_probes_deferred`, cross-build (x86_64-pc-windows-gnu), deploy the DLL, arm the
bridge override (openfut-poke arm --host 127.0.0.1 --port 8443), relaunch FIFA. Compiles
clean for the Windows target. Expected: hook.log shows PUMP lines, conn flips to '+onl',
and a dial to 127.0.0.1:8443 (the Blaze handshake start).