3 Commits

Author SHA1 Message Date
amizing25
61928b9269 feat: Update patterns 2025-05-22 18:42:02 +07:00
amizing25
3f6933c096 feat: add RSA patch for AccountRSAKey 2025-03-16 15:14:45 +07:00
amizing25
bc651397e6 fix: remove hkcheck as it's not needed (yet?) 2025-03-15 05:48:53 +07:00
10 changed files with 141 additions and 63 deletions

View File

@@ -1,4 +1,7 @@
redirect HTTP requests & remove censorship!
features:
- redirect HTTP requests
- remove censorship
- replace AccountRSAKey into custom public key (by default, compatible with [hoyo-sdk by xeondev](https://git.xeondev.com/reversedrooms/hoyo-sdk))
currently, this has only been tested on CNBETAWin3.1.53 and may require an update for future versions.

1
hkrpg/sdk_public_key.xml Normal file
View File

@@ -0,0 +1 @@
<RSAKeyValue><Exponent>AQAB</Exponent><Modulus>hEegnKISgDas5VTuRBUlixB+bvmPvXKa3kVO22UEZjPGMUFLmIl3DhH+dsZo7qJn/GfJCUkP1FA0MJ5Bj8PX8IatLJKIJ9dMCNdnAlkXTlMg86QQAhHZN83vP4swj5ILcrGNKl3YAZ49fvzo7nheuTt0/40f0HkHdNa1dUHECBs=</Modulus></RSAKeyValue>

View File

@@ -2,20 +2,21 @@ use std::sync::{LazyLock, OnceLock};
use windows::{Win32::System::LibraryLoader::GetModuleHandleA, core::s};
use crate::util::{scan_il2cpp_section, scan_unity_player_section};
use crate::util::scan_il2cpp_section;
const PTR_TO_STRING_ANSI: &str = "E8 ? ? ? ? 48 ? ? 48 85 C0 75 ? 48 8D 4C 24";
const MAKE_INITIAL_URL: &str = "55 41 56 56 57 53 48 83 EC ? 48 8D 6C 24 ? 48 C7 45 ? ? ? ? ? 48 89 D6 48 89 CF E8 ? ? ? ? 84 C0"; // TODO
const SET_ELEVATION_DITHER: &str = "E9 ? ? ? ? 0F 28 74 24 ? 48 83 C4 ? 5B 5F 5E 41 5E 41 5F C3 31 F6"; // TODO
const IL2CPP_STRING_NEW_LEN: &str = "E8 ? ? ? ? EB ? 31 C0 48 89 06 48 8B 47 ? 48 89 46 ? F2 0F 10 47";
const MAKE_INITIAL_URL: &str = "E8 ? ? ? ? 48 89 D9 48 89 C2 E8 ? ? ? ? 48 89 D9 4C 89 FA E8 ? ? ? ? 49 89 5D"; // TODO
const SET_ELEVATION_DITHER: &str = "56 48 83 EC ? 0F 29 74 24 ? 0F 28 F1 48 89 CE 80 3D ? ? ? ? ? 75 ? 80 7E ? ? 74 ? 0F 57 C0 F3 0F 5F C6 F3 0F 10 0D ? ? ? ? F3 0F 5D C8 F3 0F 11 4E ? F3 0F 59 4E ? 48 89 F1 41 B8 ? ? ? ? E8 ? ? ? ? 84 C0 75 ? C7 46 ? ? ? ? ? 0F 28 74 24 ? 48 83 C4 ? 5E C3 B9 ? ? ? ? E8 ? ? ? ? 48 85 C0 74 ? 48 89 C1 48 89 F2 0F 28 D6 0F 28 74 24 ? 48 83 C4 ? 5E E9 ? ? ? ? E8 ? ? ? ? CC 0F 1F 00 56 57 53"; // TODO
const SET_DISTANCE_DITHER: &str = "E8 ? ? ? ? 49 8B 46 ? 48 85 C0 0F 84 ? ? ? ? 48 8B 4D"; // TODO
const SET_DITHER_ALPHA: &str = "56 57 48 83 EC ? 0F 29 74 24 ? 44 89 C6 0F 28 F1 48 89 CF 80 3D ? ? ? ? ? 75 ? 80 7F"; // TODO
const SET_DITHER_ALPHA_ANIM: &str = "56 57 55 53 48 83 EC ? 44 0F 29 44 24 ? 0F 29 7C 24 ? 0F 29 74 24 ? 44 0F 28 C3 0F 28 F2 0F 28 F9"; // TODO
const HK_CHECK1: &str = "55 41 56 56 57 53 48 81 EC 00 01 00 00 48 8D AC 24 80 00 00 00 C7 45 7C 00 00 00 00";
const HK_CHECK2: &str = "55 41 57 41 56 41 55 41 54 56 57 53 48 81 EC B8 02 00 00";
const SET_DITHER_ALPHA: &str = "E8 ? ? ? ? 0F 28 74 24 ? 48 83 C4 ? 5B 5F 5E C3 B9 ? ? ? ? E8 ? ? ? ? 48 85 C0 0F 84 ? ? ? ? 48 89 C1 48 89 F2 E8"; // TODO
const SET_DITHER_ALPHA_ANIM: &str = "E8 ? ? ? ? 8B 46 ? 0F 57 C0 0F 2E C6 0F 82"; // TODO
const SDK_PUBLIC_KEY_LITERAL: &str = "48 8B 0D ? ? ? ? 4C 89 E2 E8 ? ? ? ? 48 89 C6 48 8B 0D ? ? ? ? E8 ? ? ? ? 48 89 C7 48 8B 0D";
// const HK_CHECK1: &str = "55 41 56 56 57 53 48 81 EC 00 01 00 00 48 8D AC 24 80 00 00 00 C7 45 7C 00 00 00 00";
// const HK_CHECK2: &str = "55 41 57 41 56 41 55 41 54 56 57 53 48 81 EC B8 02 00 00";
#[derive(Default)]
pub struct RVAConfig {
pub ptr_to_string_ansi: usize,
pub il2cpp_string_new_len: usize,
pub make_initial_url: usize,
pub set_elevation_dither: usize,
pub set_distance_dither: usize,
@@ -23,6 +24,7 @@ pub struct RVAConfig {
pub set_dither_alpha_anim: usize,
pub hk_check1: usize,
pub hk_check2: usize,
pub sdk_public_key: usize,
}
#[allow(static_mut_refs)]
@@ -34,22 +36,24 @@ pub fn rva_config() -> &'static mut RVAConfig {
pub static GAME_ASSEMBLY_BASE: LazyLock<usize> =
LazyLock::new(|| unsafe { GetModuleHandleA(s!("GameAssembly.dll")).unwrap().0 as usize });
pub static UNITY_PLAYER_BASE: LazyLock<usize> =
LazyLock::new(|| unsafe { GetModuleHandleA(s!("UnityPlayer.dll")).unwrap().0 as usize });
// pub static UNITY_PLAYER_BASE: LazyLock<usize> =
// LazyLock::new(|| unsafe { GetModuleHandleA(s!("UnityPlayer.dll")).unwrap().0 as usize });
macro_rules! set_rva {
($config:ident, $field:ident, $scan_fn:ident, $rva_pat:expr, $fallback:expr) => {
($base:ident, $config:ident, $field:ident, $scan_fn:ident, $rva_pat:expr, $fallback:expr) => {
if let Some(addr) = unsafe { $scan_fn($rva_pat) } {
$config.$field = addr - *GAME_ASSEMBLY_BASE;
$config.$field = addr - *$base;
println!(
"[hkrpg::addr::set_rva] Found relative address for {} -> 0x{:X}",
"[hkrpg::addr::set_rva] Found relative address for {} [{}] -> 0x{:X}",
stringify!($field),
stringify!($base),
$config.$field
);
} else {
eprintln!(
"[hkrpg::addr::set_rva] Failed to find pattern for {} using {}",
"[hkrpg::addr::set_rva] Failed to find pattern for {} [{}] using {}",
stringify!($field),
stringify!($base),
stringify!($scan_fn)
);
@@ -61,17 +65,19 @@ macro_rules! set_rva {
pub unsafe fn init_rvas() {
let config = rva_config();
// ptr_to_string_ansi
// il2cpp_string_new_len
set_rva!(
GAME_ASSEMBLY_BASE,
config,
ptr_to_string_ansi,
il2cpp_string_new_len,
scan_il2cpp_section,
PTR_TO_STRING_ANSI,
IL2CPP_STRING_NEW_LEN,
0x0
);
// make_initial_url
set_rva!(
GAME_ASSEMBLY_BASE,
config,
make_initial_url,
scan_il2cpp_section,
@@ -81,6 +87,7 @@ pub unsafe fn init_rvas() {
// set_elevation_dither
set_rva!(
GAME_ASSEMBLY_BASE,
config,
set_elevation_dither,
scan_il2cpp_section,
@@ -89,6 +96,7 @@ pub unsafe fn init_rvas() {
);
// set_distance_dither
set_rva!(
GAME_ASSEMBLY_BASE,
config,
set_distance_dither,
scan_il2cpp_section,
@@ -97,6 +105,7 @@ pub unsafe fn init_rvas() {
);
// set_dither_alpha
set_rva!(
GAME_ASSEMBLY_BASE,
config,
set_dither_alpha,
scan_il2cpp_section,
@@ -105,6 +114,7 @@ pub unsafe fn init_rvas() {
);
// set_dither_alpha_anim
set_rva!(
GAME_ASSEMBLY_BASE,
config,
set_dither_alpha_anim,
scan_il2cpp_section,
@@ -112,6 +122,30 @@ pub unsafe fn init_rvas() {
0x0
);
set_rva!(config, hk_check1, scan_unity_player_section, HK_CHECK1, 0x0);
set_rva!(config, hk_check2, scan_unity_player_section, HK_CHECK2, 0x0);
// sdk_public_key_literal
set_rva!(
GAME_ASSEMBLY_BASE,
config,
sdk_public_key,
scan_il2cpp_section,
SDK_PUBLIC_KEY_LITERAL,
0x0
)
// set_rva!(
// UNITY_PLAYER_BASE,
// config,
// hk_check1,
// scan_unity_player_section,
// HK_CHECK1,
// 0x0
// );
// set_rva!(
// UNITY_PLAYER_BASE,
// config,
// hk_check2,
// scan_unity_player_section,
// HK_CHECK2,
// 0x0
// );
}

View File

@@ -15,7 +15,7 @@ impl Il2cppString {
pub fn new(string: &str) -> Self {
let func = unsafe {
std::mem::transmute::<usize, fn(*const u8, usize) -> usize>(
*GAME_ASSEMBLY_BASE + rva_config().ptr_to_string_ansi,
*GAME_ASSEMBLY_BASE + rva_config().il2cpp_string_new_len,
)
};
let len = string.len();

View File

@@ -3,7 +3,8 @@
use std::{thread, time::Duration};
use modules::{
HkrpgModuleManager, censorship_patch::CensorshipPatch, hk_check::HkCheck, network::Network,
HkrpgModuleManager, censorship_patch::CensorshipPatch, crypto::Crypto, hk_check::HkCheck,
network::Network,
};
use windows::{
Win32::System::{Console, LibraryLoader::GetModuleHandleA},
@@ -31,6 +32,7 @@ pub fn main() {
let mut module_manager = HkrpgModuleManager::default();
module_manager.add::<HkCheck>();
module_manager.add::<Network>();
module_manager.add::<Crypto>();
module_manager.add::<CensorshipPatch>();
module_manager
.init()

View File

@@ -0,0 +1,26 @@
use crate::{
addr::{GAME_ASSEMBLY_BASE, rva_config},
il2cpp_string::Il2cppString,
};
use super::{HkrpgModule, HkrpgModuleContext};
pub struct Crypto;
const ACCOUNT_RSA_KEY_REPLACEMENT: &str = include_str!("../../sdk_public_key.xml");
impl HkrpgModule for HkrpgModuleContext<Crypto> {
unsafe fn init(&mut self) -> Result<(), ilhook::HookError> {
let config = rva_config();
if config.sdk_public_key != 0 {
unsafe {
*(GAME_ASSEMBLY_BASE.wrapping_add(config.sdk_public_key) as *mut Il2cppString) =
Il2cppString::new(ACCOUNT_RSA_KEY_REPLACEMENT)
}
println!("[crypto::init] AccountRSAKey replaced")
} else {
println!("[crypto::init] pattern is outdated! disabling AccountRSAKey replacement")
}
Ok(())
}
}

View File

@@ -18,6 +18,7 @@ impl HkrpgModule for HkrpgModuleContext<HkCheck> {
self.base.wrapping_add(config.hk_check2),
HkCheck::replacement,
)?;
println!("[hk_check::init] hk_check bypassed")
}
Ok(())
}

View File

@@ -3,6 +3,7 @@ use std::marker::PhantomData;
use crate::{addr, interceptor::Interceptor};
pub mod censorship_patch;
pub mod crypto;
pub mod hk_check;
pub mod network;

View File

@@ -9,11 +9,12 @@ pub struct Network;
impl HkrpgModule for HkrpgModuleContext<Network> {
unsafe fn init(&mut self) -> Result<(), ilhook::HookError> {
let config = rva_config();
if config.make_initial_url != 0 && config.ptr_to_string_ansi != 0 {
if config.make_initial_url != 0 && config.il2cpp_string_new_len != 0 {
self.interceptor.attach(
self.base.wrapping_add(config.make_initial_url),
Network::on_make_initial_url,
)?;
println!("[network::init] network patch enabled")
} else {
println!("[network::init] pattern is outdated! disabling http redirection")
}

View File

@@ -10,7 +10,7 @@ use windows::{
core::s,
};
use crate::addr::{GAME_ASSEMBLY_BASE, UNITY_PLAYER_BASE};
use crate::addr::GAME_ASSEMBLY_BASE;
#[allow(static_mut_refs)]
unsafe fn game_assembly_slice() -> &'static [u8] {
@@ -38,49 +38,58 @@ unsafe fn game_assembly_slice() -> &'static [u8] {
}
}
#[allow(static_mut_refs)]
unsafe fn unity_player_slice() -> &'static [u8] {
static mut SLICE: OnceCell<&[u8]> = OnceCell::new();
unsafe {
SLICE.get_or_init(|| {
let module = GetModuleHandleA(s!("UnityPlayer.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(),
)
})
}
}
pub unsafe fn scan_il2cpp_section(pat: &str) -> Option<usize> {
let mut slice = unsafe { game_assembly_slice() };
scan_first_match(&mut slice, pat).unwrap().map(|address| {
let slice = unsafe { game_assembly_slice() };
let instruction = &slice[address..address + 1][0];
if *instruction == 0xE8 {
let offset = i32::from_le_bytes((&slice[address + 1..address + 1 + 4]).try_into().unwrap());
let pointer = offset as usize + 5 + address;
return GAME_ASSEMBLY_BASE.wrapping_add(pointer);
match slice.get(address) {
// jmp sub_xxxxxxx
Some(&0xE8) => {
let offset = i32::from_le_bytes(slice[address + 1..address + 5].try_into().unwrap());
GAME_ASSEMBLY_BASE.wrapping_add(address + 5 + offset as usize)
}
// mov rcx, [rip + offset] (0x48 0x8B 0x0D XXXXXXXX)
Some(&0x48)
if slice.get(address + 1) == Some(&0x8B)
&& slice.get(address + 2) == Some(&0x0D) =>
{
let offset = i32::from_le_bytes(slice[address + 3..address + 7].try_into().unwrap());
GAME_ASSEMBLY_BASE.wrapping_add(address + 7 + offset as usize)
}
_ => GAME_ASSEMBLY_BASE.wrapping_add(address),
}
GAME_ASSEMBLY_BASE.wrapping_add(address)
})
}
pub unsafe fn scan_unity_player_section(pat: &str) -> Option<usize> {
let mut slice = unsafe { unity_player_slice() };
scan_first_match(&mut slice, pat)
.unwrap()
.map(|loc| UNITY_PLAYER_BASE.wrapping_add(loc))
}
// #[allow(static_mut_refs)]
// unsafe fn unity_player_slice() -> &'static [u8] {
// static mut SLICE: OnceCell<&[u8]> = OnceCell::new();
// unsafe {
// SLICE.get_or_init(|| {
// let module = GetModuleHandleA(s!("UnityPlayer.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(),
// )
// })
// }
// }
// pub unsafe fn scan_unity_player_section(pat: &str) -> Option<usize> {
// let mut slice = unsafe { unity_player_slice() };
// scan_first_match(&mut slice, pat)
// .unwrap()
// .map(|loc| UNITY_PLAYER_BASE.wrapping_add(loc))
// }