From baaa507fb2912cc0dacf3b0e7cb482acaa2ce6ed Mon Sep 17 00:00:00 2001 From: progre Date: Mon, 13 May 2024 23:34:49 +0900 Subject: [PATCH] feat: add standalone --- .github/workflows/main.yml | 24 +++++++--- Cargo.lock | 29 +++++------- junowen-lib/src/find_process_id.rs | 2 +- junowen-lib/src/hook_utils.rs | 19 +++++++- junowen-lib/src/lang.rs | 1 + .../src/memory_accessors/hooked_process.rs | 5 ++- junowen/Cargo.toml | 5 +++ junowen/src/bin/junowen-standalone.rs | 45 +++++++++++++++++++ junowen/src/bin/lang/ja.toml | 3 ++ junowen/src/bin/lang/mod.rs | 6 +++ junowen/src/file.rs | 25 +++++------ junowen/src/lib.rs | 41 ++++++++++++++--- th19loader/src/lib.rs | 16 +------ 13 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 junowen/src/bin/junowen-standalone.rs create mode 100644 junowen/src/bin/lang/ja.toml create mode 100644 junowen/src/bin/lang/mod.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a45fc6f..09e88a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,16 +30,24 @@ jobs: - name: Build shell: bash run: | + cargo build --locked --release --package junowen --features simple-dll-injection + mv \ + target/i686-pc-windows-msvc/release/th19_junowen.dll \ + target/i686-pc-windows-msvc/release/junowen.dll + cargo build --locked --release --package junowen --bin junowen-standalone + mv \ + target/i686-pc-windows-msvc/release/junowen-standalone.exe \ + target/i686-pc-windows-msvc/release/junowen.exe cargo build --locked --release --workspace cargo bundle-licenses --format yaml --output THIRDPARTY.yml mkdir --parents artifact/modules mv \ - ./target/i686-pc-windows-msvc/release/th19_*.dll \ - ./artifact/modules/ + target/i686-pc-windows-msvc/release/th19_*.dll \ + artifact/modules/ mv \ - ./target/i686-pc-windows-msvc/release/*.dll \ - ./target/i686-pc-windows-msvc/release/*.exe \ - ./artifact/ + target/i686-pc-windows-msvc/release/*.dll \ + target/i686-pc-windows-msvc/release/*.exe \ + artifact/ cp --recursive LICENSE README.md THIRDPARTY.yml ./artifact/ - uses: actions/upload-artifact@v3 with: @@ -51,6 +59,7 @@ jobs: runs-on: ubuntu-latest env: FILENAME: ${{ github.ref_name }}.zip + FILENAME_STANDALONE: ${{ github.ref_name }}-standalone.zip steps: - uses: actions/download-artifact@v3 with: @@ -60,6 +69,7 @@ jobs: run: | cd artifact zip ../${{ env.FILENAME }} d3d9.dll modules/th19_junowen.dll LICENSE README.md THIRDPARTY.yml + zip ../${{ env.FILENAME_STANDALONE }} junowen.dll junowen.exe LICENSE README.md THIRDPARTY.yml ref_name=${{github.ref_name}} echo "RELEASE_NAME=${ref_name/junowen-/Ju.N.Owen }" >> "$GITHUB_ENV" @@ -68,7 +78,9 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} name: ${{ env.RELEASE_NAME }} - files: ${{ env.FILENAME }} + files: | + ${{ env.FILENAME }} + ${{ env.FILENAME_STANDALONE }} build-server: runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 67016ca..dea71df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1776,9 +1776,11 @@ dependencies = [ "serde", "serde_json", "static_vcruntime", + "sys-locale", "thiserror", "time", "tokio", + "toml 0.8.12", "toml_edit 0.22.12", "tracing", "tracing-appender", @@ -1811,7 +1813,7 @@ dependencies = [ "sys-locale", "thiserror", "tokio", - "toml 0.8.8", + "toml 0.8.12", "tracing", "uuid", "webrtc", @@ -2976,9 +2978,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -3513,14 +3515,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.12", ] [[package]] @@ -3543,19 +3545,6 @@ dependencies = [ "winnow 0.5.15", ] -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.15", -] - [[package]] name = "toml_edit" version = "0.22.12" @@ -3563,6 +3552,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.0.0", + "serde", + "serde_spanned", "toml_datetime", "winnow 0.6.8", ] diff --git a/junowen-lib/src/find_process_id.rs b/junowen-lib/src/find_process_id.rs index 5307ef4..47f25d8 100644 --- a/junowen-lib/src/find_process_id.rs +++ b/junowen-lib/src/find_process_id.rs @@ -40,7 +40,7 @@ pub fn find_process_id(exe_file: &str) -> Result { let snapshot = SafeHandle(unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }?); let process_id = find_process_id_in_snapshot(snapshot, exe_file) - .ok_or_else(|| anyhow!("Process not found"))?; + .ok_or_else(|| anyhow!("process not found"))?; Ok(process_id) } diff --git a/junowen-lib/src/hook_utils.rs b/junowen-lib/src/hook_utils.rs index e74618b..8d60fc3 100644 --- a/junowen-lib/src/hook_utils.rs +++ b/junowen-lib/src/hook_utils.rs @@ -6,12 +6,27 @@ use std::{fs::File, io::Read}; use sha3::digest::Digest; // using for Sha3_224::new() use sha3::{digest::generic_array::GenericArray, Sha3_224}; use windows::{ - core::PCWSTR, - Win32::{Foundation::MAX_PATH, System::LibraryLoader::GetModuleFileNameW}, + core::{HSTRING, PCWSTR}, + Win32::{ + Foundation::{HWND, MAX_PATH}, + System::LibraryLoader::GetModuleFileNameW, + UI::WindowsAndMessaging::{MessageBoxW, MB_ICONWARNING, MB_OK}, + }, }; pub use dll_injection::{do_dll_injection, DllInjectionError}; +pub fn show_warn_dialog(msg: &str) { + unsafe { + MessageBoxW( + HWND::default(), + &HSTRING::from(msg), + &HSTRING::from(env!("CARGO_PKG_NAME")), + MB_ICONWARNING | MB_OK, + ) + }; +} + pub fn calc_th19_hash() -> Vec { let mut buf = [0u16; MAX_PATH as usize]; if unsafe { GetModuleFileNameW(None, &mut buf) } == 0 { diff --git a/junowen-lib/src/lang.rs b/junowen-lib/src/lang.rs index 41a7339..386be71 100644 --- a/junowen-lib/src/lang.rs +++ b/junowen-lib/src/lang.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, fs}; use sys_locale::get_locales; +#[derive(derive_new::new)] pub struct Lang { lang: HashMap, } diff --git a/junowen-lib/src/memory_accessors/hooked_process.rs b/junowen-lib/src/memory_accessors/hooked_process.rs index 80e5c59..0cddcf1 100644 --- a/junowen-lib/src/memory_accessors/hooked_process.rs +++ b/junowen-lib/src/memory_accessors/hooked_process.rs @@ -1,7 +1,7 @@ use std::{ ffi::c_void, mem::{size_of, transmute}, - ptr::{copy_nonoverlapping, replace}, + ptr::{copy_nonoverlapping, read_unaligned, write_unaligned}, }; use anyhow::Result; @@ -55,7 +55,8 @@ unsafe fn assemble_call_and_manage_register(mut addr: *mut u8, target: usize) { unsafe fn assemble_jmp_target(addr: *mut u8, target: usize) -> usize { let jump_base_addr = addr.wrapping_add(5) as i64; let p_jump_target = addr.wrapping_add(1) as *mut i32; - let old_value = replace(p_jump_target, (target as i64 - jump_base_addr) as i32); + let old_value = read_unaligned(p_jump_target); + write_unaligned(p_jump_target, (target as i64 - jump_base_addr) as i32); (jump_base_addr + old_value as i64) as usize } diff --git a/junowen/Cargo.toml b/junowen/Cargo.toml index 118ff69..d7c5489 100644 --- a/junowen/Cargo.toml +++ b/junowen/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true name = "th19_junowen" crate-type = ['cdylib'] +[features] +simple-dll-injection = [] + [build-dependencies] static_vcruntime.workspace = true @@ -25,6 +28,7 @@ reqwest = { version = "0.12.4", features = ["json"] } rmp-serde = "1.1.2" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.108" +sys-locale = "0.3.1" thiserror = "1.0.50" time = { version = "0.3.29", features = [] } tokio = { version = "1.32.0", features = [ @@ -33,6 +37,7 @@ tokio = { version = "1.32.0", features = [ "rt-multi-thread", "time" ] } +toml = "0.8.12" toml_edit = "0.22.12" tracing.workspace = true tracing-appender = "0.2.2" diff --git a/junowen/src/bin/junowen-standalone.rs b/junowen/src/bin/junowen-standalone.rs new file mode 100644 index 0000000..0fc1540 --- /dev/null +++ b/junowen/src/bin/junowen-standalone.rs @@ -0,0 +1,45 @@ +mod lang; + +use std::{env::current_exe, process::ExitCode}; + +use junowen_lib::{hook_utils::do_dll_injection, lang::Lang}; +use sys_locale::get_locales; + +use crate::lang::to_lang_source; + +fn create_lang() -> Lang { + let lang = get_locales() + .flat_map(|tag| { + let primary_lang = tag.split('-').next().unwrap_or(&tag).to_owned(); + [tag, primary_lang] + }) + .filter_map(|tag| to_lang_source(&tag)) + .find_map(|file| toml::from_str(file).ok()) + .unwrap_or_default(); + Lang::new(lang) +} + +fn main() -> ExitCode { + let lang = create_lang(); + + let dll_path = current_exe() + .unwrap() + .as_path() + .parent() + .unwrap() + .join(concat!(env!("CARGO_PKG_NAME"), ".dll")); + if let Err(err) = do_dll_injection("th19.exe", &dll_path) { + lang.print("failed injection into th19.exe"); + println!(": {}", err); + println!(); + lang.println("you can close this window by pressing enter..."); + let _ = std::io::stdin().read_line(&mut String::new()); + return ExitCode::FAILURE; + } + + lang.println("completed injection into th19.exe"); + println!(); + lang.println("you can close this window by pressing enter..."); + let _ = std::io::stdin().read_line(&mut String::new()); + ExitCode::SUCCESS +} diff --git a/junowen/src/bin/lang/ja.toml b/junowen/src/bin/lang/ja.toml new file mode 100644 index 0000000..80fcc0b --- /dev/null +++ b/junowen/src/bin/lang/ja.toml @@ -0,0 +1,3 @@ +"failed injection into th19.exe" = "th19.exe への注入に失敗しました" +"completed injection into th19.exe" = "th19.exe への注入が完了しました" +"you can close this window by pressing enter..." = "エンターキーを押してこのウィンドウを閉じることができます..." diff --git a/junowen/src/bin/lang/mod.rs b/junowen/src/bin/lang/mod.rs new file mode 100644 index 0000000..b068764 --- /dev/null +++ b/junowen/src/bin/lang/mod.rs @@ -0,0 +1,6 @@ +pub fn to_lang_source(tag: &str) -> Option<&'static str> { + match tag { + "ja" => Some(include_str!("ja.toml")), + _ => None, + } +} diff --git a/junowen/src/file.rs b/junowen/src/file.rs index ca55b17..1dfc4bd 100644 --- a/junowen/src/file.rs +++ b/junowen/src/file.rs @@ -18,18 +18,16 @@ use windows::{ }, }; -pub async fn ini_file_path_log_dir_path_log_file_name_old_log_path( - module: HMODULE, -) -> (String, String, String, String) { - let dll_path = { - let mut buf = [0u16; u16::MAX as usize]; - if unsafe { GetModuleFileNameW(module, &mut buf) } == 0 { - panic!(); - } - let dll_path = unsafe { PCWSTR::from_raw(buf.as_ptr()).to_string() }.unwrap(); - PathBuf::from(dll_path) - }; +pub fn to_dll_path(module: HMODULE) -> PathBuf { + let mut buf = [0u16; u16::MAX as usize]; + if unsafe { GetModuleFileNameW(module, &mut buf) } == 0 { + panic!(); + } + let dll_path = unsafe { PCWSTR::from_raw(buf.as_ptr()).to_string() }.unwrap(); + PathBuf::from(dll_path) +} +pub fn to_ini_file_path_log_dir_path_log_file_name(dll_stem: &str) -> (String, String, String) { let module_dir = { let guid = FOLDERID_RoamingAppData; let res = unsafe { SHGetKnownFolderPath(&guid, KNOWN_FOLDER_FLAG(0), HANDLE::default()) }; @@ -37,13 +35,10 @@ pub async fn ini_file_path_log_dir_path_log_file_name_old_log_path( format!("{}/ShanghaiAlice/th19/modules", app_data_dir) }; - let dll_stem = dll_path.file_stem().unwrap().to_string_lossy(); let ini_file_path = format!("{}/{}.ini", module_dir, dll_stem); let log_file_name = format!("{}.log", dll_stem); - let dll_dir_path = dll_path.parent().unwrap().to_string_lossy(); - let old_log_path = format!("{}/{}", dll_dir_path, log_file_name); - (ini_file_path, module_dir, log_file_name, old_log_path) + (ini_file_path, module_dir, log_file_name) } pub async fn move_old_log_to_new_path(old_log_path: &str, module_dir: &str, log_file_name: &str) { diff --git a/junowen/src/lib.rs b/junowen/src/lib.rs index 3f78db9..02b8d35 100644 --- a/junowen/src/lib.rs +++ b/junowen/src/lib.rs @@ -9,7 +9,7 @@ mod tracing_helper; use std::{ffi::c_void, slice}; use junowen_lib::{ - hook_utils::WELL_KNOWN_VERSION_HASHES, + hook_utils::{calc_th19_hash, show_warn_dialog, WELL_KNOWN_VERSION_HASHES}, structs::{others::RenderingText, selection::Selection}, Fn009fa0, Fn011560, Fn0b7d40, Fn0d5ae0, Fn0d6e10, Fn1049e0, Fn10f720, FnOfHookAssembly, Th19, }; @@ -22,7 +22,7 @@ use windows::Win32::{ use crate::{ file::{ - ini_file_path_log_dir_path_log_file_name_old_log_path, move_old_log_to_new_path, + move_old_log_to_new_path, to_dll_path, to_ini_file_path_log_dir_path_log_file_name, SettingsRepo, }, state::State, @@ -129,15 +129,18 @@ fn check_version(hash: &[u8]) -> bool { .any(|&valid_hash| valid_hash == hash) } -async fn init(module: HMODULE) { +async fn init(dll_stem: &str, old_log_dir_path: Option<&str>) { if cfg!(debug_assertions) { let _ = unsafe { AllocConsole() }; std::env::set_var("RUST_BACKTRACE", "1"); } - let (ini_file_path, module_dir, log_file_name, old_log_path) = - ini_file_path_log_dir_path_log_file_name_old_log_path(module).await; + let (ini_file_path, module_dir, log_file_name) = + to_ini_file_path_log_dir_path_log_file_name(dll_stem); tracing_helper::init_tracing(&module_dir, &log_file_name, false); - move_old_log_to_new_path(&old_log_path, &module_dir, &log_file_name).await; + if let Some(old_log_dir_path) = old_log_dir_path { + let old_log_path = format!("{}/{}", old_log_dir_path, log_file_name); + move_old_log_to_new_path(&old_log_path, &module_dir, &log_file_name).await; + }; let mut th19 = Th19::new_hooked_process("th19.exe").unwrap(); @@ -185,10 +188,30 @@ async fn init(module: HMODULE) { apply_hook_13f9d0_0446(th19); } +fn launch_init(dll_stem: &str, old_log_dir_path: Option<&str>) { + TOKIO_RUNTIME.block_on(init(dll_stem, old_log_dir_path)); +} + +fn self_init() -> bool { + let hash = calc_th19_hash(); + let dll_path = to_dll_path(unsafe { MODULE }); + if !check_version(&hash) { + show_warn_dialog(&format!("Hash mismatch: {}", dll_path.to_string_lossy())); + return false; + } + let dll_stem = dll_path.file_stem().unwrap().to_string_lossy().to_string(); + std::thread::spawn(move || launch_init(&dll_stem, None)); + + true +} + #[no_mangle] pub extern "stdcall" fn DllMain(inst_dll: HINSTANCE, reason: u32, _reserved: u32) -> bool { if reason == DLL_PROCESS_ATTACH { unsafe { MODULE = inst_dll.into() }; + if cfg!(feature = "simple-dll-injection") && !self_init() { + return false; + } } true } @@ -205,7 +228,11 @@ pub unsafe extern "C" fn CheckVersion(hash: *const u8, length: usize) -> bool { #[allow(non_snake_case)] #[no_mangle] pub extern "C" fn Initialize(_direct_3d: *const IDirect3D9) -> bool { - TOKIO_RUNTIME.block_on(init(unsafe { MODULE })); + let dll_path = to_dll_path(unsafe { MODULE }); + let dll_stem = dll_path.file_stem().unwrap().to_string_lossy(); + let old_log_dir_path = dll_path.parent().unwrap().to_string_lossy(); + + launch_init(&dll_stem, Some(&old_log_dir_path)); true } diff --git a/th19loader/src/lib.rs b/th19loader/src/lib.rs index 2de999f..87264c7 100644 --- a/th19loader/src/lib.rs +++ b/th19loader/src/lib.rs @@ -1,11 +1,11 @@ use std::{env::current_dir, mem::transmute}; -use junowen_lib::hook_utils::calc_th19_hash; +use junowen_lib::hook_utils::{calc_th19_hash, show_warn_dialog}; use windows::{ core::{s, HSTRING, PCWSTR}, Win32::{ - Foundation::{FreeLibrary, HINSTANCE, HMODULE, HWND, MAX_PATH}, + Foundation::{FreeLibrary, HINSTANCE, HMODULE, MAX_PATH}, Graphics::Direct3D9::IDirect3D9, System::{ Console::AllocConsole, @@ -13,7 +13,6 @@ use windows::{ SystemInformation::GetSystemDirectoryW, SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}, }, - UI::WindowsAndMessaging::{MessageBoxW, MB_ICONWARNING, MB_OK}, }, }; @@ -34,17 +33,6 @@ fn load_library(dll_name: &str) -> HMODULE { dll_instance } -fn show_warn_dialog(msg: &str) { - unsafe { - MessageBoxW( - HWND::default(), - &HSTRING::from(msg), - &HSTRING::from(env!("CARGO_PKG_NAME")), - MB_ICONWARNING | MB_OK, - ) - }; -} - fn hook(direct_3d: *const IDirect3D9) { let hash = calc_th19_hash(); println!("{:x?}", hash);