00ad631034
FIFA 23 is not in development and was never a valid template for FIFA 17
(different game, different in-memory layout). Remove it as a build target and
as scaffolding, while preserving the per-game feature architecture so future
games plug in as new modules — never by copying retired reverse-engineering.
Hook (openfut-hook):
- Delete install_hooks_fifa23 and every FIFA23-only module: config, hooks,
transport_watch, ssl_patch, origin_spy, tls_bypass, dial_notification, probe
(+ probe feature), recv_hook (+ capture_baseline feature), plus the orphan
FIFA23 LSX/Origin files lsx.rs and ea_stub.rs. ~3.6k lines; git + Vault retain
the research.
- lib.rs is now game-generic: a per-game feature selects that game's module and
install_hooks dispatches to it. No game feature => compile_error!("select a
game, e.g. --features fifa17"). --features fifa17 remains the build invariant.
- Drop the crate-wide blanket (it existed only
to hide the compiled-but-unused FIFA23 modules). Replace with narrow, justified
#[allow(dead_code)] on the three FIFA17 SBC RE-scaffolding items it was masking,
so the candidate stays behavior-identical.
- connect_hook: the redirect is now always the config-driven path (openfut-common
target from openfut.cfg); the hardcoded-loopback rewrite and its dead consts are
gone. Removed the FIFA23-era transport_watch diagnostics from the shared
connect/WSAConnect/ConnectEx detours. Deleted unused iat::patch_iat_in.
Launcher:
- fifa_game_dir no longer defaults to a hardcoded '.../FIFA 23' Steam path; it is
empty by default, matching the launcher's own rule that it never invents a path
to somebody's game install (like openfut_server_host and game_profile).
- Generalise the remaining 'FIFA 23' doc literals in config.rs / setup.rs.
Proof: fifa17 clippy -D warnings clean; no-game build fails with the documented
compile_error; launcher 75 tests pass unchanged; launcher + hook cross-build
x86_64-pc-windows-gnu; cargo fmt --check clean; zero FIFA23 symbols/literals
remain. FIFA17 armed-module set unchanged (redirect + SBC/store/season).
137 lines
3.8 KiB
Rust
137 lines
3.8 KiB
Rust
/// IAT (Import Address Table) patching.
|
|
///
|
|
/// We define the PE structs ourselves rather than pulling in windows-sys PE
|
|
/// headers (which are in a different crate / feature path).
|
|
use windows_sys::Win32::{
|
|
Foundation::HMODULE,
|
|
System::{
|
|
LibraryLoader::{GetModuleHandleA, GetProcAddress},
|
|
Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE},
|
|
},
|
|
};
|
|
|
|
// ── Minimal PE struct definitions ─────────────────────────────────────────────
|
|
|
|
#[repr(C)]
|
|
struct ImageDosHeader {
|
|
e_magic: u16,
|
|
_pad: [u16; 29],
|
|
e_lfanew: i32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ImageFileHeader {
|
|
machine: u16,
|
|
number_of_sections: u16,
|
|
time_date_stamp: u32,
|
|
pointer_to_symbol_table: u32,
|
|
number_of_symbols: u32,
|
|
size_of_optional_header: u16,
|
|
characteristics: u16,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ImageDataDirectory {
|
|
virtual_address: u32,
|
|
size: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ImageOptionalHeader64 {
|
|
magic: u16,
|
|
_pad: [u8; 110],
|
|
data_directory: [ImageDataDirectory; 16],
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ImageNtHeaders64 {
|
|
signature: u32,
|
|
file_header: ImageFileHeader,
|
|
optional_header: ImageOptionalHeader64,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct ImageImportDescriptor {
|
|
original_first_thunk: u32,
|
|
time_date_stamp: u32,
|
|
forwarder_chain: u32,
|
|
name: u32,
|
|
first_thunk: u32,
|
|
}
|
|
|
|
// ── IAT patching ──────────────────────────────────────────────────────────────
|
|
|
|
/// Replace every IAT slot in the main module that currently holds
|
|
/// `original_fn` with `hook_fn`.
|
|
///
|
|
/// # Safety
|
|
/// Caller must ensure hook_fn has the same calling convention and signature.
|
|
pub unsafe fn patch_iat(original_fn: *const (), hook_fn: *const ()) -> usize {
|
|
let module = GetModuleHandleA(std::ptr::null());
|
|
patch_module(module, original_fn, hook_fn)
|
|
}
|
|
|
|
unsafe fn patch_module(module: HMODULE, original_fn: *const (), hook_fn: *const ()) -> usize {
|
|
if module.is_null() {
|
|
return 0;
|
|
}
|
|
let base = module as usize;
|
|
let dos = base as *const ImageDosHeader;
|
|
if (*dos).e_magic != 0x5A4D {
|
|
return 0;
|
|
}
|
|
|
|
let nt = (base + (*dos).e_lfanew as usize) as *const ImageNtHeaders64;
|
|
|
|
let import_rva = (*nt).optional_header.data_directory[1].virtual_address as usize;
|
|
if import_rva == 0 {
|
|
return 0;
|
|
}
|
|
|
|
let mut desc = (base + import_rva) as *const ImageImportDescriptor;
|
|
let mut count = 0usize;
|
|
|
|
while (*desc).name != 0 {
|
|
let ft = (*desc).first_thunk as usize;
|
|
let iat_slot = (base + ft) as *mut usize;
|
|
let mut i = 0usize;
|
|
|
|
loop {
|
|
let val = *iat_slot.add(i);
|
|
if val == 0 {
|
|
break;
|
|
}
|
|
if val == original_fn as usize {
|
|
let target = iat_slot.add(i) as *const std::ffi::c_void;
|
|
let mut old: u32 = 0;
|
|
VirtualProtect(
|
|
target,
|
|
std::mem::size_of::<usize>(),
|
|
PAGE_EXECUTE_READWRITE,
|
|
&mut old,
|
|
);
|
|
*iat_slot.add(i) = hook_fn as usize;
|
|
VirtualProtect(target, std::mem::size_of::<usize>(), old, &mut old);
|
|
count += 1;
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
desc = desc.add(1);
|
|
}
|
|
|
|
count
|
|
}
|
|
|
|
/// Resolve the address of an exported function from an already-loaded DLL.
|
|
pub unsafe fn resolve(dll: &[u8], fn_name: &[u8]) -> *const () {
|
|
let module = GetModuleHandleA(dll.as_ptr());
|
|
if module.is_null() {
|
|
return std::ptr::null();
|
|
}
|
|
match GetProcAddress(module, fn_name.as_ptr()) {
|
|
Some(f) => f as *const (),
|
|
None => std::ptr::null(),
|
|
}
|
|
}
|