Compare commits

..

No commits in common. "master" and "0.4.0" have entirely different histories.

9 changed files with 123 additions and 172 deletions

37
Cargo.lock generated
View file

@ -17,6 +17,14 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "camellya-patch"
version = "0.1.0"
dependencies = [
"ilhook",
"windows",
]
[[package]] [[package]]
name = "iced-x86" name = "iced-x86"
version = "1.21.0" version = "1.21.0"
@ -41,14 +49,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "interceptor-rs"
version = "0.1.0"
source = "git+https://git.xeondev.com/xavo95/interceptor-rs.git#282da6f98b8e4a4e9844422343d4ce11606c9de6"
dependencies = [
"ilhook",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@ -87,9 +87,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -151,23 +151,6 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "wicked-waifus-win-patch"
version = "0.1.0"
dependencies = [
"ilhook",
"interceptor-rs",
"regex",
"widestring",
"windows",
]
[[package]]
name = "widestring"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.58.0" version = "0.58.0"

View file

@ -1,10 +1,10 @@
[package] [package]
name = "wicked-waifus-win-patch" name = "camellya-patch"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[lib] [lib]
name = "wicked_waifus_win" name = "camellya"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[features] [features]
@ -13,14 +13,9 @@ cn_beta_1_3_0 = []
cn_live_1_3_0 = [] cn_live_1_3_0 = []
os_live_1_3_0 = [] os_live_1_3_0 = []
enable-sdk = [] enable-sdk = []
only-sig-bypass = []
regular = ["dep:regex", "dep:widestring"]
[dependencies] [dependencies]
ilhook = "2.1.1" ilhook = "2.1.1"
interceptor-rs = { git = "https://git.xeondev.com/xavo95/interceptor-rs.git" }
regex = {version = "1.11.1", optional = true}
widestring = {version = "1.1.0", optional = true}
windows = { version = "0.58.0", features = [ windows = { version = "0.58.0", features = [
"Win32_Foundation", "Win32_Foundation",
"Win32_System_LibraryLoader", "Win32_System_LibraryLoader",

View file

@ -1,6 +1,6 @@
# wicked-waifus-win-patch # camellya-patch
PAK files signature check bypass for the PC version of a certain game. PAK files signature check bypass for Wuthering Waves 1.4 beta
### How to use ### How to use
Just inject it at early startup. Just inject it at early startup.

View file

@ -4,34 +4,24 @@ GOTO:MAIN
SETLOCAL ENABLEDELAYEDEXPANSION SETLOCAL ENABLEDELAYEDEXPANSION
cargo clean cargo clean
cargo build --release --no-default-features -F %~1 cargo build --release --no-default-features -F %~1
set features=%~1 set features=%~1:,=-%
set cleaned_features=%features:,=-% COPY target\release\camellya.dll build\camellya-%~1.dll
COPY target\release\wicked_waifus_win.dll build\%~2\wicked-waifus-win-%cleaned_features%.dll
cargo clean cargo clean
ENDLOCAL ENDLOCAL
EXIT /B 0 EXIT /B 0
:buildAllVariants
SETLOCAL ENABLEDELAYEDEXPANSION
: Build for cn_beta_1_4_0
call:cargoReleaseBuild "cn_beta_1_4_0,%~1" %~1
: Build for cn_beta_1_3_0
call:cargoReleaseBuild "cn_beta_1_3_0,%~1" %~1
: Build for cn_live_1_3_0
call:cargoReleaseBuild "cn_live_1_3_0,%~1" %~1
: Build for os_live_1_3_0
call:cargoReleaseBuild "os_live_1_3_0,%~1" %~1
ENDLOCAL
EXIT /B 0
:MAIN :MAIN
if exist "build" rd /q /s "build" if exist "build" rd /q /s "build"
mkdir build mkdir build
mkdir build\regular
mkdir build\only-sig-bypass
cargo clean cargo clean
call:buildAllVariants regular : Build for cn_beta_1_4_0
call:buildAllVariants only-sig-bypass call:cargoReleaseBuild cn_beta_1_4_0
: Build for cn_beta_1_3_0
call:cargoReleaseBuild cn_beta_1_3_0
: Build for cn_live_1_3_0
call:cargoReleaseBuild cn_live_1_3_0
: Build for os_live_1_3_0
call:cargoReleaseBuild os_live_1_3_0
tar -acvf wicked-waifus-win-patch-win64.zip -C build . tar -acvf camellya-patch-win64.zip -C build .

View file

@ -1,63 +0,0 @@
#![cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
use std::sync::OnceLock;
use std::thread;
use std::time::Duration;
use ilhook::x64::Registers;
use windows::core::{PCSTR, PCWSTR};
use windows::Win32::System::LibraryLoader::GetModuleHandleA;
use crate::offsets::CONFIG;
use crate::replacer::{GenericReplacer, Replacer};
static CFG_SERVER_REPLACER: OnceLock<GenericReplacer> = OnceLock::new();
pub(crate) fn configure_extras(interceptor: &mut interceptor_rs::Interceptor) {
let module = unsafe { GetModuleHandleA(PCSTR::null()) }.unwrap();
println!("Game base: {:X}", module.0 as usize);
interceptor
.attach((module.0 as usize) + CONFIG.kuro_http_get, on_kurohttp_get)
.unwrap();
let krsdk_ex = loop {
match unsafe { GetModuleHandleA(CONFIG.disable_sdk.sdk_dll) } {
Ok(handle) => break handle,
Err(_) => thread::sleep(Duration::from_millis(1)),
}
};
interceptor
.replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.eula_accept, dummy)
.unwrap();
interceptor
.replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.sdk_go_away, dummy)
.unwrap();
}
unsafe extern "win64" fn on_kurohttp_get(reg: *mut Registers, _: usize) {
let wstr = *((*reg).rcx as *const usize) as *mut u16;
let url = PCWSTR::from_raw(wstr).to_string().unwrap();
println!("HTTP GET: {url}");
let replacer = CFG_SERVER_REPLACER.get_or_init(|| {
GenericReplacer {
regex: regex::Regex::new(r#"^(?:https|http)://.*/([a-zA-Z0-9]{32}/index\.json)$"#).unwrap(),
replacement: std::env::var("CFG_SERVER_URL").unwrap_or("127.0.0.1:10001".to_string()),
scheme: std::env::var("CFG_SERVER_SCHEME").unwrap_or("http".to_string()),
}
});
if let Some(result) = replacer.replace(url.as_str()) {
println!("Redirecting to: {result}");
// TODO: Track https://doc.rust-lang.org/nightly/unstable-book/library-features/str-from-utf16-endian.html to replace widestring when stabilized
let new_url = widestring::U16CString::from_str(result.as_str()).unwrap();
let new_wstr = PCWSTR::from_raw(new_url.as_ptr());
std::ptr::copy_nonoverlapping(new_wstr.as_ptr(), wstr, new_wstr.as_wide().len() + 2);
};
}
unsafe extern "win64" fn dummy(_: *mut Registers, _: usize, _: usize) -> usize {
1
}

50
src/interceptor.rs Normal file
View file

@ -0,0 +1,50 @@
use ilhook::x64::{
CallbackOption, Hooker, HookFlags, HookPoint, HookType, JmpBackRoutine, RetnRoutine,
};
pub struct Interceptor {
pub hooks: Vec<HookPoint>,
}
impl Interceptor {
pub const fn new() -> Self {
Self { hooks: Vec::new() }
}
#[allow(dead_code)]
pub fn attach(
&mut self,
addr: usize,
routine: JmpBackRoutine,
) -> Result<(), ilhook::HookError> {
let hooker = Hooker::new(
addr,
HookType::JmpBack(routine),
CallbackOption::None,
0,
HookFlags::empty(),
);
let hook_point = unsafe { hooker.hook() }?;
self.hooks.push(hook_point);
Ok(())
}
pub fn replace(
&mut self,
addr: usize,
routine: RetnRoutine,
) -> Result<(), ilhook::HookError> {
let hooker = Hooker::new(
addr,
HookType::Retn(routine),
CallbackOption::None,
0,
HookFlags::empty(),
);
let hook_point = unsafe { hooker.hook() }?;
self.hooks.push(hook_point);
Ok(())
}
}

View file

@ -2,20 +2,17 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
use ilhook::x64::Registers; use ilhook::x64::Registers;
use interceptor_rs::Interceptor; use windows::core::{PCSTR, PCWSTR, w};
use windows::core::{PCSTR, PCWSTR};
use windows::Win32::Foundation::HINSTANCE; use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::System::Console; use windows::Win32::System::Console;
use windows::Win32::System::LibraryLoader::GetModuleHandleA; use windows::Win32::System::LibraryLoader::GetModuleHandleA;
use windows::Win32::System::SystemServices::DLL_PROCESS_ATTACH; use windows::Win32::System::SystemServices::DLL_PROCESS_ATTACH;
use interceptor::Interceptor;
use offsets::CONFIG; use offsets::CONFIG;
mod interceptor;
mod offsets; mod offsets;
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
mod replacer;
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
mod extras;
fn thread_func() { fn thread_func() {
unsafe { Console::AllocConsole() }.unwrap(); unsafe { Console::AllocConsole() }.unwrap();
@ -38,14 +35,44 @@ fn thread_func() {
.replace((module.0 as usize) + CONFIG.f_pak_file_check, fpakfile_check_replacement) .replace((module.0 as usize) + CONFIG.f_pak_file_check, fpakfile_check_replacement)
.unwrap(); .unwrap();
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))] let module = unsafe { GetModuleHandleA(PCSTR::null()) }.unwrap();
extras::configure_extras(&mut interceptor); println!("Game base: {:X}", module.0 as usize);
interceptor
.attach((module.0 as usize) + CONFIG.kuro_http_get, on_kurohttp_get)
.unwrap();
let krsdk_ex = loop {
match unsafe { GetModuleHandleA(CONFIG.disable_sdk.sdk_dll) } {
Ok(handle) => break handle,
Err(_) => thread::sleep(Duration::from_millis(1)),
}
};
interceptor
.replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.eula_accept, dummy)
.unwrap();
interceptor
.replace((krsdk_ex.0 as usize) + CONFIG.disable_sdk.sdk_go_away, dummy)
.unwrap();
println!("Successfully initialized!"); println!("Successfully initialized!");
thread::sleep(Duration::from_secs(u64::MAX)); thread::sleep(Duration::from_secs(u64::MAX));
} }
unsafe extern "win64" fn on_kurohttp_get(reg: *mut Registers, _: usize) {
let wstr = *((*reg).rcx as *const usize) as *mut u16;
let url = PCWSTR::from_raw(wstr).to_string().unwrap();
println!("HTTP GET: {url}");
if url.ends_with("/index.json") {
println!("index.json requested, redirecting");
let new_wstr = w!("http://127.0.0.1:10001/index.json");
std::ptr::copy_nonoverlapping(new_wstr.as_ptr(), wstr, new_wstr.as_wide().len() + 2);
}
}
unsafe extern "win64" fn fpakfile_check_replacement( unsafe extern "win64" fn fpakfile_check_replacement(
reg: *mut Registers, reg: *mut Registers,
_: usize, _: usize,
@ -58,6 +85,10 @@ unsafe extern "win64" fn fpakfile_check_replacement(
1 1
} }
unsafe extern "win64" fn dummy(_: *mut Registers, _: usize, _: usize) -> usize {
1
}
#[no_mangle] #[no_mangle]
unsafe extern "system" fn DllMain(_: HINSTANCE, call_reason: u32, _: *mut ()) -> bool { unsafe extern "system" fn DllMain(_: HINSTANCE, call_reason: u32, _: *mut ()) -> bool {
if call_reason == DLL_PROCESS_ATTACH { if call_reason == DLL_PROCESS_ATTACH {

View file

@ -1,7 +1,7 @@
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
use windows::core::{PCSTR, s}; use windows::core::{PCSTR, s};
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
pub(crate) struct DisableSdkConfiguration { pub(crate) struct DisableSdkConfiguration {
pub(crate) sdk_dll: PCSTR, pub(crate) sdk_dll: PCSTR,
pub(crate) eula_accept: usize, pub(crate) eula_accept: usize,
@ -11,9 +11,8 @@ pub(crate) struct DisableSdkConfiguration {
pub(crate) struct InjectConfiguration { pub(crate) struct InjectConfiguration {
pub(crate) f_pak_file_check: usize, pub(crate) f_pak_file_check: usize,
pub(crate) f_pak_file_check_preamble: u64, pub(crate) f_pak_file_check_preamble: u64,
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
pub(crate) kuro_http_get: usize, pub(crate) kuro_http_get: usize,
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
pub(crate) disable_sdk: DisableSdkConfiguration, pub(crate) disable_sdk: DisableSdkConfiguration,
} }
@ -21,9 +20,8 @@ pub(crate) struct InjectConfiguration {
pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration { pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
f_pak_file_check: 0x3E37D90, // 0x3E37D90 f_pak_file_check: 0x3E37D90, // 0x3E37D90
f_pak_file_check_preamble: 0x8148574157565340, f_pak_file_check_preamble: 0x8148574157565340,
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
kuro_http_get: 0xFE9E00, kuro_http_get: 0xFE9E00,
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
disable_sdk: DisableSdkConfiguration{ disable_sdk: DisableSdkConfiguration{
sdk_dll: s!("KRSDKEx.dll"), sdk_dll: s!("KRSDKEx.dll"),
eula_accept: 0x4A6D0, eula_accept: 0x4A6D0,
@ -35,9 +33,8 @@ pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration { pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
f_pak_file_check: 0x3D2F460, f_pak_file_check: 0x3D2F460,
f_pak_file_check_preamble: 0x8148574157565340, f_pak_file_check_preamble: 0x8148574157565340,
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
kuro_http_get: 0xFC8CF0, kuro_http_get: 0xFC8CF0,
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
disable_sdk: DisableSdkConfiguration{ disable_sdk: DisableSdkConfiguration{
sdk_dll: s!("KRSDKEx.dll"), sdk_dll: s!("KRSDKEx.dll"),
eula_accept: 0x4A690, eula_accept: 0x4A690,
@ -49,9 +46,8 @@ pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration { pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
f_pak_file_check: 0x3D35DF0, f_pak_file_check: 0x3D35DF0,
f_pak_file_check_preamble: 0x8148574157565340, f_pak_file_check_preamble: 0x8148574157565340,
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
kuro_http_get: 0xFC9900, kuro_http_get: 0xFC9900,
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
disable_sdk: DisableSdkConfiguration{ disable_sdk: DisableSdkConfiguration{
sdk_dll: s!("KRSDKEx.dll"), sdk_dll: s!("KRSDKEx.dll"),
eula_accept: 0x4A690, eula_accept: 0x4A690,
@ -63,12 +59,11 @@ pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration { pub(crate) const CONFIG: InjectConfiguration = InjectConfiguration {
f_pak_file_check: 0x3CDC430, f_pak_file_check: 0x3CDC430,
f_pak_file_check_preamble: 0x8148574157565340, f_pak_file_check_preamble: 0x8148574157565340,
#[cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
kuro_http_get: 0xFC6C20, kuro_http_get: 0xFC6C20,
#[cfg(all(not(feature = "enable-sdk"), not(feature = "only-sig-bypass"), feature = "regular"))] #[cfg(not(feature = "enable-sdk"))]
disable_sdk: DisableSdkConfiguration{ disable_sdk: DisableSdkConfiguration{
sdk_dll: s!("KRSDK.dll"), sdk_dll: s!("KRSDK.dll"),
eula_accept: 0x95440, eula_accept: 0x94710,
sdk_go_away: 0xA1280 sdk_go_away: 0x9FE10
} }
}; };

View file

@ -1,30 +0,0 @@
#![cfg(all(not(feature = "only-sig-bypass"), feature = "regular"))]
pub(crate) trait Replacer {
fn replace(&self, original: &str) -> Option<String>;
}
pub(crate) struct GenericReplacer {
pub(crate) regex: regex::Regex,
pub(crate) replacement: String,
pub(crate) scheme: String,
}
impl Replacer for GenericReplacer {
fn replace(&self, original: &str) -> Option<String> {
// Prepare output array
let mut results: Vec<String> = vec![];
// Perform the capture over input
for (_, [path]) in self.regex.captures_iter(original).map(|c| c.extract()) {
results.push(format!("{}://{}/{}", self.scheme, self.replacement, path));
}
// We are supposed to only parse one entry from text
if 1 == results.len() {
return Some(results.remove(0));
} else if results.is_empty() {
println!("No valid url match found so returning original url");
} else {
println!("Invalid number of entries parsed, expected 1, obtained {:?}", results.len());
}
None
}
}