From bd9ba85687ae282d0bd33f47cddbdc39f9588b85 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 15 Aug 2025 15:29:21 +0800 Subject: [PATCH] Add support to read int password from exe file --- Cargo.lock | 63 +++++++++++++++++++ Cargo.toml | 3 +- src/args.rs | 20 +++++- src/main.rs | 3 +- src/scripts/cat_system/archive/int.rs | 2 + .../cat_system/archive/int_password.rs | 38 +++++++++++ src/scripts/cat_system/archive/mod.rs | 1 + src/utils/blowfish.rs | 11 ++++ src/utils/mod.rs | 3 + 9 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/scripts/cat_system/archive/int_password.rs diff --git a/Cargo.lock b/Cargo.lock index 838f6ba..668e54e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,6 +276,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "dataview" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50eb3a329e19d78c3a3dfa4ec5a51ecb84fa3a20c06edad04be25356018218f9" +dependencies = [ + "derive_pod", +] + +[[package]] +name = "derive_pod" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ea6706d74fca54e15f1d40b5cf7fe7f764aaec61352a9fcec58fe27e042fc8" + [[package]] name = "digest" version = "0.10.7" @@ -818,6 +833,7 @@ dependencies = [ "mozjpeg", "msg_tool_macro", "overf", + "pelite", "png", "rand 0.9.2", "serde", @@ -870,6 +886,12 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "num-traits" version = "0.2.19" @@ -919,6 +941,25 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "pelite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dccf4bd32294364aeb7bd55d749604450e9db54605887551f21baea7617685" +dependencies = [ + "dataview", + "libc", + "no-std-compat", + "pelite-macros", + "winapi", +] + +[[package]] +name = "pelite-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1391,6 +1432,28 @@ dependencies = [ "libwebp-sys", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 40ba1b7..e159753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ memchr = { version = "2.7", optional = true } mozjpeg = { version = "0.10", optional = true } msg_tool_macro = { version = "0.1.6" } overf = "0.1" +pelite = { version = "0.10", optional = true } png = { version = "0.17", optional = true } rand = { version = "0.9", optional = true } serde = { version = "1", features = ["derive"] } @@ -55,7 +56,7 @@ bgi-arc = ["bgi", "rand", "utils-bit-stream"] bgi-audio = ["bgi"] bgi-img = ["bgi", "image", "rand", "utils-bit-stream"] cat-system = ["fancy-regex", "flate2", "int-enum"] -cat-system-arc = ["cat-system", "utils-blowfish", "utils-crc32"] +cat-system-arc = ["cat-system", "pelite", "utils-blowfish", "utils-crc32"] cat-system-img = ["cat-system", "flate2", "image", "mozjpeg", "utils-bit-stream"] circus = [] circus-arc = ["circus"] diff --git a/src/args.rs b/src/args.rs index 99c3d2f..690f057 100644 --- a/src/args.rs +++ b/src/args.rs @@ -55,6 +55,7 @@ fn parse_webp_quality(quality: &str) -> Result { group = ArgGroup::new("ex_hibit_rld_xor_keyg").multiple(false), group = ArgGroup::new("ex_hibit_rld_def_xor_keyg").multiple(false), group = ArgGroup::new("webp_qualityg").multiple(false), + group = ArgGroup::new("cat_system_int_encrypt_passwordg").multiple(false), )] #[command( version, @@ -155,9 +156,13 @@ pub struct Arg { /// When in creation mode, it is not enabled by default. pub bgi_img_scramble: Option, #[cfg(feature = "cat-system-arc")] - #[arg(long, global = true)] + #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")] /// CatSystem2 engine int archive password pub cat_system_int_encrypt_password: Option, + #[cfg(feature = "cat-system-arc")] + #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")] + /// The path to the CatSystem2 engine executable file. Used to get the int archive password. + pub cat_system_int_exe: Option, #[cfg(feature = "cat-system-img")] #[arg(long, global = true, action = ArgAction::SetTrue)] /// Draw CatSystem2 image on canvas (if canvas width and height are specified in file) @@ -469,3 +474,16 @@ pub fn load_ex_hibit_rld_def_xor_key(arg: &crate::args::Arg) -> anyhow::Result anyhow::Result> { + if let Some(exe) = &arg.cat_system_int_exe { + return Ok(Some( + crate::scripts::cat_system::archive::int::get_password_from_exe(exe)?, + )); + } + if let Some(password) = &arg.cat_system_int_encrypt_password { + return Ok(Some(password.clone())); + } + Ok(None) +} diff --git a/src/main.rs b/src/main.rs index a0a33ac..4438447 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1673,7 +1673,8 @@ fn main() { #[cfg(feature = "bgi-img")] bgi_img_scramble: arg.bgi_img_scramble.clone(), #[cfg(feature = "cat-system-arc")] - cat_system_int_encrypt_password: arg.cat_system_int_encrypt_password.clone(), + cat_system_int_encrypt_password: args::get_cat_system_int_encrypt_password(&arg) + .expect("Failed to get CatSystem2 int encrypt password"), #[cfg(feature = "cat-system-img")] cat_system_image_canvas: arg.cat_system_image_canvas, #[cfg(feature = "kirikiri")] diff --git a/src/scripts/cat_system/archive/int.rs b/src/scripts/cat_system/archive/int.rs index e0003dc..59a8ef5 100644 --- a/src/scripts/cat_system/archive/int.rs +++ b/src/scripts/cat_system/archive/int.rs @@ -11,6 +11,8 @@ use overf::wrapping; use std::io::{Read, Seek, SeekFrom}; use std::sync::{Arc, Mutex}; +pub use super::int_password::get_password_from_exe; + #[derive(Debug)] /// Builder for CatSystem2 Archive scripts. pub struct CSIntArcBuilder {} diff --git a/src/scripts/cat_system/archive/int_password.rs b/src/scripts/cat_system/archive/int_password.rs new file mode 100644 index 0000000..c70c704 --- /dev/null +++ b/src/scripts/cat_system/archive/int_password.rs @@ -0,0 +1,38 @@ +//! Get Password from CatSystem2 Executable +use crate::types::*; +use crate::utils::blowfish::{Blowfish, BlowfishLE}; +use crate::utils::encoding::*; +use anyhow::Result; +use pelite::FileMap; +use pelite::pe32::*; +use std::path::Path; + +/// Retrieves the int archive's password from a CatSystem2 executable file. +pub fn get_password_from_exe + ?Sized>(exe_path: &S) -> Result { + let path = exe_path.as_ref(); + let file_map = FileMap::open(path)?; + let file = PeFile::from_bytes(&file_map)?; + let resources = file.resources()?; + let mut code = resources + .find_resource(&["V_CODE2".into(), "DATA".into()])? + .to_vec(); + if code.len() < 8 { + return Err(anyhow::anyhow!("Invalid V_CODE2 resource length")); + } + let key = resources + .find_resource(&["KEY_CODE".into(), "KEY".into()]) + .map(|s| { + let mut s = s.to_vec(); + for i in s.iter_mut() { + *i ^= 0xCD; + } + s + }) + .unwrap_or_else(|_| b"windmill".to_vec()); + let blowfish: BlowfishLE = Blowfish::new(&key)?; + blowfish.decrypt_block(&mut code); + let len = code.iter().position(|&x| x == 0).unwrap_or(code.len()); + let result = decode_to_string(Encoding::Cp932, &code[..len], true)?; + eprintln!("Used password from CatSystem2 executable: {}", result); + Ok(result) +} diff --git a/src/scripts/cat_system/archive/mod.rs b/src/scripts/cat_system/archive/mod.rs index 2d0c07c..8f7de0e 100644 --- a/src/scripts/cat_system/archive/mod.rs +++ b/src/scripts/cat_system/archive/mod.rs @@ -1,3 +1,4 @@ //! CatSystem2 Archive pub mod int; +mod int_password; mod twister; diff --git a/src/utils/blowfish.rs b/src/utils/blowfish.rs index 620ed65..07e0bb9 100644 --- a/src/utils/blowfish.rs +++ b/src/utils/blowfish.rs @@ -269,6 +269,17 @@ impl Blowfish { [r, l] } + /// Decrypts a block of data in-place. + pub fn decrypt_block(&self, buf: &mut [u8]) { + for i in 0..buf.len() / 8 { + let mut l = T::read_u32(&buf[i * 8..]); + let mut r = T::read_u32(&buf[i * 8 + 4..]); + [l, r] = self.decrypt([l, r]); + T::write_u32(&mut buf[i * 8..], l); + T::write_u32(&mut buf[i * 8 + 4..], r); + } + } + /// Creates a new Blowfish cipher instance with the given key. pub fn new(key: &[u8]) -> Result { if key.len() < 4 || key.len() > 56 { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f44c677..20d38f6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -21,3 +21,6 @@ pub mod pcm; #[cfg(feature = "utils-str")] pub mod str; pub mod struct_pack; + +#[cfg(windows)] +pub use encoding_win::WinError;