feat: add AccountPlatNative patch for CN SDK auth
This commit is contained in:
+2
-1
@@ -6,4 +6,5 @@ version.workspace = true
|
||||
[dependencies]
|
||||
windows.workspace = true
|
||||
ilhook.workspace = true
|
||||
patternscan.workspace = true
|
||||
patternscan.workspace = true
|
||||
dashmap.workspace = true
|
||||
|
||||
@@ -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
@@ -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>();
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
Reference in New Issue
Block a user