Files
openfut-launcher/openfut-hook/src/fifa17.rs
T
2026-08-07 11:43:08 -07:00

109 lines
4.4 KiB
Rust

//! FIFA 17 injection path (feature = "fifa17").
//!
//! This is a *separate, minimal* entry point from the FIFA-23 `install_hooks`.
//! FIFA 17 is a different game with different in-memory structures, so we run NONE
//! of the FIFA-23 connect/LSX/origin_spy/dial logic here — that would at best
//! no-op and at worst crash. For now this proves the version.dll hijack actually
//! loads us into FIFA17.exe and dumps the module map, which we need to locate
//! DirtySDK/ProtoSSL's cert-verify function (the next milestone: patch it so the
//! secure Blaze redirector's TLS handshake succeeds against our bridge cert).
//!
//! Everything here is read-only except the (not-yet-enabled) cert-verify patch.
use crate::write_log;
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Module32FirstW, Module32NextW, MODULEENTRY32W, TH32CS_SNAPMODULE,
TH32CS_SNAPMODULE32,
};
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleA;
/// Read the SizeOfImage from a module's in-memory PE headers.
unsafe fn size_of_image(base: usize) -> u32 {
if base == 0 {
return 0;
}
// DOS header -> e_lfanew (i32 @ 0x3c) -> PE header. SizeOfImage is in the
// optional header at offset 0x50 from the PE signature (same for PE32/PE32+).
let e_lfanew = *((base + 0x3c) as *const i32);
let pe = base + e_lfanew as usize;
// sanity: 'PE\0\0'
if *(pe as *const u32) != 0x0000_4550 {
return 0;
}
*((pe + 24 + 0x38) as *const u32) // opt header +0x38 = SizeOfImage
}
fn wide_to_string(w: &[u16]) -> String {
let end = w.iter().position(|&c| c == 0).unwrap_or(w.len());
String::from_utf16_lossy(&w[..end])
}
/// Enumerate loaded modules (name, base, size) via ToolHelp and log them.
unsafe fn dump_modules() {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, 0);
if snap == INVALID_HANDLE_VALUE {
write_log("fifa17: module snapshot FAILED\n");
return;
}
let mut me: MODULEENTRY32W = core::mem::zeroed();
me.dwSize = core::mem::size_of::<MODULEENTRY32W>() as u32;
if Module32FirstW(snap, &mut me) != 0 {
loop {
let name = wide_to_string(&me.szModule);
let base = me.modBaseAddr as usize;
let size = me.modBaseSize;
write_log(&format!(
"fifa17: module {name:<28} base={base:#018x} size={size:#x}\n"
));
me.dwSize = core::mem::size_of::<MODULEENTRY32W>() as u32;
if Module32NextW(snap, &mut me) == 0 {
break;
}
}
} else {
write_log("fifa17: Module32FirstW FAILED\n");
}
CloseHandle(snap);
}
/// Worker that runs AFTER DllMain returns (loader lock released). ToolHelp and
/// other loader-touching calls are unsafe under the loader lock, so we defer them
/// to this thread. This is what fixed the "game exits right after DllMain" issue.
unsafe extern "system" fn worker(_: *mut core::ffi::c_void) -> u32 {
write_log("=== fifa17 hook: worker thread start ===\n");
let main_base = GetModuleHandleA(core::ptr::null()) as usize;
let img = size_of_image(main_base);
write_log(&format!(
"fifa17: main exe base={main_base:#018x} SizeOfImage={img:#x}\n"
));
dump_modules();
write_log("fifa17: worker complete (injection healthy)\n");
// SBC render intervention (inert unless OPENFUT_SBC_HOOK=1). Spawns its own deferred
// worker that waits for CardsDLL to load. See sbc_hook.rs / docs/sbc-hook-dll-spec.md.
crate::sbc_hook::install();
// Passive transaction tracing has a separate kill switch from cache resolution.
// It currently fails closed until safe relocating trampolines are proven.
crate::sbc_trace::install();
crate::sbc_request_trace::install();
0
}
/// Minimal FIFA-17 install. Keep DllMain itself trivial: only spawn a worker
/// thread and return immediately, so we never touch the loader lock from here.
pub unsafe fn install() {
use windows_sys::Win32::System::Threading::CreateThread;
write_log("=== fifa17 hook: DllMain ATTACH (spawning worker) ===\n");
let h = CreateThread(
core::ptr::null(),
0,
Some(worker),
core::ptr::null(),
0,
core::ptr::null_mut(),
);
if h == 0 as _ {
write_log("fifa17: CreateThread FAILED\n");
}
}