Files
openfut-launcher/openfut-hook/src/origin_spy.rs
T
2026-08-07 12:03:21 -07:00

138 lines
3.9 KiB
Rust

/// Hooks RegQueryValueExA/W and OpenMutexA/W to log what the Origin SDK is checking.
use std::sync::OnceLock;
type RegQueryValueExAFn = unsafe extern "system" fn(
hkey: isize,
lpvaluename: *const u8,
lpreserved: *mut u32,
lptype: *mut u32,
lpdata: *mut u8,
lpcbdata: *mut u32,
) -> i32;
type RegQueryValueExWFn = unsafe extern "system" fn(
hkey: isize,
lpvaluename: *const u16,
lpreserved: *mut u32,
lptype: *mut u32,
lpdata: *mut u8,
lpcbdata: *mut u32,
) -> i32;
type OpenMutexAFn = unsafe extern "system" fn(u32, i32, *const u8) -> isize;
type OpenMutexWFn = unsafe extern "system" fn(u32, i32, *const u16) -> isize;
static REAL_REG_A: OnceLock<RegQueryValueExAFn> = OnceLock::new();
static REAL_REG_W: OnceLock<RegQueryValueExWFn> = OnceLock::new();
static REAL_MUTEX_A: OnceLock<OpenMutexAFn> = OnceLock::new();
static REAL_MUTEX_W: OnceLock<OpenMutexWFn> = OnceLock::new();
pub fn set_real_reg_a(f: RegQueryValueExAFn) {
let _ = REAL_REG_A.set(f);
}
pub fn set_real_reg_w(f: RegQueryValueExWFn) {
let _ = REAL_REG_W.set(f);
}
pub fn set_real_mutex_a(f: OpenMutexAFn) {
let _ = REAL_MUTEX_A.set(f);
}
pub fn set_real_mutex_w(f: OpenMutexWFn) {
let _ = REAL_MUTEX_W.set(f);
}
fn narrow_to_string(p: *const u8) -> String {
if p.is_null() {
return "(null)".into();
}
let bytes = unsafe { std::ffi::CStr::from_ptr(p as *const i8) };
bytes.to_string_lossy().into_owned()
}
fn wide_to_string(p: *const u16) -> String {
if p.is_null() {
return "(null)".into();
}
let mut len = 0usize;
unsafe {
while *p.add(len) != 0 {
len += 1;
}
}
String::from_utf16_lossy(unsafe { std::slice::from_raw_parts(p, len) })
}
fn is_interesting(name: &str) -> bool {
name.contains("LSX")
|| name.contains("Origin")
|| name.contains("EAL")
|| name.contains("Client")
|| name.contains("lsx")
|| name.contains("Port")
|| name.contains("EA")
|| name.contains("Connection")
}
pub unsafe extern "system" fn hooked_reg_query_a(
hkey: isize,
lpvaluename: *const u8,
lpreserved: *mut u32,
lptype: *mut u32,
lpdata: *mut u8,
lpcbdata: *mut u32,
) -> i32 {
let name = narrow_to_string(lpvaluename);
let real = REAL_REG_A.get().copied().unwrap();
let ret = real(hkey, lpvaluename, lpreserved, lptype, lpdata, lpcbdata);
if is_interesting(&name) {
crate::write_log(&format!("origin_spy: RegQueryValueExA({name}) → {ret}\n"));
}
ret
}
pub unsafe extern "system" fn hooked_reg_query_w(
hkey: isize,
lpvaluename: *const u16,
lpreserved: *mut u32,
lptype: *mut u32,
lpdata: *mut u8,
lpcbdata: *mut u32,
) -> i32 {
let name = wide_to_string(lpvaluename);
let real = REAL_REG_W.get().copied().unwrap();
let ret = real(hkey, lpvaluename, lpreserved, lptype, lpdata, lpcbdata);
if is_interesting(&name) {
crate::write_log(&format!("origin_spy: RegQueryValueExW({name}) → {ret}\n"));
}
ret
}
pub unsafe extern "system" fn hooked_open_mutex_a(
dwdesiredaccess: u32,
binherithandle: i32,
lpmutexname: *const u8,
) -> isize {
let name = narrow_to_string(lpmutexname);
let real = REAL_MUTEX_A.get().copied().unwrap();
let handle = real(dwdesiredaccess, binherithandle, lpmutexname);
crate::write_log(&format!(
"origin_spy: OpenMutexA({name}) → {}\n",
if handle == 0 { "NOT_FOUND" } else { "FOUND" }
));
handle
}
pub unsafe extern "system" fn hooked_open_mutex_w(
dwdesiredaccess: u32,
binherithandle: i32,
lpmutexname: *const u16,
) -> isize {
let name = wide_to_string(lpmutexname);
let real = REAL_MUTEX_W.get().copied().unwrap();
let handle = real(dwdesiredaccess, binherithandle, lpmutexname);
crate::write_log(&format!(
"origin_spy: OpenMutexW({name}) → {}\n",
if handle == 0 { "NOT_FOUND" } else { "FOUND" }
));
handle
}