Compare commits

8 Commits

Author SHA1 Message Date
amizing25
7ab400507e feat(launcher): warn if the launcher is not run as admin 2025-08-22 06:08:34 +07:00
amizing25
d66398b9b8 fix: fixed client damaged error 2025-08-22 06:04:37 +07:00
amizing25
ee7fa339fb refactor: minor changes
- use VA directly on interceptor instead of RVA
2025-08-22 05:53:53 +07:00
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
14 changed files with 267 additions and 125 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,24 @@ 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 =
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 "E8 ? ? ? ? EB ? 31 C0 48 89 06 48 8B 47 ? 48 89 46 ? F2 0F 10 47";
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 MAKE_INITIAL_URL: &str =
const SET_DISTANCE_DITHER: &str = "E8 ? ? ? ? 49 8B 46 ? 48 85 C0 0F 84 ? ? ? ? 48 8B 4D"; // TODO "E8 ? ? ? ? 48 89 D9 48 89 C2 E8 ? ? ? ? 48 89 D9 4C 89 FA E8 ? ? ? ? 49 89 5D"; // 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: &str = "E8 ? ? ? ? 84 C0 75 ? C7 43";
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 SDK_PUBLIC_KEY_LITERAL: &str =
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"; "48 8B 0D ? ? ? ? 4C 89 E2 E8 ? ? ? ? 48 89 C6 48 8B 0D ? ? ? ? E8 ? ? ? ? 48 89 C7 48 8B 0D";
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 +33,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;
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),
$config.$field stringify!($base),
$config.$field - *$base
); );
} 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 +62,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 +82,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

@@ -1,6 +1,6 @@
use std::{ffi::CString, fmt::Display}; use std::{ffi::CString, fmt::Display};
use crate::addr::{GAME_ASSEMBLY_BASE, rva_config}; use crate::addr::rva_config;
#[repr(transparent)] #[repr(transparent)]
pub struct Il2cppString(usize); pub struct Il2cppString(usize);
@@ -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, rva_config().il2cpp_string_new_len,
) )
}; };
let len = string.len(); let len = string.len();

View File

