From 89c38ccf4713e45f26824564e30e4436c237276c Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 1 Nov 2023 23:33:53 -0500 Subject: [PATCH] Migrate to patternsleuth image scanning --- Cargo.lock | 88 ++++++++++++++++++++++++++++++-- hook/Cargo.toml | 5 +- hook/src/lib.rs | 130 +++++++++++++++++++++++++----------------------- 3 files changed, 153 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37cfb5e8..8e69f99a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2114,7 +2114,7 @@ name = "hook" version = "0.1.0" dependencies = [ "anyhow", - "patternsleuth_scanner", + "patternsleuth", "retour", "windows 0.52.0", ] @@ -2946,7 +2946,9 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ + "flate2", "memchr", + "ruzstd", ] [[package]] @@ -3089,10 +3091,25 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "patternsleuth" +version = "0.1.0" +source = "git+https://github.com/trumank/patternsleuth#e6ec49d1b0b140462a3e0355fca1a921ed44d096" +dependencies = [ + "anyhow", + "libc", + "memchr", + "object", + "patternsleuth_scanner", + "rayon", + "strum 0.25.0", + "windows 0.48.0", +] + [[package]] name = "patternsleuth_scanner" version = "0.1.0" -source = "git+https://github.com/trumank/patternsleuth#a84c3ac3d657188daad28c7daf8e2d61e79c793d" +source = "git+https://github.com/trumank/patternsleuth#e6ec49d1b0b140462a3e0355fca1a921ed44d096" dependencies = [ "anyhow", "memchr", @@ -3512,7 +3529,7 @@ dependencies = [ "libloading 0.7.4", "once_cell", "sha1", - "strum", + "strum 0.24.1", "thiserror", "ureq", "zstd", @@ -3749,6 +3766,17 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "ruzstd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" +dependencies = [ + "byteorder", + "thiserror-core", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.15" @@ -4159,7 +4187,16 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", ] [[package]] @@ -4175,6 +4212,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.39", +] + [[package]] name = "subtle" version = "2.5.0" @@ -4286,6 +4336,26 @@ dependencies = [ "thiserror-impl", ] +[[package]] +name = "thiserror-core" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" +dependencies = [ + "thiserror-core-impl", +] + +[[package]] +name = "thiserror-core-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "thiserror-impl" version = "1.0.50" @@ -4575,6 +4645,16 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/hook/Cargo.toml b/hook/Cargo.toml index 580d41ef..8e715ba2 100644 --- a/hook/Cargo.toml +++ b/hook/Cargo.toml @@ -9,15 +9,12 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1.0.75" -patternsleuth_scanner = { git = "https://github.com/trumank/patternsleuth", version = "0.1.0" } +patternsleuth = { git = "https://github.com/trumank/patternsleuth", features = ["process-internal"] } retour = { version = "0.3.1", features = ["static-detour"] } windows = { version = "0.52.0", features = [ "Win32_Foundation", "Win32_System_SystemServices", - "Win32_UI_WindowsAndMessaging", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Threading", - "Win32_Security", - "Win32_System_ProcessStatus", ] } diff --git a/hook/src/lib.rs b/hook/src/lib.rs index b39969cc..ab1d7c3a 100644 --- a/hook/src/lib.rs +++ b/hook/src/lib.rs @@ -4,15 +4,13 @@ use std::{ }; use anyhow::{bail, Context, Result}; +use patternsleuth::{resolvers::resolve_self, scanner::Pattern, PatternConfig}; use retour::static_detour; use windows::Win32::{ Foundation::HMODULE, System::{ - LibraryLoader::GetModuleHandleA, Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS}, - ProcessStatus::{GetModuleInformation, MODULEINFO}, SystemServices::*, - Threading::GetCurrentProcess, Threading::{GetCurrentThread, QueueUserAPC}, }, }; @@ -91,25 +89,7 @@ unsafe fn patch() -> Result<()> { let installation_type = DRGInstallationType::from_exe_path()?; - let module = GetModuleHandleA(None).context("could not find main module")?; - let process = GetCurrentProcess(); - - let mut mod_info = MODULEINFO::default(); - GetModuleInformation( - process, - module, - &mut mod_info as *mut _, - std::mem::size_of::() as u32, - )?; - - let module_addr = mod_info.lpBaseOfDll; - - let memory = std::slice::from_raw_parts_mut( - mod_info.lpBaseOfDll as *mut u8, - mod_info.SizeOfImage as usize, - ); - - #[derive(Debug, Clone, Copy, Eq, PartialEq)] + #[derive(Debug, PartialEq)] enum Sig { GetServerName, Disable, @@ -120,32 +100,55 @@ unsafe fn patch() -> Result<()> { } let patterns = [ - (Sig::GetServerName, "48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 45 33 FF 4C 8B F2 48 8B D9 44 89 7C 24 50 41 8B FF"), - (Sig::Disable, "4C 8B B4 24 48 01 00 00 0F 84"), - (Sig::SaveGameToSlot, "48 89 5c 24 08 48 89 74 24 10 57 48 83 ec 40 48 8b da 33 f6 48 8d 54 24 30 48 89 74 24 30 48 89 74 24 38 41 8b f8"), - (Sig::LoadGameFromMemory, "40 55 48 8d ac 24 00 ff ff ff 48 81 ec 00 02 00 00 83 79 08 00"), - (Sig::LoadGameFromSlot, "48 8b c4 55 57 48 8d a8 d8 fe ff ff 48 81 ec 18 02 00 00"), - (Sig::DoesSaveGameExist, "48 89 5C 24 08 57 48 83 EC 20 8B FA 48 8B D9 E8 ?? ?? ?? ?? 48 8B C8 4C 8B 00 41 FF 50 40 48 8B C8 48 85 C0 74 38 83 7B 08 00 74 17 48 8B 00 44 8B C7 48 8B 13 48 8B 5C 24 30 48 83 C4 20 5F 48 FF 60 08 48 8B 00 48 8D ?? ?? ?? ?? ?? 44 8B C7 48 8B 5C 24 30 48 83 C4 20 5F 48 FF 60 08 48 8B 5C 24 30 48 83 C4 20 5F C3"), - ].iter().map(|(name, pattern)| Ok((name, patternsleuth_scanner::Pattern::new(pattern)?))).collect::>>()?; - let pattern_refs = patterns - .iter() - .map(|(name, pattern)| (name, pattern)) - .collect::>(); - - let results = patternsleuth_scanner::scan_memchr_lookup(&pattern_refs, 0, memory); - - let get_sig = |sig: Sig| { - results - .iter() - .find_map(|(s, addr)| (***s == sig).then_some(*addr)) - }; - - if let Some(rva) = get_sig(Sig::GetServerName) { - let address = module_addr.add(rva); - - Resize16 = Some(std::mem::transmute(address.add(53 + 4).offset( - i32::from_le_bytes(memory[rva + 53..rva + 53 + 4].try_into().unwrap()) as isize, - ))); + PatternConfig::new(Sig::GetServerName, + "GetServerName".to_string(), + None, + Pattern::new("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 45 33 FF 4C 8B F2 48 8B D9 44 89 7C 24 50 41 8B FF")?, + resolve_self, + ), + PatternConfig::new(Sig::Disable, + "Disable".to_string(), + None, + Pattern::new("4C 8B B4 24 48 01 00 00 0F 84")?, + resolve_self, + ), + PatternConfig::new(Sig::SaveGameToSlot, + "SaveGameToSlot".to_string(), + None, + Pattern::new("48 89 5c 24 08 48 89 74 24 10 57 48 83 ec 40 48 8b da 33 f6 48 8d 54 24 30 48 89 74 24 30 48 89 74 24 38 41 8b f8")?, + resolve_self, + ), + PatternConfig::new(Sig::LoadGameFromMemory, + "LoadGameFromMemory".to_string(), + None, + Pattern::new("40 55 48 8d ac 24 00 ff ff ff 48 81 ec 00 02 00 00 83 79 08 00")?, + resolve_self, + ), + PatternConfig::new(Sig::LoadGameFromSlot, + "LoadGameFromSlot".to_string(), + None, + Pattern::new("48 8b c4 55 57 48 8d a8 d8 fe ff ff 48 81 ec 18 02 00 00")?, + resolve_self, + ), + PatternConfig::new(Sig::DoesSaveGameExist, + "DoesSaveGameExist".to_string(), + None, + Pattern::new("48 89 5C 24 08 57 48 83 EC 20 8B FA 48 8B D9 E8 ?? ?? ?? ?? 48 8B C8 4C 8B 00 41 FF 50 40 48 8B C8 48 85 C0 74 38 83 7B 08 00 74 17 48 8B 00 44 8B C7 48 8B 13 48 8B 5C 24 30 48 83 C4 20 5F 48 FF 60 08 48 8B 00 48 8D ?? ?? ?? ?? ?? 44 8B C7 48 8B 5C 24 30 48 83 C4 20 5F 48 FF 60 08 48 8B 5C 24 30 48 83 C4 20 5F C3")?, + resolve_self, + ), + ]; + + let scan = patternsleuth::process::internal::read_image()?.scan(&patterns)?; + + if let Ok(address) = scan.get_unique_sig_address(Sig::GetServerName) { + let address = address as *const c_void; + + let resize_rip = address.add(53); + Resize16 = Some(std::mem::transmute( + resize_rip + .add(4) + .offset((resize_rip as *const i32).read_unaligned() as isize), + )); let target: FnGetServerName = std::mem::transmute(address); GetServerName @@ -154,11 +157,11 @@ unsafe fn patch() -> Result<()> { } if matches!(installation_type, DRGInstallationType::Steam) { - if let Some(rva) = get_sig(Sig::Disable) { + if let Ok(address) = scan.get_unique_sig_address(Sig::Disable) { + let address = (address as *mut u8).add(29); let patch = [0xB8, 0x01, 0x00, 0x00, 0x00]; - let rva = rva + 29; - let patch_mem = &mut memory[rva..rva + 5]; + let patch_mem = std::slice::from_raw_parts_mut(address, patch.len()); let mut old: PAGE_PROTECTION_FLAGS = Default::default(); VirtualProtect( @@ -191,12 +194,15 @@ unsafe fn patch() -> Result<()> { .join("SaveGames"), ); - if let Some(rva) = get_sig(Sig::SaveGameToSlot) { - let address = module_addr.add(rva); + if let Ok(address) = scan.get_unique_sig_address(Sig::SaveGameToSlot) { + let address = address as *const c_void; - SaveGameToMemory = Some(std::mem::transmute(address.add(39 + 4).offset( - i32::from_le_bytes(memory[rva + 39..rva + 39 + 4].try_into().unwrap()) as isize, - ))); + let save_rip = address.add(39); + SaveGameToMemory = Some(std::mem::transmute( + save_rip + .add(4) + .offset((save_rip as *const i32).read_unaligned() as isize), + )); let target: FnSaveGameToSlot = std::mem::transmute(address); SaveGameToSlot @@ -204,12 +210,12 @@ unsafe fn patch() -> Result<()> { .enable()?; } - if let Some(rva) = get_sig(Sig::LoadGameFromMemory) { - let address = module_addr.add(rva); + if let Ok(address) = scan.get_unique_sig_address(Sig::LoadGameFromMemory) { + let address = address as *const c_void; LoadGameFromMemory = Some(std::mem::transmute(address)); - if let Some(rva) = get_sig(Sig::LoadGameFromSlot) { - let address = module_addr.add(rva); + if let Ok(address) = scan.get_unique_sig_address(Sig::LoadGameFromSlot) { + let address = address as *const c_void; let target: FnLoadGameFromSlot = std::mem::transmute(address); LoadGameFromSlot @@ -218,8 +224,8 @@ unsafe fn patch() -> Result<()> { } } - if let Some(rva) = get_sig(Sig::DoesSaveGameExist) { - let address = module_addr.add(rva); + if let Ok(address) = scan.get_unique_sig_address(Sig::DoesSaveGameExist) { + let address = address as *const c_void; let target: FnDoesSaveGameExist = std::mem::transmute(address); DoesSaveGameExist