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()
+}