#![cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))] use std::env::VarError; use std::ffi::{c_char, CStr, CString}; use std::sync::OnceLock; use std::thread; use std::time::Duration; use ilhook::x64::Registers; use windows::core::PCSTR; use windows::Win32::System::LibraryLoader::GetModuleHandleA; use crate::{config, curl_utils}; use crate::config::{CONFIG, CurlConfig}; use crate::replacer::{AbstractReplacer, GenericReplacer, Replacer}; type CurlEasySetStr = fn(handle: usize, tag: u64, value: *const c_char); static mut URL_REPLACER: OnceLock> = OnceLock::new(); static UE_CURL_EASY_SETOPT_FUNC: OnceLock = OnceLock::new(); trait EnvBoolMapper { fn map_env_bool(self, default: bool) -> bool; } impl EnvBoolMapper for Result { fn map_env_bool(self, default: bool) -> bool { self.map(|val| match val.as_str() { "y" => true, _ => false }).unwrap_or(default) } } pub(crate) fn configure_extras(interceptor: &mut interceptor_rs::Interceptor) { unsafe { URL_REPLACER.set(vec![ // Config Server Replacer AbstractReplacer::GenericReplacer(GenericReplacer { regex: regex::Regex::new(CONFIG.replacement_config.config_server_regex).unwrap(), replacement: std::env::var("CFG_SERVER_URL") .unwrap_or(config::CONFIG_SERVER_DEFAULT.to_string()), force_http: std::env::var("CFG_SERVER_FORCE_HTTP").map_env_bool(true), }), // TODO: Hotpatch Server replacer // Log server replacer AbstractReplacer::GenericReplacer(GenericReplacer { regex: regex::Regex::new(CONFIG.replacement_config.log_server_regex).unwrap(), replacement: std::env::var("LOG_SERVER_URL") .unwrap_or(config::LOG_SERVER_DEFAULT.to_string()), force_http: std::env::var("LOG_SERVER_FORCE_HTTP").map_env_bool(true), }), // TODO: SDK Server replacer ]).unwrap() } let module = unsafe { GetModuleHandleA(PCSTR::null()) }.unwrap(); println!("Game base: {:X}", module.0 as usize); let _ = UE_CURL_EASY_SETOPT_FUNC.set(module.0 as usize + CONFIG.ue_curl_config.curl_easy_setopt); interceptor .attach( (module.0 as usize) + CONFIG.ue_curl_config.curl_easy_perform, on_curl_easy_perform, Some(&CONFIG.ue_curl_config as *const _ as usize) ).unwrap(); let krsdk_ex = loop { match unsafe { GetModuleHandleA(CONFIG.disable_sdk.sdk_dll) } { Ok(handle) => break handle, Err(_) => thread::sleep(Duration::from_millis(1)), } }; interceptor .replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.eula_accept, dummy, None) .unwrap(); interceptor .replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.sdk_go_away, dummy, None) .unwrap(); } unsafe extern "win64" fn dummy(_: *mut Registers, _: usize, _: usize) -> usize { 1 } unsafe extern "win64" fn on_curl_easy_perform(reg: *mut Registers, user_data: usize) { let config = std::ptr::read(user_data as *const CurlConfig); let curl_handle = unsafe { *(((*reg).rcx + config.handle_rcx_relative_offset) as *const usize) }; let url_ptr = unsafe { *((curl_handle + config.url_handle_relative_offset) as *const usize) }; let url = unsafe { CStr::from_ptr(url_ptr as *const i8) }.to_str().unwrap(); println!("[curl_easy_perform] Original URL: {url}"); for replacer in unsafe { URL_REPLACER.get_mut().unwrap() } { match replacer.replace(url) { Ok(result) => { println!("[curl_easy_perform] Replacement URL: {result}"); let url = CString::new(result.as_str()).unwrap(); unsafe { // TODO: Rethink this for other curl interceptors std::mem::transmute::(*UE_CURL_EASY_SETOPT_FUNC.get().unwrap())( curl_handle, curl_utils::CURL_OPT_URL, url.as_ptr(), ) } // TODO: Future stuff // let headers_ptr = unsafe { *(((*reg).rcx + 0x118) as *const usize) }; // let headers = unsafe { std::slice::from_raw_parts(*(headers_ptr as *const usize) as *const u8, 1000) }; // println!("Headers: {headers:02X?}"); return; } Err(_) => {} }; }; println!("No valid url match found"); }