From c01adb6c012f4793ee3a7524635e8d8b17f77f82 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 | 139 ++++++++++++++++++++++++++++++++++++++++++++++-- hook/Cargo.toml | 6 +-- hook/src/lib.rs | 130 +++++++++++++++++++++++--------------------- 3 files changed, 203 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 090ed7b2..091e0efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.31.1", "rustc-demangle", ] @@ -1464,6 +1464,12 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fastrand" version = "1.9.0" @@ -1951,7 +1957,7 @@ name = "hook" version = "0.1.0" dependencies = [ "anyhow", - "patternsleuth_scanner", + "patternsleuth", "retour", "windows 0.48.0", ] @@ -2520,6 +2526,15 @@ dependencies = [ "url", ] +[[package]] +name = "msvc-demangler" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb67c6dd0fa9b00619c41c5700b6f92d5f418be49b45ddb9970fbd4569df3c8" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "naive-cityhash" version = "0.2.0" @@ -2825,6 +2840,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -3009,10 +3035,27 @@ 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#41ea4e174f59881f303c6e2c362f461981fadffc" +dependencies = [ + "anyhow", + "libc", + "memchr", + "msvc-demangler", + "object 0.32.1", + "patternsleuth_scanner", + "pdb", + "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#41ea4e174f59881f303c6e2c362f461981fadffc" dependencies = [ "anyhow", "memchr", @@ -3031,6 +3074,17 @@ dependencies = [ "sha2", ] +[[package]] +name = "pdb" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" +dependencies = [ + "fallible-iterator", + "scroll", + "uuid", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -3386,7 +3440,7 @@ dependencies = [ "libloading 0.7.4", "once_cell", "sha1", - "strum", + "strum 0.24.1", "thiserror", "ureq", "zstd", @@ -3604,6 +3658,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" @@ -3640,6 +3705,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" + [[package]] name = "sct" version = "0.7.0" @@ -4007,7 +4078,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]] @@ -4023,6 +4103,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.29", +] + [[package]] name = "subtle" version = "2.5.0" @@ -4113,6 +4206,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.47" @@ -4400,6 +4513,16 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a464a4b34948a5f67fddd2b823c62d9d92e44be75058b99939eae6c5b6960b33" +[[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.16.0" @@ -4658,6 +4781,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" + [[package]] name = "valuable" version = "0.1.0" diff --git a/hook/Cargo.toml b/hook/Cargo.toml index e8d079de..ebdd5aa7 100644 --- a/hook/Cargo.toml +++ b/hook/Cargo.toml @@ -9,15 +9,11 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1.0.72" -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.48.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 3124177e..f8fe362c 100644 --- a/hook/src/lib.rs +++ b/hook/src/lib.rs @@ -4,15 +4,13 @@ use std::{ }; use anyhow::{bail, Context, Result}; +use patternsleuth::{patterns::resolve_self, scanner::Pattern, PatternConfig}; use retour::static_detour; use windows::{ Win32::Foundation::*, Win32::System::{ - LibraryLoader::GetModuleHandleA, Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS}, - ProcessStatus::{GetModuleInformation, MODULEINFO}, SystemServices::*, - Threading::GetCurrentProcess, }, }; @@ -86,25 +84,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, @@ -115,32 +95,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) as isize), + )); let target: FnGetServerName = std::mem::transmute(address); GetServerName @@ -149,11 +152,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( @@ -186,12 +189,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) as isize), + )); let target: FnSaveGameToSlot = std::mem::transmute(address); SaveGameToSlot @@ -199,12 +205,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 @@ -213,8 +219,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