feat: add AccountPlatNative patch for CN SDK auth

This commit is contained in:
amizing25
2026-06-03 18:47:17 +07:00
parent aec2a1e575
commit 1ec60097a6
7 changed files with 265 additions and 4 deletions
Generated
+88
View File
@@ -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"
+1
View File
@@ -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/" }
+2 -1
View File
@@ -6,4 +6,5 @@ version.workspace = true
[dependencies]
windows.workspace = true
ilhook.workspace = true
patternscan.workspace = true
patternscan.workspace = true
dashmap.workspace = true
+9
View File
@@ -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-----
+3 -2
View File
@@ -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::<ApnPatch>();
module_manager.add::<HkCheck>();
module_manager.add::<Network>();
module_manager.add::<Crypto>();
+160
View File
@@ -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<ApnPatch> {
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<DashMap<Cow<'static, str>, 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<u16> = 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<usize> {
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::<MODULEINFO>() as u32,
)
.unwrap();
std::slice::from_raw_parts(
module.0 as *const u8,
module_info.SizeOfImage.try_into().unwrap(),
)
})
}
}
+2 -1
View File
@@ -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<T> {
_base: usize,