f16828a584
RETAINED PRE-EXISTING client-hook WIP (brought forward after verification). Adds resolver_hook.rs + store_hook.rs and a new openfut-common crate, plus config/connect/fifa17/hooks/lib refinements. No secrets/staging addresses. Preserved on feat/sbc-hook-tracing so it is recoverable and pushed.
255 lines
8.2 KiB
Rust
255 lines
8.2 KiB
Rust
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 (),
|
|
);
|
|
}
|
|
}
|