@@ -2,13 +2,16 @@ use ilhook::x64::{
CallbackOption, HookFlags, HookPoint, HookType, Hooker, JmpBackRoutine, RetnRoutine, CallbackOption, HookFlags, HookPoint, HookType, Hooker, JmpBackRoutine, RetnRoutine,
}; };
#[derive(Default)]
pub struct Interceptor { pub struct Interceptor {
hooks: Vec<HookPoint>, hooks: Vec<HookPoint>,
} }
type Result<T> = std::result::Result<T, ilhook::HookError>; type Result<T> = std::result::Result<T, ilhook::HookError>;
impl Interceptor { impl Interceptor {
pub const fn new() -> Self {
Interceptor { hooks: Vec::new() }
}
pub fn attach(&mut self, addr: usize, routine: JmpBackRoutine) -> Result<()> { pub fn attach(&mut self, addr: usize, routine: JmpBackRoutine) -> Result<()> {
let hooker = Hooker::new( let hooker = Hooker::new(
addr, addr,

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,
misc::Misc, network::Network,
}; };
use windows::{ use windows::{
Win32::System::{Console, LibraryLoader::GetModuleHandleA}, Win32::System::{Console, LibraryLoader::GetModuleHandleA},
@@ -26,11 +27,17 @@ pub fn main() {
thread::sleep(Duration::from_millis(200)); thread::sleep(Duration::from_millis(200));
} }
let mut mm1 = HkrpgModuleManager::default();
mm1.add::<Misc>();
mm1.init()
.expect("[hkrpg::main] failed to initialize module (Misc)");
addr::init_rvas(); addr::init_rvas();
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

@@ -11,7 +11,7 @@ macro_rules! replace {
$( $(
if $config.$field != 0 { if $config.$field != 0 {
$self.interceptor.replace( $self.interceptor.replace(
$self.base.wrapping_add($config.$field), $config.$field,
CensorshipPatch::on_set_dither, CensorshipPatch::on_set_dither,
)?; )?;
} else { } else {
@@ -24,14 +24,7 @@ macro_rules! replace {
impl HkrpgModule for HkrpgModuleContext<CensorshipPatch> { impl HkrpgModule for HkrpgModuleContext<CensorshipPatch> {
unsafe fn init(&mut self) -> Result<(), ilhook::HookError> { unsafe fn init(&mut self) -> Result<(), ilhook::HookError> {
let config = rva_config(); let config = rva_config();
replace!( replace!(self, config, set_dither);
self,
config,
set_distance_dither,
set_elevation_dither,
set_dither_alpha,
set_dither_alpha_anim
);
Ok(()) Ok(())
} }
} }

View File

@@ -0,0 +1,23 @@
use crate::{addr::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 {
*(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

@@ -10,14 +10,11 @@ impl HkrpgModule for HkrpgModuleContext<HkCheck> {
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.hk_check1 != 0 && config.hk_check2 != 0 { if config.hk_check1 != 0 && config.hk_check2 != 0 {
self.interceptor.replace( self.interceptor
self.base.wrapping_add(config.hk_check1), .replace(config.hk_check1, HkCheck::replacement)?;
HkCheck::replacement, self.interceptor
)?; .replace(config.hk_check2, HkCheck::replacement)?;
self.interceptor.replace( println!("[hk_check::init] hk_check bypassed")
self.base.wrapping_add(config.hk_check2),
HkCheck::replacement,
)?;
} }
Ok(()) Ok(())
} }

39
hkrpg/src/modules/misc.rs Normal file
View File

@@ -0,0 +1,39 @@
use crate::modules::{HkrpgModule, HkrpgModuleContext};
use ilhook::x64::Registers;
use std::ffi::CStr;
use windows::{
Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress},
core::s,
};
pub struct Misc;
impl HkrpgModule for HkrpgModuleContext<Misc> {
unsafe fn init(&mut self) -> Result<(), ilhook::HookError> {
unsafe {
let ws32 = GetModuleHandleA(s!("Ws2_32.dll")).unwrap();
let get_addr_info = GetProcAddress(ws32, s!("getaddrinfo")).unwrap();
self.interceptor
.attach(get_addr_info as usize, Misc::on_get_addr_info)?;
println!("[misc::init] initialized")
}
Ok(())
}
}
impl Misc {
pub unsafe extern "win64" fn on_get_addr_info(reg: *mut Registers, _: usize) {
unsafe {
let host = CStr::from_ptr((*reg).rcx as *const i8).to_string_lossy();
if host.contains("globaldp-")
&& (host.contains("bhsr.com") || host.contains("starrails.com"))
{
println!("[*] [on_get_addr_info] {host} -> 0.0.0.0");
std::ptr::copy_nonoverlapping(c"0.0.0.0".as_ptr(), (*reg).rcx as *mut i8, 9);
}
}
}
}

View File

@@ -3,11 +3,13 @@ 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;
pub mod misc;
pub struct HkrpgModuleContext<T> { pub struct HkrpgModuleContext<T> {
base: usize, _base: usize,
interceptor: Interceptor, interceptor: Interceptor,
_module_type: PhantomData<T>, _module_type: PhantomData<T>,
} }
@@ -15,8 +17,8 @@ pub struct HkrpgModuleContext<T> {
impl<T> HkrpgModuleContext<T> { impl<T> HkrpgModuleContext<T> {
fn new(base: usize) -> Self { fn new(base: usize) -> Self {
Self { Self {
base, _base: base,
interceptor: Interceptor::default(), interceptor: Interceptor::new(),
_module_type: PhantomData, _module_type: PhantomData,
} }
} }

View File

@@ -9,11 +9,10 @@ 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
self.base.wrapping_add(config.make_initial_url), .attach(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,78 @@ 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 =
return GAME_ASSEMBLY_BASE.wrapping_add(pointer); 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> { // #[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(|address| {
// let slice = unsafe { unity_player_slice() };
// match slice.get(address) {
// // jmp sub_xxxxxxx
// Some(&0xE8) => {
// let offset =
// i32::from_le_bytes(slice[address + 1..address + 5].try_into().unwrap());
// UNITY_PLAYER_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());
// UNITY_PLAYER_BASE.wrapping_add(address + 7 + offset as usize)
// }
// _ => UNITY_PLAYER_BASE.wrapping_add(address),
// }
// })
// }

View File

@@ -4,16 +4,17 @@ use std::ffi::CString;
use std::ptr::null_mut; use std::ptr::null_mut;
use windows::Win32::Foundation::{CloseHandle, GetLastError, HANDLE}; use windows::Win32::Foundation::{CloseHandle, GetLastError, HANDLE};
use windows::Win32::Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation};
use windows::Win32::System::Diagnostics::Debug::WriteProcessMemory; use windows::Win32::System::Diagnostics::Debug::WriteProcessMemory;
use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}; use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
use windows::Win32::System::Memory::{ use windows::Win32::System::Memory::{
MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE, VirtualAllocEx, VirtualFreeEx, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE, VirtualAllocEx, VirtualFreeEx,
}; };
use windows::Win32::System::Threading::{ use windows::Win32::System::Threading::{
CREATE_SUSPENDED, CreateProcessA, CreateRemoteThread, PROCESS_INFORMATION, ResumeThread, CREATE_SUSPENDED, CreateProcessA, CreateRemoteThread, OpenProcessToken, PROCESS_INFORMATION,
STARTUPINFOA, WaitForSingleObject, ResumeThread, STARTUPINFOA, WaitForSingleObject,
}; };
use windows::core::{PSTR, s}; use windows::core::{Error, PSTR, s};
fn inject_standard(h_target: HANDLE, dll_path: &str) -> bool { fn inject_standard(h_target: HANDLE, dll_path: &str) -> bool {
unsafe { unsafe {
@@ -70,7 +71,48 @@ fn inject_standard(h_target: HANDLE, dll_path: &str) -> bool {
} }
} }
fn is_running_as_admin() -> Result<bool, Error> {
unsafe {
let mut token_handle = HANDLE::default();
let current_process = windows::Win32::System::Threading::GetCurrentProcess();
if OpenProcessToken(current_process, TOKEN_QUERY, &mut token_handle).is_err() {
return Err(windows::core::Error::from_win32());
}
let mut elevation = TOKEN_ELEVATION::default();
let mut size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
let success = GetTokenInformation(
token_handle,
TokenElevation,
Some(&mut elevation as *mut _ as *mut _),
size,
&mut size,
);
let _ = CloseHandle(token_handle);
if success.is_ok() {
Ok(elevation.TokenIsElevated != 0)
} else {
Err(windows::core::Error::from_win32())
}
}
}
fn wait_exit() {
println!("Press any key to exit...");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
}
fn main() { fn main() {
if !is_running_as_admin().unwrap_or_default() {
println!("launcher need to be launched as admin");
wait_exit();
}
let current_dir = std::env::current_dir().unwrap(); let current_dir = std::env::current_dir().unwrap();
let dll_path = current_dir.join("hkrpg.dll"); let dll_path = current_dir.join("hkrpg.dll");
if !dll_path.is_file() { if !dll_path.is_file() {