M2: flips fire but gate is async event (not a poll)
- Hook: force GetInternetConnectedState->connected (flags +0xCAB1A/+0xCAB1B) and flip GoOnline to report "1" (+0xAF530) instead of "0" (+0xADE64). - protossl-scan: add `read` mode (hex/ascii dump at addr|module+off). - Finding: neither flip unblocks the game; it keeps retrying GoOnline every ~7s. The FUT-online flow is event-driven -- the game waits for an async "online established" event anadius (offline-only) never pushes. Documented in connection-gate-findings.md. Next: trace FIFA-side flow (Ghidra) / anadius event-send to inject the online event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+52
-8
@@ -419,12 +419,49 @@ unsafe fn install_detour_at(addr: usize, detour: *const (), slot: &AtomicUsize,
|
||||
|
||||
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})"));
|
||||
/// M2 flip on anadius's GoOnline handler (anadius64.dll+0x2BB90). The original
|
||||
/// handler is `mov rcx,rdx; lea r8,[+0xADD73]; lea rdx,[+0xADE64 = "0"]; call
|
||||
/// +0x25BE0; mov al,1` — i.e. it builds its ErrorSuccess response with the value
|
||||
/// "0" (offline). We replicate it but pass "1" (+0xAF530 = the connected value),
|
||||
/// so GoOnline reports online, then return success (al=1).
|
||||
unsafe extern "system" fn hooked_goonline(_a: usize, b: usize, _c: usize, _d: usize) -> usize {
|
||||
let base = ANADIUS_BASE.load(Ordering::SeqCst);
|
||||
if base != 0 {
|
||||
log("FLIP GoOnline -> reporting online (\"1\")");
|
||||
let builder: unsafe extern "system" fn(usize, usize, usize) -> usize =
|
||||
core::mem::transmute(base + 0x25BE0);
|
||||
// 0x25BE0(rcx = handler's rdx, rdx = "1", r8 = +0xADD73)
|
||||
builder(b, base + 0xAF530, base + 0xADD73);
|
||||
return 1;
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// --- M2 flip: force GetInternetConnectedState to report "connected" --------
|
||||
|
||||
static ORIG_ICS: AtomicUsize = AtomicUsize::new(0);
|
||||
static ANADIUS_BASE: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// anadius's GetInternetConnectedState handler (anadius64.dll+0x27790) builds an
|
||||
/// LSX response whose `connected` value is:
|
||||
/// (byte[+0xCAB1B] || byte[+0xCAB1A]) ? connected : offline
|
||||
/// Both default to 0 → offline → the game aborts at "connecting". We force both
|
||||
/// flags to 1 before the original runs, so it builds the "connected" response.
|
||||
unsafe extern "system" fn hooked_ics(a: usize, b: usize, c: usize, d: usize) -> usize {
|
||||
let base = ANADIUS_BASE.load(Ordering::SeqCst);
|
||||
if base != 0 {
|
||||
core::ptr::write_volatile((base + 0xCAB1A) as *mut u8, 1u8);
|
||||
core::ptr::write_volatile((base + 0xCAB1B) as *mut u8, 1u8);
|
||||
}
|
||||
log("FLIP GetInternetConnectedState -> forcing connected (flags set)");
|
||||
let orig = ORIG_ICS.load(Ordering::SeqCst);
|
||||
if orig != 0 {
|
||||
let f: unsafe extern "system" fn(usize, usize, usize, usize) -> usize =
|
||||
core::mem::transmute(orig);
|
||||
@@ -434,21 +471,28 @@ unsafe extern "system" fn hooked_goonline(a: usize, b: usize, c: usize, d: usize
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Resolve anadius64.dll's runtime base, detour the GoOnline probe, and install
|
||||
/// the M2 GetInternetConnectedState flip. Returns false if anadius isn't loaded.
|
||||
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
|
||||
};
|
||||
ANADIUS_BASE.store(base, Ordering::SeqCst);
|
||||
log(&format!("anadius64.dll base = 0x{base:X}"));
|
||||
|
||||
install_detour_at(
|
||||
base + 0x2BB90,
|
||||
hooked_goonline as *const (),
|
||||
&ORIG_GOONLINE,
|
||||
"PROBE anadius GoOnline @ +0x2BB90",
|
||||
);
|
||||
install_detour_at(
|
||||
base + 0x27790,
|
||||
hooked_ics as *const (),
|
||||
&ORIG_ICS,
|
||||
"FLIP anadius GetInternetConnectedState @ +0x27790",
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user