diff --git a/Cargo.lock b/Cargo.lock index d483f8b..da46ffc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,32 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "ext" version = "0.1.0" @@ -25,10 +51,17 @@ dependencies = [ "windows", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hkrpg" version = "0.1.0" dependencies = [ + "dashmap", "ilhook", "patternscan", "windows", @@ -77,6 +110,15 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "memchr" version = "2.7.4" @@ -91,6 +133,25 @@ dependencies = [ "windows", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "patternscan" version = "1.2.0" @@ -115,6 +176,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.11.1" @@ -144,6 +214,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "syn" version = "2.0.100" @@ -201,6 +283,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-result" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index fd6e973..82a304d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ windows = { version = "0.54.0", features = [ ] } ilhook = "2.1.1" patternscan = "1.2.0" +dashmap = "6.1.0" # Local crates hkrpg = { path = "hkrpg/" } diff --git a/hkrpg/Cargo.toml b/hkrpg/Cargo.toml index 3b493dd..ee8ddc0 100644 --- a/hkrpg/Cargo.toml +++ b/hkrpg/Cargo.toml @@ -6,4 +6,5 @@ version.workspace = true [dependencies] windows.workspace = true ilhook.workspace = true -patternscan.workspace = true \ No newline at end of file +patternscan.workspace = true +dashmap.workspace = true diff --git a/hkrpg/sdk_public_key.pem b/hkrpg/sdk_public_key.pem new file mode 100644 index 0000000..5c5a6ce --- /dev/null +++ b/hkrpg/sdk_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudOyZJDKXjYQdUS+wN7d +lKBP2XkhkDO/sx6pttn719o+rJ8y8XOO3rftSoRasvw1nzidDQqEnHl1Aw5+a90D +OQvlgBaIda3npqVmi4X2sEzswR7EH5BOJizx7LAxixcoby3jOQathdpl44wQ04XR ++SIkukvxcL1jTepfuc0Pp+DD+4MU5O4/38b9v+ryizU+1CdwzukV27rkMvOtA6ok +Is6Vkb5ubcAC/W3vCIP5gv+QRdYB6ihzLhs5WI/9Ex2PrLpdpxr7gClvEFxkHcLy +LCvSfp9pvt/tnsYsk1tct0HKEr8tlaJ6t1zEGEvFQZRpBsahl/lhpT15vyM8iyiD +DwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/hkrpg/src/lib.rs b/hkrpg/src/lib.rs index 12bd31e..9554897 100644 --- a/hkrpg/src/lib.rs +++ b/hkrpg/src/lib.rs @@ -3,8 +3,8 @@ use std::{thread, time::Duration}; use modules::{ - HkrpgModuleManager, censorship_patch::CensorshipPatch, crypto::Crypto, hk_check::HkCheck, - misc::Misc, network::Network, + HkrpgModuleManager, apn::ApnPatch, censorship_patch::CensorshipPatch, crypto::Crypto, + hk_check::HkCheck, misc::Misc, network::Network, }; use windows::{ Win32::System::{Console, LibraryLoader::GetModuleHandleA}, @@ -35,6 +35,7 @@ pub fn main() { addr::init_rvas(); let mut module_manager = HkrpgModuleManager::default(); + module_manager.add::(); module_manager.add::(); module_manager.add::(); module_manager.add::(); diff --git a/hkrpg/src/modules/apn.rs b/hkrpg/src/modules/apn.rs new file mode 100644 index 0000000..f9ea9dd --- /dev/null +++ b/hkrpg/src/modules/apn.rs @@ -0,0 +1,160 @@ +use dashmap::DashMap; +use ilhook::x64::Registers; +use patternscan::scan_first_match; +use std::{ + borrow::Cow, + ffi::{CStr, CString}, + path::Path, + sync::{LazyLock, OnceLock}, +}; +use windows::{ + Win32::System::{ + LibraryLoader::{GetModuleHandleW, LoadLibraryW}, + ProcessStatus::{GetModuleInformation, MODULEINFO}, + Threading::GetCurrentProcess, + }, + core::{PCWSTR, w}, +}; + +use crate::modules::{HkrpgModule, HkrpgModuleContext}; + +pub struct ApnPatch; + +impl HkrpgModule for HkrpgModuleContext { + unsafe fn init(&mut self) -> Result<(), ilhook::HookError> { + if !load_apn_dll() { + return Ok(()); + }; + + let base; + loop { + if let Ok(module) = unsafe { GetModuleHandleW(w!("AccountPlatNative.dll")) } { + base = module.0 as usize; + println!("AccountPlatNative.dll: 0x{base:X}"); + break; + } + std::thread::sleep(std::time::Duration::from_millis(50)); + } + + let Some(string_alloc_rva) = scan_apn("E8 ? ? ? ? 41 89 BF") else { + println!("[AccountPlatNative] failed to scan string_alloc rva. patch disabled"); + return Ok(()); + }; + + self.interceptor + .attach(base + string_alloc_rva, on_string_alloc) + .expect("Failed to hook AccountPlatNative string alloc"); + + println!("[AccountPlatNative] patch enabled"); + Ok(()) + } +} + +extern "win64" fn on_string_alloc(reg: *mut Registers, _: usize) { + const SDK_PUBLIC_KEY: &str = include_str!("../../sdk_public_key.pem"); + static STRING_CACHE_MAP: LazyLock, CString>> = + LazyLock::new(DashMap::new); + + unsafe { + let content = CStr::from_ptr((*reg).rdx as *const i8).to_string_lossy(); + let size = (*reg).r8; + + if let Some(cstring) = STRING_CACHE_MAP.get(&content) { + (*reg).rdx = cstring.as_ptr() as u64; + (*reg).r8 = cstring.count_bytes() as u64; + return; + } + + // URL string alloc + if content.starts_with("https://") && content.contains(".mihoyo.com") { + let mut new_url = String::from("http://127.0.0.1:21000"); + content.split('/').skip(3).for_each(|s| { + new_url.push('/'); + new_url.push_str(s); + }); + + let cstring = STRING_CACHE_MAP + .entry(content.clone()) + .or_insert_with(|| CString::new(&*new_url).expect("Failed to create CString")); + + (*reg).rdx = cstring.as_ptr() as u64; + (*reg).r8 = new_url.len() as u64; + + println!("[AccountPlatNative] url {content} replaced to {new_url}"); + } + // RSA public key string alloc + else if size == 268 && content.starts_with("-----BEGIN PUBLIC KEY-----") { + let cstring = STRING_CACHE_MAP + .entry(content.clone()) + .or_insert_with(|| CString::new(SDK_PUBLIC_KEY).expect("Failed to create CString")); + + (*reg).rdx = cstring.as_ptr() as u64; + (*reg).r8 = SDK_PUBLIC_KEY.len() as u64; + println!("[AccountPlatNative] sdk public key replaced! "); + } + } +} + +fn load_apn_dll() -> bool { + let dll_path = "./StarRail_Data/Plugins/x86_64/AccountPlatNative.dll"; + + if !Path::new(dll_path).exists() { + return false; + } + + let wide_dll_path: Vec = dll_path.encode_utf16().chain(std::iter::once(0)).collect(); + let ptr = wide_dll_path.as_ptr(); + + if let Err(err) = unsafe { LoadLibraryW(PCWSTR::from_raw(ptr)) } { + println!("[AccountPlatNative] Failed to inject {dll_path}. Error: {err:#?}",); + }; + + true +} + +pub fn scan_apn(pat: &str) -> Option { + let mut slice = apn_slice(); + scan_first_match(&mut slice, pat).unwrap().map(|address| { + let slice = apn_slice(); + match slice.get(address) { + // jmp sub_xxxxxxx + Some(&0xE8) => { + let offset = + i32::from_le_bytes(slice[address + 1..address + 5].try_into().unwrap()); + address + 5 + offset as usize + } + // mov REGISTER, [rip + offset] (0x48 0x8B 0x0D XXXXXXXX) + Some(&0x48) if slice.get(address + 1) == Some(&0x8B) => { + let offset = + i32::from_le_bytes(slice[address + 3..address + 7].try_into().unwrap()); + address + 7 + offset as usize + } + _ => address, + } + }) +} + +fn apn_slice() -> &'static [u8] { + static SLICE: OnceLock<&[u8]> = OnceLock::new(); + unsafe { + SLICE.get_or_init(|| { + let module = GetModuleHandleW(w!("AccountPlatNative.dll")).unwrap(); + let mut module_info = MODULEINFO { + lpBaseOfDll: std::ptr::null_mut(), + SizeOfImage: 0, + EntryPoint: std::ptr::null_mut(), + }; + GetModuleInformation( + GetCurrentProcess(), + module, + &mut module_info, + std::mem::size_of::() as u32, + ) + .unwrap(); + std::slice::from_raw_parts( + module.0 as *const u8, + module_info.SizeOfImage.try_into().unwrap(), + ) + }) + } +} diff --git a/hkrpg/src/modules/mod.rs b/hkrpg/src/modules/mod.rs index 497852a..7b64c19 100644 --- a/hkrpg/src/modules/mod.rs +++ b/hkrpg/src/modules/mod.rs @@ -2,11 +2,12 @@ use std::marker::PhantomData; use crate::{addr, interceptor::Interceptor}; +pub mod apn; pub mod censorship_patch; pub mod crypto; pub mod hk_check; -pub mod network; pub mod misc; +pub mod network; pub struct HkrpgModuleContext { _base: usize,