Compare commits

5 Commits

Author SHA1 Message Date
amizing25
cc2f1865fc fix: update set_dither pattern 2025-05-27 10:54:20 +07:00
amizing25
0c87252394 fix censorship patch 2025-05-23 08:23:35 +07:00
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
11 changed files with 141 additions and 99 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. 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,25 +2,21 @@ use std::sync::{LazyLock, OnceLock};
use windows::{Win32::System::LibraryLoader::GetModuleHandleA, core::s}; 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 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 = "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 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 = "E9 ? ? ? ? 0F 28 74 24 ? 48 83 C4 ? 5B 5F 5E 41 5E 41 5F C3 31 F6"; // TODO const SET_DITHER: &str = "E8 ? ? ? ? 84 C0 75 ? C7 43";
const SET_DISTANCE_DITHER: &str = "E8 ? ? ? ? 49 8B 46 ? 48 85 C0 0F 84 ? ? ? ? 48 8B 4D"; // 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 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 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 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_CHECK2: &str = "55 41 57 41 56 41 55 41 54 56 57 53 48 81 EC B8 02 00 00";
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)] #[derive(Default)]
pub struct RVAConfig { pub struct RVAConfig {
pub ptr_to_string_ansi: usize, pub il2cpp_string_new_len: usize,
pub make_initial_url: usize, pub make_initial_url: usize,
pub set_elevation_dither: usize, pub set_dither: usize,
pub set_distance_dither: usize, pub sdk_public_key: usize,
pub set_dither_alpha: usize,
pub set_dither_alpha_anim: usize,
pub hk_check1: usize, pub hk_check1: usize,
pub hk_check2: usize, pub hk_check2: usize,
} }
@@ -34,22 +30,24 @@ pub fn rva_config() -> &'static mut RVAConfig {
pub static GAME_ASSEMBLY_BASE: LazyLock<usize> = pub static GAME_ASSEMBLY_BASE: LazyLock<usize> =
LazyLock::new(|| unsafe { GetModuleHandleA(s!("GameAssembly.dll")).unwrap().0 as usize }); LazyLock::new(|| unsafe { GetModuleHandleA(s!("GameAssembly.dll")).unwrap().0 as usize });
pub static UNITY_PLAYER_BASE: LazyLock<usize> = // pub static UNITY_PLAYER_BASE: LazyLock<usize> =
LazyLock::new(|| unsafe { GetModuleHandleA(s!("UnityPlayer.dll")).unwrap().0 as usize }); // LazyLock::new(|| unsafe { GetModuleHandleA(s!("UnityPlayer.dll")).unwrap().0 as usize });
macro_rules! set_rva { 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) } { if let Some(addr) = unsafe { $scan_fn($rva_pat) } {
$config.$field = addr - *GAME_ASSEMBLY_BASE; $config.$field = addr - *$base;
println!( println!(
"[hkrpg::addr::set_rva] Found relative address for {} -> 0x{:X}", "[hkrpg::addr::set_rva] Found relative address for {} [{}] -> 0x{:X}",
stringify!($field), stringify!($field),
stringify!($base),
$config.$field $config.$field
); );
} else { } else {
eprintln!( eprintln!(
"[hkrpg::addr::set_rva] Failed to find pattern for {} using {}", "[hkrpg::addr::set_rva] Failed to find pattern for {} [{}] using {}",
stringify!($field), stringify!($field),
stringify!($base),
stringify!($scan_fn) stringify!($scan_fn)
); );
@@ -61,17 +59,19 @@ macro_rules! set_rva {
pub unsafe fn init_rvas() { pub unsafe fn init_rvas() {
let config = rva_config(); let config = rva_config();
// ptr_to_string_ansi // il2cpp_string_new_len
set_rva!( set_rva!(
GAME_ASSEMBLY_BASE,
config, config,
ptr_to_string_ansi, il2cpp_string_new_len,
scan_il2cpp_section, scan_il2cpp_section,
PTR_TO_STRING_ANSI, IL2CPP_STRING_NEW_LEN,
0x0 0x0
); );
// make_initial_url // make_initial_url
set_rva!( set_rva!(
GAME_ASSEMBLY_BASE,
config, config,
make_initial_url, make_initial_url,
scan_il2cpp_section, scan_il2cpp_section,
@@ -79,39 +79,40 @@ pub unsafe fn init_rvas() {
0x0 0x0
); );
// set_elevation_dither // set_dither
set_rva!( set_rva!(
GAME_ASSEMBLY_BASE,
config, config,
set_elevation_dither, set_dither,
scan_il2cpp_section, scan_il2cpp_section,
SET_ELEVATION_DITHER, SET_DITHER,
0x0
);
// set_distance_dither
set_rva!(
config,
set_distance_dither,
scan_il2cpp_section,
SET_DISTANCE_DITHER,
0x0
);
// set_dither_alpha
set_rva!(
config,
set_dither_alpha,
scan_il2cpp_section,
SET_DITHER_ALPHA,
0x0
);
// set_dither_alpha_anim
set_rva!(
config,
set_dither_alpha_anim,
scan_il2cpp_section,
SET_DITHER_ALPHA_ANIM,
0x0 0x0
); );
set_rva!(config, hk_check1, scan_unity_player_section, HK_CHECK1, 0x0); // sdk_public_key_literal
set_rva!(config, hk_check2, scan_unity_player_section, HK_CHECK2, 0x0); 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 { pub fn new(string: &str) -> Self {
let func = unsafe { let func = unsafe {
std::mem::transmute::<usize, fn(*const u8, usize) -> usize>( 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(); let len = string.len();

View File

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

View File

@@ -27,10 +27,7 @@ impl HkrpgModule for HkrpgModuleContext<CensorshipPatch> {
replace!( replace!(
self, self,
config, config,
set_distance_dither, set_dither
set_elevation_dither,
set_dither_alpha,
set_dither_alpha_anim
); );
Ok(()) Ok(())
} }

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), self.base.wrapping_add(config.hk_check2),
HkCheck::replacement, HkCheck::replacement,
)?; )?;
println!("[hk_check::init] hk_check bypassed")
} }
Ok(()) Ok(())
} }

View File

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

View File

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

View File

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