M1 probe: GoOnline passes, gate is downstream (token/status)
Add a read-only detour on anadius's GoOnline handler (anadius64.dll+0x2BB90). Result: the game calls GoOnline during the "connecting" attempt and anadius returns success, yet no Blaze connection follows. The gate is therefore downstream of GoOnline -- the auth-token (GetAuthCode) and/or the "online established" status callback. Recorded in connection-gate-findings.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -119,7 +119,26 @@ only — no EA source.
|
|||||||
- `TODO/CONFIRM` the exact abort point (no-token vs state-says-offline vs both) —
|
- `TODO/CONFIRM` the exact abort point (no-token vs state-says-offline vs both) —
|
||||||
needs Ghidra-level control-flow tracing.
|
needs Ghidra-level control-flow tracing.
|
||||||
|
|
||||||
### Recommendation
|
### Dynamic probe result (read-only) — `GoOnline` is NOT the gate
|
||||||
Load `FIFA23.exe` into **Ghidra** to (a) name the connection-state getter
|
A read-only detour on anadius's `GoOnline` handler (`anadius64.dll+0x2BB90`)
|
||||||
precisely and (b) confirm the gate mechanism, then return to `protossl-scan` +
|
shows the game **does** call `GoOnline` during the "connecting" attempt (incl.
|
||||||
the hook to map those addresses onto the live process and install the M2 hook.
|
on the Ultimate Team click) and anadius returns success — **yet no Blaze
|
||||||
|
connection follows** (still zero external `CONNECT`/DNS).
|
||||||
|
|
||||||
|
Therefore the gate is **downstream of `GoOnline`**: the game decides to go
|
||||||
|
online and the request is accepted, then it aborts at the **auth-token step
|
||||||
|
(`GetAuthCode`) and/or while waiting for the "online established" status
|
||||||
|
callback** (`ONLINE_STATUS_EVENT`), and times out into offline.
|
||||||
|
|
||||||
|
This sharpens the two-gate picture: `GoOnline` passes; the real blocker is the
|
||||||
|
**token / online-status step**. `TODO/CONFIRM` which (token-missing vs
|
||||||
|
status-never-fires) — via a `GetAuthCode` probe and/or Ghidra.
|
||||||
|
|
||||||
|
### Recommendation / next
|
||||||
|
Name the downstream gate. Two complementary routes:
|
||||||
|
- **Dynamic:** probe anadius's `GetAuthCode` handler (does it return a token or
|
||||||
|
fail?) — distinguishes token-gate from status-callback.
|
||||||
|
- **Static (Ghidra):** xref the `GoOnline` / `GetAuthCode` / `ONLINE_STATUS_EVENT`
|
||||||
|
strings in `FIFA23.exe` to find the FUT online-flow code that issues `GoOnline`
|
||||||
|
then waits for the token/status, and decompile it. FIFA isn't ASLR'd, so Ghidra
|
||||||
|
addresses (image base `0x140000000`) map 1:1 to our recorded offsets.
|
||||||
|
|||||||
+60
-1
@@ -191,15 +191,31 @@ unsafe extern "system" fn init_thread(_: *mut c_void) -> u32 {
|
|||||||
hook_winsock_data();
|
hook_winsock_data();
|
||||||
hook_connect();
|
hook_connect();
|
||||||
|
|
||||||
// Then the plaintext-capture detour on _ProtoSSLSendPacket.
|
// Then the plaintext-capture detour on _ProtoSSLSendPacket, plus the
|
||||||
|
// read-only anadius GoOnline probe (M1). Retry until both are installed.
|
||||||
|
let mut send_done = false;
|
||||||
|
let mut anadius_done = false;
|
||||||
for _ in 0..60 {
|
for _ in 0..60 {
|
||||||
|
if !send_done {
|
||||||
if let Some(addr) = find_send_packet() {
|
if let Some(addr) = find_send_packet() {
|
||||||
install_hook(addr);
|
install_hook(addr);
|
||||||
|
send_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !anadius_done && hook_anadius_probes() {
|
||||||
|
anadius_done = true;
|
||||||
|
}
|
||||||
|
if send_done && anadius_done {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
}
|
}
|
||||||
|
if !send_done {
|
||||||
log("ERROR: _ProtoSSLSendPacket pattern not found after 60s");
|
log("ERROR: _ProtoSSLSendPacket pattern not found after 60s");
|
||||||
|
}
|
||||||
|
if !anadius_done {
|
||||||
|
log("ERROR: anadius64.dll not loaded after 60s; GoOnline probe not installed");
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,6 +393,12 @@ unsafe fn install_detour(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
install_detour_at(addr, detour, slot, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install an inline detour at a raw address (for non-exported targets such as
|
||||||
|
/// internal anadius handlers located by module+offset).
|
||||||
|
unsafe fn install_detour_at(addr: usize, detour: *const (), slot: &AtomicUsize, label: &str) {
|
||||||
let d = match RawDetour::new(addr as *const (), detour) {
|
let d = match RawDetour::new(addr as *const (), detour) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -393,6 +415,43 @@ unsafe fn install_detour(
|
|||||||
log(&format!("{label} hook installed"));
|
log(&format!("{label} hook installed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- M1 read-only probe: anadius GoOnline handler -------------------------
|
||||||
|
|
||||||
|
static ORIG_GOONLINE: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
/// Read-only detour on anadius's GoOnline handler (anadius64.dll+0x2BB90): log
|
||||||
|
/// that it was reached, then call the original unchanged. Tells us whether the
|
||||||
|
/// game even asks to go online during the "connecting" attempt.
|
||||||
|
unsafe extern "system" fn hooked_goonline(a: usize, b: usize, c: usize, d: usize) -> usize {
|
||||||
|
log(&format!("PROBE anadius GoOnline CALLED (rcx=0x{a:X} rdx=0x{b:X})"));
|
||||||
|
let orig = ORIG_GOONLINE.load(Ordering::SeqCst);
|
||||||
|
if orig != 0 {
|
||||||
|
let f: unsafe extern "system" fn(usize, usize, usize, usize) -> usize =
|
||||||
|
core::mem::transmute(orig);
|
||||||
|
f(a, b, c, d)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve anadius64.dll's runtime base and detour the GoOnline handler.
|
||||||
|
/// Returns true once installed (or if anadius isn't present and we should stop
|
||||||
|
/// retrying is decided by the caller). Returns false if anadius isn't loaded yet.
|
||||||
|
unsafe fn hook_anadius_probes() -> bool {
|
||||||
|
let base = match GetModuleHandleW(PCWSTR(wide("anadius64.dll").as_ptr())) {
|
||||||
|
Ok(m) => m.0 as usize,
|
||||||
|
Err(_) => return false, // not loaded yet
|
||||||
|
};
|
||||||
|
log(&format!("anadius64.dll base = 0x{base:X}"));
|
||||||
|
install_detour_at(
|
||||||
|
base + 0x2BB90,
|
||||||
|
hooked_goonline as *const (),
|
||||||
|
&ORIG_GOONLINE,
|
||||||
|
"PROBE anadius GoOnline @ +0x2BB90",
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Detour the DNS resolvers so we see every hostname lookup.
|
/// Detour the DNS resolvers so we see every hostname lookup.
|
||||||
unsafe fn hook_dns() {
|
unsafe fn hook_dns() {
|
||||||
let ws2 = match LoadLibraryW(PCWSTR(wide("ws2_32.dll").as_ptr())) {
|
let ws2 = match LoadLibraryW(PCWSTR(wide("ws2_32.dll").as_ptr())) {
|
||||||
|
|||||||
Reference in New Issue
Block a user