diff --git a/README.md b/README.md index a62df8e..961ec5f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # hk4e-patch -Genshin Impact encryption patch (4.6.0) \ No newline at end of file +Genshin Impact encryption patch (4.8.50) diff --git a/sdk_public_key.xml b/sdk_public_key.xml new file mode 100644 index 0000000..49b7b3d --- /dev/null +++ b/sdk_public_key.xml @@ -0,0 +1 @@ +AQABhEegnKISgDas5VTuRBUlixB+bvmPvXKa3kVO22UEZjPGMUFLmIl3DhH+dsZo7qJn/GfJCUkP1FA0MJ5Bj8PX8IatLJKIJ9dMCNdnAlkXTlMg86QQAhHZN83vP4swj5ILcrGNKl3YAZ49fvzo7nheuTt0/40f0HkHdNa1dUHECBs= diff --git a/src/lib.rs b/src/lib.rs index a24a2de..243bf47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,27 +12,19 @@ mod interceptor; mod marshal; mod modules; mod util; -mod version; -use version::VersionDllProxy; use crate::modules::{Http, MhyContext, ModuleManager, Security}; unsafe fn thread_func() { - let base = loop { - if let Some(base) = try_get_base_address("UserAssembly.dll") { - break base; - } + let base = try_get_base_address("GenshinImpact.exe").unwrap(); - std::thread::sleep(Duration::from_millis(500)); - }; - - std::thread::sleep(Duration::from_secs(7)); + std::thread::sleep(Duration::from_secs(12)); util::disable_memprotect_guard(); Console::AllocConsole().unwrap(); - println!("Genshin Impact encryption patch\nMade by xeondev\nTo work with NaviaImpact: git.xeondev.com/reversedrooms/NaviaImpact"); - println!("UserAssembly: {:X}", base); + println!("Genshin Impact encryption patch\nMade by xeondev\nTo work with MualaniImpact: git.xeondev.com/reversedrooms/MualaniImpact"); + println!("Base: {:X}", base); let mut module_manager = MODULE_MANAGER.write().unwrap(); module_manager.enable(MhyContext::::new(base)); @@ -42,18 +34,12 @@ unsafe fn thread_func() { } lazy_static! { - static ref VERSION_DLL_PROXY: version::VersionDllProxy = - VersionDllProxy::new().expect("Failed to load version.dll"); static ref MODULE_MANAGER: RwLock = RwLock::new(ModuleManager::default()); } #[no_mangle] unsafe extern "system" fn DllMain(_: HINSTANCE, call_reason: u32, _: *mut ()) -> bool { if call_reason == DLL_PROCESS_ATTACH { - VERSION_DLL_PROXY - .load_functions() - .expect("Failed to load functions from version.dll"); - std::thread::spawn(|| thread_func()); } diff --git a/src/marshal.rs b/src/marshal.rs index b957213..f85aea3 100644 --- a/src/marshal.rs +++ b/src/marshal.rs @@ -2,7 +2,7 @@ use std::ffi::CStr; use crate::util; -const PTR_TO_STRING_ANSI: usize = 0xCF7CE50; +const PTR_TO_STRING_ANSI: usize = 0x104F80F0; type MarshalPtrToStringAnsi = unsafe extern "fastcall" fn(*const u8) -> *const u8; pub unsafe fn ptr_to_string_ansi(content: &CStr) -> *const u8 { @@ -11,5 +11,5 @@ pub unsafe fn ptr_to_string_ansi(content: &CStr) -> *const u8 { } unsafe fn base() -> usize { - util::try_get_base_address("UserAssembly.dll").unwrap() + util::try_get_base_address("GenshinImpact.exe").unwrap() } diff --git a/src/modules/http.rs b/src/modules/http.rs index eca86ab..99ab612 100644 --- a/src/modules/http.rs +++ b/src/modules/http.rs @@ -5,16 +5,20 @@ use crate::marshal; use anyhow::Result; use ilhook::x64::Registers; -const UNITY_WEB_REQUEST_SET_URL: usize = 0xD9442D0; +const WEB_REQUEST_UTILS_MAKE_INITIAL_URL: usize = 0x1119FF40; +const BROWSER_LOAD_URL: usize = 0x10FD8450; pub struct Http; impl MhyModule for MhyContext { unsafe fn init(&mut self) -> Result<()> { self.interceptor.attach( - self.assembly_base + UNITY_WEB_REQUEST_SET_URL, - on_uwr_set_url, - ) + self.assembly_base + WEB_REQUEST_UTILS_MAKE_INITIAL_URL, + on_make_initial_url, + )?; + + self.interceptor + .attach(self.assembly_base + BROWSER_LOAD_URL, on_browser_load_url) } unsafe fn de_init(&mut self) -> Result<()> { @@ -26,7 +30,30 @@ impl MhyModule for MhyContext { } } -unsafe extern "win64" fn on_uwr_set_url(reg: *mut Registers, _: usize) { +unsafe extern "win64" fn on_make_initial_url(reg: *mut Registers, _: usize) { + let str_length = *((*reg).rcx.wrapping_add(16) as *const u32); + let str_ptr = (*reg).rcx.wrapping_add(20) as *const u8; + + let slice = std::slice::from_raw_parts(str_ptr, (str_length * 2) as usize); + let url = String::from_utf16le(slice).unwrap(); + + let mut new_url = if url.contains("/query_region_list") { + String::from("http://127.0.0.1:21041") + } else { + String::from("http://127.0.0.1:21000") + }; + + url.split('/').skip(3).for_each(|s| { + new_url.push_str("/"); + new_url.push_str(s); + }); + + println!("Redirect: {url} -> {new_url}"); + (*reg).rcx = + marshal::ptr_to_string_ansi(CString::new(new_url.as_str()).unwrap().as_c_str()) as u64; +} + +unsafe extern "win64" fn on_browser_load_url(reg: *mut Registers, _: usize) { let str_length = *((*reg).rdx.wrapping_add(16) as *const u32); let str_ptr = (*reg).rdx.wrapping_add(20) as *const u8; @@ -39,7 +66,8 @@ unsafe extern "win64" fn on_uwr_set_url(reg: *mut Registers, _: usize) { new_url.push_str(s); }); - println!("Redirect: {url} -> {new_url}"); + println!("Browser::LoadURL: {url} -> {new_url}"); + (*reg).rdx = marshal::ptr_to_string_ansi(CString::new(new_url.as_str()).unwrap().as_c_str()) as u64; } diff --git a/src/modules/security.rs b/src/modules/security.rs index fe45169..bcd8bbc 100644 --- a/src/modules/security.rs +++ b/src/modules/security.rs @@ -1,25 +1,35 @@ +use std::ffi::CString; + +use crate::marshal; + use super::{MhyContext, MhyModule, ModuleType}; use anyhow::Result; use ilhook::x64::Registers; -const IL2CPP_ARRAY_NEW: usize = 0x553C10; -const KEY_SIGN_CHECK: usize = 0x41C5; +const MHYRSA_PERFORM_CRYPTO_ACTION: usize = 0xC3DEDB; +const KEY_SIGN_CHECK: usize = 0xC4236D; +const SDK_UTIL_RSA_ENCRYPT: usize = 0x10A02A60; -const KEY_SIZE: u64 = 272; -const KEY_PREFIX: u64 = 0x0D700010182020A01; +const KEY_SIZE: usize = 268; static SERVER_PUBLIC_KEY: &[u8] = include_bytes!("../../server_public_key.bin"); -type Il2cppArrayNew = unsafe extern "fastcall" fn(u64, u64) -> *const u8; +static SDK_PUBLIC_KEY: &str = include_str!("../../sdk_public_key.xml"); pub struct Security; impl MhyModule for MhyContext { unsafe fn init(&mut self) -> Result<()> { - self.interceptor.replace( - self.assembly_base + IL2CPP_ARRAY_NEW, - il2cpp_array_new_replacement, + self.interceptor.attach( + self.assembly_base + MHYRSA_PERFORM_CRYPTO_ACTION, + on_mhy_rsa, )?; + self.interceptor - .attach(self.assembly_base + KEY_SIGN_CHECK, after_key_sign_check) + .attach(self.assembly_base + KEY_SIGN_CHECK, after_key_sign_check)?; + + self.interceptor.attach( + self.assembly_base + SDK_UTIL_RSA_ENCRYPT, + on_sdk_util_rsa_encrypt, + ) } unsafe fn de_init(&mut self) -> Result<()> { @@ -31,36 +41,28 @@ impl MhyModule for MhyContext { } } -// Sign check of rsa key that we just replaced. unsafe extern "win64" fn after_key_sign_check(reg: *mut Registers, _: usize) { + println!("key sign check!"); (*reg).rax = 1 } -static mut KEY_PTR: Option<*mut u8> = None; -unsafe extern "win64" fn il2cpp_array_new_replacement( - reg: *mut Registers, - actual_func: usize, - _: usize, -) -> usize { - let il2cpp_array_new = std::mem::transmute::(actual_func); - let ret_val = il2cpp_array_new((*reg).rcx, (*reg).rdx) as usize; +unsafe extern "win64" fn on_mhy_rsa(reg: *mut Registers, _: usize) { + println!("key: {:X}", *((*reg).rdx as *const u64)); + println!("len: {:X}", (*reg).r8); - let rdx = (*reg).rdx; - if rdx == KEY_SIZE { - KEY_PTR = Some(ret_val as *mut u8); - } else { - if let Some(key_ptr) = KEY_PTR { - if *(key_ptr.wrapping_add(32) as *const u64) == KEY_PREFIX { - std::ptr::copy_nonoverlapping( - SERVER_PUBLIC_KEY.as_ptr(), - key_ptr.wrapping_add(32), - SERVER_PUBLIC_KEY.len(), - ); - } - } + if (*reg).r8 as usize == KEY_SIZE { + println!("[*] key replaced"); - KEY_PTR = None; + std::ptr::copy_nonoverlapping( + SERVER_PUBLIC_KEY.as_ptr(), + (*reg).rdx as *mut u8, + SERVER_PUBLIC_KEY.len(), + ); } - - ret_val +} + +unsafe extern "win64" fn on_sdk_util_rsa_encrypt(reg: *mut Registers, _: usize) { + println!("[*] SDK RSA: key replaced"); + (*reg).rcx = + marshal::ptr_to_string_ansi(CString::new(SDK_PUBLIC_KEY).unwrap().as_c_str()) as u64; } diff --git a/src/util.rs b/src/util.rs index 6319997..472ade7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,9 +2,9 @@ use core::iter::once; use std::ffi::{c_void, OsStr}; use std::os::windows::ffi::OsStrExt; -use windows::Win32::System::LibraryLoader::{GetProcAddress, GetModuleHandleW}; +use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetModuleHandleW, GetProcAddress}; use windows::Win32::System::Memory::{PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS, VirtualProtect}; -use windows::core::{PCSTR, PCWSTR}; +use windows::core::{s, PCSTR, PCWSTR}; pub fn wide_str(value: &str) -> Vec { OsStr::new(value).encode_wide().chain(once(0)).collect() @@ -29,7 +29,12 @@ pub unsafe fn disable_memprotect_guard() { PCSTR::from_raw(c"NtProtectVirtualMemory".to_bytes_with_nul().as_ptr()), ) .unwrap(); - let nt_query = GetProcAddress(ntdll, PCSTR::from_raw(c"NtQuerySection".to_bytes_with_nul().as_ptr())).unwrap(); + + let routine = if is_wine() { + GetProcAddress(ntdll, s!("NtPulseEvent")).unwrap() + } else { + GetProcAddress(ntdll, s!("NtQuerySection")).unwrap() + } as *mut u32; let mut old_prot = PAGE_PROTECTION_FLAGS(0); VirtualProtect( @@ -40,7 +45,6 @@ pub unsafe fn disable_memprotect_guard() { ) .unwrap(); - let routine = nt_query as *mut u32; let routine_val = *(routine as *const usize); let lower_bits_mask = !(0xFFu64 << 32); @@ -61,3 +65,8 @@ pub unsafe fn disable_memprotect_guard() { ) .unwrap(); } + +unsafe fn is_wine() -> bool { + let module = GetModuleHandleA(s!("ntdll.dll")).unwrap(); + GetProcAddress(module, s!("wine_get_version")).is_some() +}