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
+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,