mod config; mod connect_hook; mod connectex_hook; mod dial_notification; #[cfg(feature = "fifa17")] mod fifa17; mod hooks; mod iat; mod origin_spy; #[cfg(feature = "probe")] mod probe; #[cfg(feature = "capture_baseline")] mod recv_hook; mod resolver_hook; #[cfg(feature = "fifa17")] mod sbc_hook; #[cfg(feature = "fifa17")] mod sbc_request_trace; #[cfg(feature = "fifa17")] mod sbc_trace; #[cfg(feature = "fifa17")] mod store_hook; mod ssl_patch; mod tls_bypass; mod transport_watch; mod version_proxy; use windows_sys::Win32::{ Foundation::{BOOL, HMODULE, TRUE}, Networking::WinSock::ADDRINFOA, System::SystemServices::DLL_PROCESS_ATTACH, }; pub(crate) fn write_log(msg: &str) { use std::io::Write; if let Ok(mut f) = std::fs::OpenOptions::new() .create(true) .append(true) .open(r"C:\openfut_hook.log") { let _ = f.write_all(msg.as_bytes()); } } /// Force the log to stable storage. `write_log` already opens+closes the file per line, /// so nothing is buffered *inside our process* (a process crash can't lose a written /// line). `sync_all` additionally flushes the OS cache to disk, for durability even /// across a full system crash. We call this right before the dial trigger's call so the /// pre-call log line is guaranteed on disk if the call faults. #[allow(dead_code)] pub(crate) fn flush_log() { if let Ok(f) = std::fs::OpenOptions::new() .append(true) .open(r"C:\openfut_hook.log") { let _ = f.sync_all(); } } #[no_mangle] pub unsafe extern "system" fn DllMain(module: HMODULE, reason: u32, _: *mut ()) -> BOOL { if reason == DLL_PROCESS_ATTACH { // VERSION forwarding must be ready before DllMain returns. Hook setup // may be deferred, but a caller can use any proxy export immediately. if version_proxy::resolve() { install_hooks(module); } } TRUE } unsafe fn install_hooks(module: HMODULE) { // FIFA 17 path: run ONLY the minimal, FIFA-17-safe logic and skip every // FIFA-23-specific hook below (they assume FIFA 23's memory layout). #[cfg(feature = "fifa17")] { fifa17::install(module); return; } #[cfg(not(feature = "fifa17"))] install_hooks_fifa23(module) } #[cfg(not(feature = "fifa17"))] unsafe fn install_hooks_fifa23(module: HMODULE) { write_log("openfut_hook: DllMain fired\n"); // Milestone-0 transport watch: arm (or note disarmed) from env once, up front, so // the getaddrinfo/connect/ConnectEx detours below can log Blaze-flavored activity. transport_watch::arm_from_env(); match config::load_config(module).and_then(|c| c.resolve()) { Ok(server) => { hooks::set_redirect_ip(server.redirect_ip.to_string()); connect_hook::set_server(server); } Err(e) => { write_log(&format!( "openfut_hook: invalid/missing openfut.cfg ({e}); redirection DISABLED\n" )); } } let ga = iat::resolve(b"ws2_32.dll\0", b"getaddrinfo\0"); if !ga.is_null() { let f: unsafe extern "system" fn( *const u8, *const u8, *const ADDRINFOA, *mut *mut ADDRINFOA, ) -> i32 = std::mem::transmute(ga); hooks::set_real(f); let n = iat::patch_iat(ga, hooks::hooked_getaddrinfo as *const ()); let m = iat::patch_iat_in( b"EAWebKit.dll\0", ga, hooks::hooked_getaddrinfo as *const (), ); write_log(&format!("openfut_hook: getaddrinfo IAT patched {n}+{m}\n")); } if ssl_patch::patch_main_exe_cert_verify() { write_log("ssl: main exe cert-verify patched\n"); } else { write_log("ssl: main exe cert-verify NOT FOUND\n"); } if ssl_patch::patch_eawebkit_cert_verify() { write_log("ssl: EAWebKit cert-verify patched\n"); } else { write_log("ssl: EAWebKit cert-verify deferred\n"); } if connect_hook::install_inline_connect_hook() { write_log("connect: inline-hooked\n"); } else { write_log("connect: hook FAILED\n"); } let wp = iat::resolve(b"ws2_32.dll\0", b"WSAConnect\0"); if !wp.is_null() { let f: unsafe extern "system" fn( usize, *const u8, i32, *const (), *const (), *const (), *const (), ) -> i32 = std::mem::transmute(wp); connect_hook::set_real_wsa_connect(f); iat::patch_iat(wp, connect_hook::hooked_wsa_connect as *const ()); write_log("connect: WSAConnect IAT patched\n"); } if connectex_hook::install_wsaioctl_hook() { write_log("connectex: WSAIoctl inline-hooked\n"); } else { write_log("connectex: WSAIoctl hook FAILED\n"); } // RE instrumentation: passive logging detours on FIFA's in-process online-flow // functions (GoOnline, GetInternetConnectedState, event deserializers) to see // where FIFA stalls after our pushed LSX events. Deferred until anadius loads. #[cfg(feature = "probe")] { probe::install_probes_deferred(); write_log("probe: deferred install scheduled\n"); } // recv/send hooks removed — LSX is now handled by the native openfut-bridge // LSX server (port 3216), so in-process interception is no longer needed. // // Except in the `capture_baseline` build: with the LSX redirect off, FIFA talks // to anadius directly, and these hooks log anadius's real LSX request/response // frames (pass-through, no emulation) so we can diff them against our bridge. #[cfg(feature = "capture_baseline")] { if recv_hook::install_recv_hook() { write_log("CAP: recv inline-hooked\n"); } else { write_log("CAP: recv hook FAILED\n"); } if recv_hook::install_send_hook() { write_log("CAP: send inline-hooked\n"); } else { write_log("CAP: send hook FAILED\n"); } } macro_rules! hook_iat { ($dll:expr, $sym:expr, $setter:ident, $handler:expr, $ty:ty) => {{ let ptr = iat::resolve($dll, $sym); if !ptr.is_null() { let f: $ty = std::mem::transmute(ptr); origin_spy::$setter(f); iat::patch_iat(ptr, $handler as *const ()); "ok" } else { "miss" } }}; } let ra = hook_iat!( b"advapi32.dll\0", b"RegQueryValueExA\0", set_real_reg_a, origin_spy::hooked_reg_query_a, unsafe extern "system" fn(isize, *const u8, *mut u32, *mut u32, *mut u8, *mut u32) -> i32 ); let rw = hook_iat!( b"advapi32.dll\0", b"RegQueryValueExW\0", set_real_reg_w, origin_spy::hooked_reg_query_w, unsafe extern "system" fn(isize, *const u16, *mut u32, *mut u32, *mut u8, *mut u32) -> i32 ); let ma = hook_iat!( b"kernel32.dll\0", b"OpenMutexA\0", set_real_mutex_a, origin_spy::hooked_open_mutex_a, unsafe extern "system" fn(u32, i32, *const u8) -> isize ); let mw = hook_iat!( b"kernel32.dll\0", b"OpenMutexW\0", set_real_mutex_w, origin_spy::hooked_open_mutex_w, unsafe extern "system" fn(u32, i32, *const u16) -> isize ); write_log(&format!( "origin_spy: RegA={ra} RegW={rw} MutexA={ma} MutexW={mw}\n" )); let cv = iat::resolve(b"crypt32.dll\0", b"CertVerifyCertificateChainPolicy\0"); if !cv.is_null() { let f: unsafe extern "system" fn(*const u8, *const (), *const (), *mut u32) -> BOOL = std::mem::transmute(cv); tls_bypass::set_real(f); iat::patch_iat(cv, tls_bypass::hooked_cert_verify_chain_policy as *const ()); iat::patch_iat_in( b"EAWebKit.dll\0", cv, tls_bypass::hooked_cert_verify_chain_policy as *const (), ); iat::patch_iat_in( b"winhttp.dll\0", cv, tls_bypass::hooked_cert_verify_chain_policy as *const (), ); iat::patch_iat_in( b"wininet.dll\0", cv, tls_bypass::hooked_cert_verify_chain_policy as *const (), ); } }