/// 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 = OnceLock::new(); static REAL_REG_W: OnceLock = OnceLock::new(); static REAL_MUTEX_A: OnceLock = OnceLock::new(); static REAL_MUTEX_W: OnceLock = 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 }