From c8bb5a7fff571e45fd30c88fc88842ef6ec3f209 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 29 Jan 2025 19:07:47 -0600 Subject: [PATCH 01/19] WIP split crates - clean up patternsleuth_image --- Cargo.lock | 54 +- Cargo.toml | 2 + patternsleuth/Cargo.toml | 1 + patternsleuth/src/lib.rs | 725 +----------------- patternsleuth_image/Cargo.toml | 47 ++ .../src/image/elf.rs | 0 .../src/image/macros.rs | 5 + .../src/image/mod.rs | 30 +- .../src/image/pe.rs | 0 patternsleuth_image/src/lib.rs | 722 +++++++++++++++++ .../src/process/external.rs | 0 .../src/process/internal.rs | 0 .../src/process/mod.rs | 0 .../src/symbols.rs | 0 .../src/uesym.rs | 0 patternsleuth_resolvers/Cargo.toml | 48 ++ .../src/lib.rs | 0 .../src}/unreal/aes.rs | 0 .../src}/unreal/blueprint_library.rs | 0 .../src}/unreal/engine_version.rs | 0 .../src}/unreal/fname.rs | 0 .../src}/unreal/ftext.rs | 0 .../src}/unreal/fuobject_hash_tables.rs | 0 .../src}/unreal/game_loop.rs | 0 .../src}/unreal/gengine.rs | 0 .../src}/unreal/gmalloc.rs | 0 .../src}/unreal/guobject_array.rs | 0 .../src}/unreal/kismet.rs | 0 .../src}/unreal/mod.rs | 0 .../src}/unreal/pak.rs | 0 .../src}/unreal/save_game.rs | 0 .../src}/unreal/static_construct_object.rs | 0 .../src}/unreal/static_find_object.rs | 0 33 files changed, 894 insertions(+), 740 deletions(-) create mode 100644 patternsleuth_image/Cargo.toml rename {patternsleuth => patternsleuth_image}/src/image/elf.rs (100%) rename {patternsleuth => patternsleuth_image}/src/image/macros.rs (93%) rename {patternsleuth => patternsleuth_image}/src/image/mod.rs (90%) rename {patternsleuth => patternsleuth_image}/src/image/pe.rs (100%) create mode 100644 patternsleuth_image/src/lib.rs rename {patternsleuth => patternsleuth_image}/src/process/external.rs (100%) rename {patternsleuth => patternsleuth_image}/src/process/internal.rs (100%) rename {patternsleuth => patternsleuth_image}/src/process/mod.rs (100%) rename {patternsleuth => patternsleuth_image}/src/symbols.rs (100%) rename {patternsleuth => patternsleuth_image}/src/uesym.rs (100%) create mode 100644 patternsleuth_resolvers/Cargo.toml rename patternsleuth/src/resolvers/mod.rs => patternsleuth_resolvers/src/lib.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/aes.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/blueprint_library.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/engine_version.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/fname.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/ftext.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/fuobject_hash_tables.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/game_loop.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/gengine.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/gmalloc.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/guobject_array.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/kismet.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/mod.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/pak.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/save_game.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/static_construct_object.rs (100%) rename {patternsleuth/src/resolvers => patternsleuth_resolvers/src}/unreal/static_find_object.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 969ade3..e8bc922 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -2613,6 +2613,7 @@ dependencies = [ "memchr", "msvc-demangler", "object", + "patternsleuth_image", "patternsleuth_scanner", "pdb", "rayon", @@ -2654,6 +2655,57 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "patternsleuth_image" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-scopes", + "gimli", + "iced-x86", + "inventory", + "itertools 0.12.0", + "libc", + "memchr", + "msvc-demangler", + "object", + "patternsleuth_scanner", + "pdb", + "rayon", + "serde", + "strum", + "tracing", + "typetag", + "windows 0.52.0", +] + +[[package]] +name = "patternsleuth_resolvers" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "futures-scopes", + "gimli", + "iced-x86", + "inventory", + "itertools 0.12.0", + "libc", + "memchr", + "msvc-demangler", + "object", + "patternsleuth_image", + "patternsleuth_scanner", + "pdb", + "rayon", + "serde", + "strum", + "tracing", + "typetag", + "windows 0.52.0", +] + [[package]] name = "patternsleuth_scanner" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0dab389..5da20cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ members = [ "patternsleuth", "patternsleuth_cli", "patternsleuth_scanner", + "patternsleuth_image", + "patternsleuth_resolvers", "examples/*", ] default-members = ["patternsleuth_*"] diff --git a/patternsleuth/Cargo.toml b/patternsleuth/Cargo.toml index ec225b5..4ba52fb 100644 --- a/patternsleuth/Cargo.toml +++ b/patternsleuth/Cargo.toml @@ -24,6 +24,7 @@ serde = { workspace = true, optional = true, features = ["derive"] } typetag = { version = "0.2.15", optional = true } gimli = { version = "0.28.1", optional = true } tracing = "0.1.40" +patternsleuth_image = { version = "0.1.0", path = "../patternsleuth_image" } [target.'cfg(target_os = "linux")'.dependencies] libc = { version = "0.2.152", optional = true } diff --git a/patternsleuth/src/lib.rs b/patternsleuth/src/lib.rs index 2b9ec17..4700540 100644 --- a/patternsleuth/src/lib.rs +++ b/patternsleuth/src/lib.rs @@ -1,723 +1,2 @@ -pub mod image; -pub mod process; -pub mod resolvers; -#[cfg(feature = "symbols")] -pub mod symbols; -#[cfg(feature = "symbols")] -pub mod uesym; - -pub mod scanner { - pub use patternsleuth_scanner::*; -} - -use scanner::{Pattern, Xref}; -use std::{ - borrow::Cow, - collections::HashMap, - ops::{Index, Range, RangeFrom, RangeTo}, - path::Path, -}; - -use anyhow::{bail, Context, Result}; -use object::{File, Object, ObjectSection}; - -use image::Image; - -pub struct ResolveContext<'data, 'pattern> { - pub exe: &'data Image<'data>, - pub memory: &'data Memory<'data>, - pub section: String, - pub match_address: usize, - pub scan: &'pattern Scan, -} - -#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct Resolution { - pub address: usize, -} - -#[derive(Debug, Clone)] -pub struct Scan { - pub section: Option, - pub scan_type: ScanType, -} -#[derive(Debug, Clone)] -pub enum ScanType { - Pattern(Pattern), - Xref(Xref), -} -impl ScanType { - pub fn get_pattern(&self) -> Option<&Pattern> { - match self { - Self::Pattern(pattern) => Some(pattern), - _ => None, - } - } - pub fn get_xref(&self) -> Option<&Xref> { - match self { - Self::Xref(xref) => Some(xref), - _ => None, - } - } -} -impl From for ScanType { - fn from(value: Pattern) -> Self { - Self::Pattern(value) - } -} -impl From for ScanType { - fn from(value: Xref) -> Self { - Self::Xref(value) - } -} - -#[derive(Debug)] -pub struct PatternConfig { - pub sig: S, - pub name: String, - pub scan: Scan, -} -impl PatternConfig { - pub fn new( - sig: S, - name: String, - section: Option, - pattern: Pattern, - ) -> Self { - Self { - sig, - name, - scan: Scan { - section, - scan_type: pattern.into(), - }, - } - } - pub fn xref(sig: S, name: String, section: Option, xref: Xref) -> Self { - Self { - sig, - name, - scan: Scan { - section, - scan_type: xref.into(), - }, - } - } -} - -#[derive(Debug)] -pub struct ScanResult<'a, S> { - pub results: Vec<(&'a PatternConfig, Resolution)>, -} -impl ScanResult<'_, S> { - pub fn get_unique_sig_address(&self, sig: S) -> Result { - let mut address = None; - for (config, res) in &self.results { - if config.sig == sig { - if let Some(existing) = address { - if existing != res.address { - bail!("sig {sig:?} matched multiple addresses") - } - } else { - address = Some(res.address) - } - } - } - address.with_context(|| format!("sig {sig:?} not found")) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct RuntimeFunction { - pub range: Range, - pub unwind: usize, -} -impl RuntimeFunction { - pub fn read<'data>( - memory: &impl MemoryTrait<'data>, - base_address: usize, - address: usize, - ) -> Result { - let addr_begin = base_address + memory.u32_le(address)? as usize; - let addr_end = base_address + memory.u32_le(address + 4)? as usize; - let unwind = base_address + memory.u32_le(address + 8)? as usize; - - Ok(RuntimeFunction { - range: addr_begin..addr_end, - unwind, - }) - } -} -impl RuntimeFunction { - pub fn range(&self) -> Range { - self.range.clone() - } -} - -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - feature = "serde-resolvers", - derive(serde::Serialize, serde::Deserialize) -)] -pub enum MemoryAccessError { - MemoryOutOfBoundsError, - Utf8Error, - Utf16Error, - MisalginedAddress(usize, usize), -} -impl std::error::Error for MemoryAccessError {} -impl std::fmt::Display for MemoryAccessError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MemoryOutOfBoundsError => write!(f, "MemoryOutOfBoundsError"), - Self::Utf8Error => write!(f, "Utf8Error"), - Self::Utf16Error => write!(f, "Utf16Error"), - Self::MisalginedAddress(addr, align) => { - write!(f, "MisalginedAddress: address {:#x} != {:#x}", addr, align) - } - } - } -} -impl From for MemoryAccessError { - fn from(_: std::str::Utf8Error) -> Self { - Self::Utf8Error - } -} -impl From for MemoryAccessError { - fn from(_: std::string::FromUtf16Error) -> Self { - Self::Utf16Error - } -} - -/// Continuous section of memory -pub trait MemoryBlockTrait<'data> { - /// Return starting address of block - fn address(&self) -> usize; - /// Returned contained memory - fn data(&self) -> &[u8]; -} - -/// Potentially sparse section of memory -pub trait MemoryTrait<'data> { - /// Return u8 at `address` - fn index(&self, address: usize) -> Result; - /// Return slice of u8 at `range` - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError>; - /// Return slice of u8 from start of `range` to end of block - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError>; - /// Return slice of u8 from end of `range` to start of block (not useful because start of block - /// is unknown to caller) - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError>; - - /// Return i16 at `address` - fn i16_le(&self, address: usize) -> Result { - Ok(i16::from_le_bytes( - self.range(address..address + std::mem::size_of::())? - .try_into() - .unwrap(), - )) - } - /// Return u16 at `address` - fn u16_le(&self, address: usize) -> Result { - Ok(u16::from_le_bytes( - self.range(address..address + std::mem::size_of::())? - .try_into() - .unwrap(), - )) - } - /// Return i32 at `address` - fn i32_le(&self, address: usize) -> Result { - Ok(i32::from_le_bytes( - self.range(address..address + std::mem::size_of::())? - .try_into() - .unwrap(), - )) - } - /// Return u32 at `address` - fn u32_le(&self, address: usize) -> Result { - Ok(u32::from_le_bytes( - self.range(address..address + std::mem::size_of::())? - .try_into() - .unwrap(), - )) - } - /// Return u64 at `address` - fn u64_le(&self, address: usize) -> Result { - Ok(u64::from_le_bytes( - self.range(address..address + std::mem::size_of::())? - .try_into() - .unwrap(), - )) - } - /// Return ptr (usize) at `address` - fn ptr(&self, address: usize) -> Result { - Ok(self.u64_le(address)? as usize) - } - /// Return instruction relative address at `address` - fn rip4(&self, address: usize) -> Result { - Ok((address + 4) - .checked_add_signed(self.i32_le(address)? as isize) - .unwrap()) - } - - /// Read null terminated string from `address` - fn read_string(&self, address: usize) -> Result { - let data = &self - .range_from(address..)? - .iter() - .cloned() - .take_while(|n| *n != 0) - .collect::>(); - - Ok(std::str::from_utf8(data)?.to_string()) - } - - /// Read null terminated wide string from `address` - fn read_wstring(&self, address: usize) -> Result { - let data = &self - .range_from(address..)? - .chunks(2) - .map(|chunk| ((chunk[1] as u16) << 8) + chunk[0] as u16) - .take_while(|n| *n != 0) - .collect::>(); - - Ok(String::from_utf16(data)?) - } -} - -impl<'data, T: MemoryBlockTrait<'data>> MemoryTrait<'data> for T { - fn index(&self, address: usize) -> Result { - // TODO bounds - Ok(self.data()[address - self.address()]) - } - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { - // TODO bounds - Ok(&self.data()[range.start - self.address()..range.end - self.address()]) - } - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { - // TODO bounds - Ok(&self.data()[range.start - self.address()..]) - } - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { - // TODO bounds - Ok(&self.data()[..range.end - self.address()]) - } -} - -impl<'data> MemoryTrait<'data> for Memory<'data> { - fn index(&self, address: usize) -> Result { - self.get_section_containing(address)?.index(address) - } - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { - self.get_section_containing(range.start)?.range(range) - } - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { - self.get_section_containing(range.start)?.range_from(range) - } - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { - self.get_section_containing(range.end)?.range_to(range) - } -} - -pub struct MemorySection<'data> { - address: usize, - data: Cow<'data, [u8]>, -} -impl<'data> MemoryBlockTrait<'data> for MemorySection<'data> { - fn address(&self) -> usize { - self.address - } - fn data(&self) -> &[u8] { - &self.data - } -} - -pub struct NamedMemorySection<'data> { - name: String, - kind: object::SectionKind, - section: MemorySection<'data>, -} - -impl<'data> NamedMemorySection<'data> { - fn new>>( - name: String, - address: usize, - kind: object::SectionKind, - data: T, - ) -> Self { - Self { - name, - kind, - section: MemorySection { - address, - data: data.into(), - }, - } - } -} -impl NamedMemorySection<'_> { - pub fn name(&self) -> &str { - &self.name - } - pub fn kind(&self) -> object::SectionKind { - self.kind - } - pub fn address(&self) -> usize { - self.section.address() - } - pub fn data(&self) -> &[u8] { - self.section.data() - } - pub fn len(&self) -> usize { - self.section.data.len() - } - pub fn is_empty(&self) -> bool { - self.section.data.is_empty() - } -} -impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { - fn address(&self) -> usize { - self.section.address() - } - fn data(&self) -> &[u8] { - self.section.data() - } -} - -pub struct Memory<'data> { - sections: Vec>, -} - -impl<'data> Memory<'data> { - pub fn new(object: &File<'data>) -> Result { - Ok(Self { - sections: object - .sections() - .map(|s| { - Ok(NamedMemorySection::new( - s.name()?.to_string(), - s.address() as usize, - s.kind(), - s.data()?, - )) - }) - .collect::>>()?, - }) - } - pub fn new_external_data(sections: Vec<(object::Section<'_, '_>, Vec)>) -> Result { - Ok(Self { - sections: sections - .into_iter() - .map(|(s, d)| { - Ok(NamedMemorySection::new( - s.name()?.to_string(), - s.address() as usize, - s.kind(), - d, - )) - }) - .collect::>>()?, - }) - } - pub fn new_internal_data( - sections: Vec<(object::Section<'_, '_>, &'data [u8])>, - ) -> Result { - Ok(Self { - sections: sections - .into_iter() - .map(|(s, d)| { - Ok(NamedMemorySection::new( - s.name()?.to_string(), - s.address() as usize, - s.kind(), - d, - )) - }) - .collect::>>()?, - }) - } - pub fn sections(&self) -> &[NamedMemorySection] { - &self.sections - } - pub fn get_section_containing( - &self, - address: usize, - ) -> Result<&NamedMemorySection<'data>, MemoryAccessError> { - self.sections - .iter() - .find(|section| { - address >= section.section.address - && address < section.section.address + section.section.data.len() - }) - .ok_or(MemoryAccessError::MemoryOutOfBoundsError) - } - pub fn find(&self, kind: object::SectionKind, filter: F) -> Option - where - F: Fn(usize, &[u8]) -> bool, - { - self.sections.iter().find_map(|section| { - if section.kind == kind { - section - .section - .data - .windows(4) - .enumerate() - .find_map(|(i, slice)| { - filter(section.section.address + i, slice) - .then_some(section.section.address + i) - }) - } else { - None - } - }) - } -} -impl Index for Memory<'_> { - type Output = u8; - fn index(&self, index: usize) -> &Self::Output { - self.sections - .iter() - .find_map(|section| section.section.data.get(index - section.section.address)) - .unwrap() - } -} -impl Index> for Memory<'_> { - type Output = [u8]; - fn index(&self, index: Range) -> &Self::Output { - self.sections - .iter() - .find_map(|section| { - if index.start >= section.section.address - && index.end <= section.section.address + section.section.data.len() - { - let relative_range = - index.start - section.section.address..index.end - section.section.address; - Some(§ion.section.data[relative_range]) - } else { - None - } - }) - .unwrap() - } -} - -pub trait Addressable { - fn rip(&self) -> usize; - fn ptr(&self) -> usize; - fn u32(&self) -> u32; -} -impl Addressable for patternsleuth_scanner::Capture<'_> { - fn rip(&self) -> usize { - (self.address + 4) - .checked_add_signed(i32::from_le_bytes(self.data.try_into().unwrap()) as isize) - .unwrap() - } - fn ptr(&self) -> usize { - usize::from_le_bytes(self.data.try_into().unwrap()) - } - fn u32(&self) -> u32 { - u32::from_le_bytes(self.data.try_into().unwrap()) - } -} - -pub trait Matchable<'data> { - fn captures( - &'data self, - pattern: &Pattern, - address: usize, - ) -> Result>>, MemoryAccessError>; -} - -impl<'data> Matchable<'data> for Memory<'data> { - fn captures( - &'data self, - pattern: &Pattern, - address: usize, - ) -> Result>>, MemoryAccessError> { - let s = self.get_section_containing(address)?; - // TODO bounds check data passed to captures - Ok(pattern.captures(s.data(), s.address(), address - s.address())) - } -} - -pub mod disassemble { - use std::{collections::HashSet, ops::Range}; - - use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, NasmFormatter}; - - use crate::{Image, MemoryAccessError, MemoryTrait}; - - pub fn function_range( - exe: &Image<'_>, - address: usize, - ) -> Result, MemoryAccessError> { - let min = address; - let mut max = min; - disassemble(exe, address, |inst| { - let cur = inst.ip() as usize; - if Some(address) != exe.get_root_function(cur)?.map(|f| f.range.start) { - return Ok(Control::Break); - } - max = max.max(cur + inst.len()); - Ok(Control::Continue) - })?; - Ok(min..max) - } - - pub fn disassemble_single<'mem, 'img: 'mem>( - exe: &'img Image<'mem>, - address: usize, - ) -> Result, MemoryAccessError> { - Ok(Decoder::with_ip( - 64, - exe.memory.range_from(address..)?, - address as u64, - DecoderOptions::NONE, - ) - .iter() - .next()) - } - - pub enum Control { - Continue, - Break, - Exit, - } - - pub fn disassemble<'mem, 'img: 'mem, F>( - exe: &'img Image<'mem>, - address: usize, - mut visitor: F, - ) -> Result<(), MemoryAccessError> - where - F: FnMut(&Instruction) -> Result, - { - struct Ctx<'mem, 'img: 'mem> { - exe: &'img Image<'mem>, - queue: Vec, - visited: HashSet, - address: usize, - block: &'mem [u8], - decoder: Decoder<'mem>, - instruction: Instruction, - } - - let block = exe.memory.range_from(address..)?; - let mut ctx = Ctx { - exe, - queue: Default::default(), - visited: Default::default(), - address, - block, - decoder: Decoder::with_ip(64, block, address as u64, DecoderOptions::NONE), - instruction: Default::default(), - }; - - impl Ctx<'_, '_> { - #[allow(unused)] - fn print(&self) { - let mut formatter = NasmFormatter::new(); - //formatter.options_mut().set_digit_separator("`"); - //formatter.options_mut().set_first_operand_char_index(10); - - let mut output = String::new(); - formatter.format(&self.instruction, &mut output); - - // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" - print!("{:016X} ", self.instruction.ip()); - let start_index = self.instruction.ip() as usize - self.address; - let instr_bytes = &self.block[start_index..start_index + self.instruction.len()]; - for b in instr_bytes.iter() { - print!("{:02X}", b); - } - if instr_bytes.len() < 0x10 { - for _ in 0..0x10 - instr_bytes.len() { - print!(" "); - } - } - println!(" {}", output); - } - fn start(&mut self, address: usize) -> Result<(), MemoryAccessError> { - //println!("starting at {address:x}"); - self.address = address; - self.block = self.exe.memory.range_from(self.address..)?; - self.decoder = - Decoder::with_ip(64, self.block, self.address as u64, DecoderOptions::NONE); - Ok(()) - } - /// Returns true if pop was successful - fn pop(&mut self) -> Result { - Ok(if let Some(next) = self.queue.pop() { - self.start(next)?; - true - } else { - false - }) - } - } - - while ctx.decoder.can_decode() { - ctx.decoder.decode_out(&mut ctx.instruction); - - let addr = ctx.instruction.ip() as usize; - - if ctx.visited.contains(&addr) { - if ctx.pop()? { - continue; - } else { - break; - } - } else { - ctx.visited.insert(addr); - match visitor(&ctx.instruction)? { - Control::Continue => {} - Control::Break => { - if ctx.pop()? { - continue; - } else { - break; - } - } - Control::Exit => { - break; - } - } - } - - /* - if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { - //println!(); - } - ctx.print(); - if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { - //println!(); - } - */ - - match ctx.instruction.flow_control() { - FlowControl::Next => {} - FlowControl::UnconditionalBranch => { - // TODO figure out how to handle tail calls - ctx.start(ctx.instruction.near_branch_target() as usize)?; - } - //FlowControl::IndirectBranch => todo!(), - FlowControl::ConditionalBranch => { - ctx.queue - .push(ctx.instruction.near_branch_target() as usize); - } - FlowControl::Return => { - if !ctx.pop()? { - break; - } - } - //FlowControl::Call => todo!(), - //FlowControl::IndirectCall => todo!(), - //FlowControl::Interrupt => todo!(), - //FlowControl::XbeginXabortXend => todo!(), - //FlowControl::Exception => todo!(), - _ => {} - } - } - Ok(()) - } -} +pub use patternsleuth_scanner as scanner; +pub use patternsleuth_image::process as process; diff --git a/patternsleuth_image/Cargo.toml b/patternsleuth_image/Cargo.toml new file mode 100644 index 0000000..3591a1e --- /dev/null +++ b/patternsleuth_image/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "patternsleuth_image" +repository.workspace = true +authors.workspace = true +license.workspace = true +version.workspace = true +edition.workspace = true + +[dependencies] +patternsleuth_scanner = { path = "../patternsleuth_scanner" } +anyhow = { workspace = true } +memchr = { workspace = true } +object = { workspace = true } +rayon = { workspace = true } +strum = { workspace = true } +pdb = { workspace = true, optional = true } +msvc-demangler = { workspace = true, optional = true} +iced-x86.workspace = true +futures = "0.3.30" +futures-scopes = "0.2.0" +inventory = "0.3.14" +itertools.workspace = true +serde = { workspace = true, optional = true, features = ["derive"] } +typetag = { version = "0.2.15", optional = true } +gimli = { version = "0.28.1", optional = true } +tracing = "0.1.40" + +[target.'cfg(target_os = "linux")'.dependencies] +libc = { version = "0.2.152", optional = true } + +[target.'cfg(windows)'.dependencies] +windows = { workspace = true, optional = true, features = [ + "Win32_Foundation", + "Win32_System_LibraryLoader", + "Win32_System_ProcessStatus", + "Win32_System_Threading", + "Win32_System_Diagnostics_Debug", +] } + +[features] +default = [] +serde-resolvers = ["dep:serde", "dep:typetag"] +symbols = ["dep:pdb", "dep:msvc-demangler"] +process-external = ["image-pe", "dep:libc", "dep:windows"] +process-internal = ["dep:libc", "dep:windows"] +image-pe = [] +image-elf = ["dep:gimli"] diff --git a/patternsleuth/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs similarity index 100% rename from patternsleuth/src/image/elf.rs rename to patternsleuth_image/src/image/elf.rs diff --git a/patternsleuth/src/image/macros.rs b/patternsleuth_image/src/image/macros.rs similarity index 93% rename from patternsleuth/src/image/macros.rs rename to patternsleuth_image/src/image/macros.rs index 8151761..35779b4 100644 --- a/patternsleuth/src/image/macros.rs +++ b/patternsleuth_image/src/image/macros.rs @@ -9,6 +9,7 @@ macro_rules! image_type_dispatch { } ) => { image_type_dispatch!(@define_imagetype $enum_name_it $enum_tt); + #[allow(unused)] impl<'data> Image<'data> { $( pub fn $fnname_it(&self, $($arg: $arg_ty),*) -> $ret { @@ -32,6 +33,8 @@ macro_rules! image_type_dispatch { #[cfg(feature = $img_feature)] $enum_name_it::$img_ident(inner) => inner.$fnname_it($self, $args_tt), )* + #[allow(unreachable_patterns)] + _ => unreachable!(), } }; @@ -41,6 +44,8 @@ macro_rules! image_type_dispatch { #[cfg(feature = $img_feature)] $enum_name_it::$img_ident(inner) => inner.$fnname_it($self), )* + #[allow(unreachable_patterns)] + _ => unreachable!(), } }; diff --git a/patternsleuth/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs similarity index 90% rename from patternsleuth/src/image/mod.rs rename to patternsleuth_image/src/image/mod.rs index 62c9971..efa6650 100644 --- a/patternsleuth/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -13,9 +13,6 @@ use pe::PEImage; use macros::*; -#[cfg(not(any(feature = "image-pe", feature = "image-elf")))] -compile_error!("requires at least one of image-pe or image-elf features"); - image_type_dispatch! { @enum ImageType as _image_type_reflection { PEImage(PEImage, "image-pe"), @@ -44,6 +41,7 @@ pub struct Image<'data> { // Type-independent impl<'data> Image<'data> { + #[allow(unused)] pub fn read>( base_addr: Option, data: &'data [u8], @@ -66,19 +64,19 @@ impl<'data> Image<'data> { pub fn builder() -> ImageBuilder { Default::default() } - pub fn resolve( - &self, - resolver: &'static resolvers::ResolverFactory, - ) -> resolvers::Result { - resolvers::resolve(self, resolver) - } - - pub fn resolve_many( - &self, - resolvers: &[fn() -> &'static resolvers::DynResolverFactory], - ) -> Vec>> { - resolvers::resolve_many(self, resolvers) - } + //pub fn resolve( + // &self, + // resolver: &'static resolvers::ResolverFactory, + //) -> resolvers::Result { + // resolvers::resolve(self, resolver) + //} + + //pub fn resolve_many( + // &self, + // resolvers: &[fn() -> &'static resolvers::DynResolverFactory], + //) -> Vec>> { + // resolvers::resolve_many(self, resolvers) + //} pub fn scan<'patterns, S>( &self, diff --git a/patternsleuth/src/image/pe.rs b/patternsleuth_image/src/image/pe.rs similarity index 100% rename from patternsleuth/src/image/pe.rs rename to patternsleuth_image/src/image/pe.rs diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs new file mode 100644 index 0000000..e7024fb --- /dev/null +++ b/patternsleuth_image/src/lib.rs @@ -0,0 +1,722 @@ +pub mod image; +pub mod process; +#[cfg(feature = "symbols")] +pub mod symbols; +#[cfg(feature = "symbols")] +pub mod uesym; + +pub mod scanner { + pub use patternsleuth_scanner::*; +} + +use scanner::{Pattern, Xref}; +use std::{ + borrow::Cow, + collections::HashMap, + ops::{Index, Range, RangeFrom, RangeTo}, + path::Path, +}; + +use anyhow::{bail, Context, Result}; +use object::{File, Object, ObjectSection}; + +use image::Image; + +pub struct ResolveContext<'data, 'pattern> { + pub exe: &'data Image<'data>, + pub memory: &'data Memory<'data>, + pub section: String, + pub match_address: usize, + pub scan: &'pattern Scan, +} + +#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub struct Resolution { + pub address: usize, +} + +#[derive(Debug, Clone)] +pub struct Scan { + pub section: Option, + pub scan_type: ScanType, +} +#[derive(Debug, Clone)] +pub enum ScanType { + Pattern(Pattern), + Xref(Xref), +} +impl ScanType { + pub fn get_pattern(&self) -> Option<&Pattern> { + match self { + Self::Pattern(pattern) => Some(pattern), + _ => None, + } + } + pub fn get_xref(&self) -> Option<&Xref> { + match self { + Self::Xref(xref) => Some(xref), + _ => None, + } + } +} +impl From for ScanType { + fn from(value: Pattern) -> Self { + Self::Pattern(value) + } +} +impl From for ScanType { + fn from(value: Xref) -> Self { + Self::Xref(value) + } +} + +#[derive(Debug)] +pub struct PatternConfig { + pub sig: S, + pub name: String, + pub scan: Scan, +} +impl PatternConfig { + pub fn new( + sig: S, + name: String, + section: Option, + pattern: Pattern, + ) -> Self { + Self { + sig, + name, + scan: Scan { + section, + scan_type: pattern.into(), + }, + } + } + pub fn xref(sig: S, name: String, section: Option, xref: Xref) -> Self { + Self { + sig, + name, + scan: Scan { + section, + scan_type: xref.into(), + }, + } + } +} + +#[derive(Debug)] +pub struct ScanResult<'a, S> { + pub results: Vec<(&'a PatternConfig, Resolution)>, +} +impl ScanResult<'_, S> { + pub fn get_unique_sig_address(&self, sig: S) -> Result { + let mut address = None; + for (config, res) in &self.results { + if config.sig == sig { + if let Some(existing) = address { + if existing != res.address { + bail!("sig {sig:?} matched multiple addresses") + } + } else { + address = Some(res.address) + } + } + } + address.with_context(|| format!("sig {sig:?} not found")) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RuntimeFunction { + pub range: Range, + pub unwind: usize, +} +impl RuntimeFunction { + pub fn read<'data>( + memory: &impl MemoryTrait<'data>, + base_address: usize, + address: usize, + ) -> Result { + let addr_begin = base_address + memory.u32_le(address)? as usize; + let addr_end = base_address + memory.u32_le(address + 4)? as usize; + let unwind = base_address + memory.u32_le(address + 8)? as usize; + + Ok(RuntimeFunction { + range: addr_begin..addr_end, + unwind, + }) + } +} +impl RuntimeFunction { + pub fn range(&self) -> Range { + self.range.clone() + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + feature = "serde-resolvers", + derive(serde::Serialize, serde::Deserialize) +)] +pub enum MemoryAccessError { + MemoryOutOfBoundsError, + Utf8Error, + Utf16Error, + MisalginedAddress(usize, usize), +} +impl std::error::Error for MemoryAccessError {} +impl std::fmt::Display for MemoryAccessError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MemoryOutOfBoundsError => write!(f, "MemoryOutOfBoundsError"), + Self::Utf8Error => write!(f, "Utf8Error"), + Self::Utf16Error => write!(f, "Utf16Error"), + Self::MisalginedAddress(addr, align) => { + write!(f, "MisalginedAddress: address {:#x} != {:#x}", addr, align) + } + } + } +} +impl From for MemoryAccessError { + fn from(_: std::str::Utf8Error) -> Self { + Self::Utf8Error + } +} +impl From for MemoryAccessError { + fn from(_: std::string::FromUtf16Error) -> Self { + Self::Utf16Error + } +} + +/// Continuous section of memory +pub trait MemoryBlockTrait<'data> { + /// Return starting address of block + fn address(&self) -> usize; + /// Returned contained memory + fn data(&self) -> &[u8]; +} + +/// Potentially sparse section of memory +pub trait MemoryTrait<'data> { + /// Return u8 at `address` + fn index(&self, address: usize) -> Result; + /// Return slice of u8 at `range` + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError>; + /// Return slice of u8 from start of `range` to end of block + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError>; + /// Return slice of u8 from end of `range` to start of block (not useful because start of block + /// is unknown to caller) + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError>; + + /// Return i16 at `address` + fn i16_le(&self, address: usize) -> Result { + Ok(i16::from_le_bytes( + self.range(address..address + std::mem::size_of::())? + .try_into() + .unwrap(), + )) + } + /// Return u16 at `address` + fn u16_le(&self, address: usize) -> Result { + Ok(u16::from_le_bytes( + self.range(address..address + std::mem::size_of::())? + .try_into() + .unwrap(), + )) + } + /// Return i32 at `address` + fn i32_le(&self, address: usize) -> Result { + Ok(i32::from_le_bytes( + self.range(address..address + std::mem::size_of::())? + .try_into() + .unwrap(), + )) + } + /// Return u32 at `address` + fn u32_le(&self, address: usize) -> Result { + Ok(u32::from_le_bytes( + self.range(address..address + std::mem::size_of::())? + .try_into() + .unwrap(), + )) + } + /// Return u64 at `address` + fn u64_le(&self, address: usize) -> Result { + Ok(u64::from_le_bytes( + self.range(address..address + std::mem::size_of::())? + .try_into() + .unwrap(), + )) + } + /// Return ptr (usize) at `address` + fn ptr(&self, address: usize) -> Result { + Ok(self.u64_le(address)? as usize) + } + /// Return instruction relative address at `address` + fn rip4(&self, address: usize) -> Result { + Ok((address + 4) + .checked_add_signed(self.i32_le(address)? as isize) + .unwrap()) + } + + /// Read null terminated string from `address` + fn read_string(&self, address: usize) -> Result { + let data = &self + .range_from(address..)? + .iter() + .cloned() + .take_while(|n| *n != 0) + .collect::>(); + + Ok(std::str::from_utf8(data)?.to_string()) + } + + /// Read null terminated wide string from `address` + fn read_wstring(&self, address: usize) -> Result { + let data = &self + .range_from(address..)? + .chunks(2) + .map(|chunk| ((chunk[1] as u16) << 8) + chunk[0] as u16) + .take_while(|n| *n != 0) + .collect::>(); + + Ok(String::from_utf16(data)?) + } +} + +impl<'data, T: MemoryBlockTrait<'data>> MemoryTrait<'data> for T { + fn index(&self, address: usize) -> Result { + // TODO bounds + Ok(self.data()[address - self.address()]) + } + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { + // TODO bounds + Ok(&self.data()[range.start - self.address()..range.end - self.address()]) + } + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { + // TODO bounds + Ok(&self.data()[range.start - self.address()..]) + } + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { + // TODO bounds + Ok(&self.data()[..range.end - self.address()]) + } +} + +impl<'data> MemoryTrait<'data> for Memory<'data> { + fn index(&self, address: usize) -> Result { + self.get_section_containing(address)?.index(address) + } + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { + self.get_section_containing(range.start)?.range(range) + } + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { + self.get_section_containing(range.start)?.range_from(range) + } + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { + self.get_section_containing(range.end)?.range_to(range) + } +} + +pub struct MemorySection<'data> { + address: usize, + data: Cow<'data, [u8]>, +} +impl<'data> MemoryBlockTrait<'data> for MemorySection<'data> { + fn address(&self) -> usize { + self.address + } + fn data(&self) -> &[u8] { + &self.data + } +} + +pub struct NamedMemorySection<'data> { + name: String, + kind: object::SectionKind, + section: MemorySection<'data>, +} + +impl<'data> NamedMemorySection<'data> { + fn new>>( + name: String, + address: usize, + kind: object::SectionKind, + data: T, + ) -> Self { + Self { + name, + kind, + section: MemorySection { + address, + data: data.into(), + }, + } + } +} +impl NamedMemorySection<'_> { + pub fn name(&self) -> &str { + &self.name + } + pub fn kind(&self) -> object::SectionKind { + self.kind + } + pub fn address(&self) -> usize { + self.section.address() + } + pub fn data(&self) -> &[u8] { + self.section.data() + } + pub fn len(&self) -> usize { + self.section.data.len() + } + pub fn is_empty(&self) -> bool { + self.section.data.is_empty() + } +} +impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { + fn address(&self) -> usize { + self.section.address() + } + fn data(&self) -> &[u8] { + self.section.data() + } +} + +pub struct Memory<'data> { + sections: Vec>, +} + +impl<'data> Memory<'data> { + pub fn new(object: &File<'data>) -> Result { + Ok(Self { + sections: object + .sections() + .map(|s| { + Ok(NamedMemorySection::new( + s.name()?.to_string(), + s.address() as usize, + s.kind(), + s.data()?, + )) + }) + .collect::>>()?, + }) + } + pub fn new_external_data(sections: Vec<(object::Section<'_, '_>, Vec)>) -> Result { + Ok(Self { + sections: sections + .into_iter() + .map(|(s, d)| { + Ok(NamedMemorySection::new( + s.name()?.to_string(), + s.address() as usize, + s.kind(), + d, + )) + }) + .collect::>>()?, + }) + } + pub fn new_internal_data( + sections: Vec<(object::Section<'_, '_>, &'data [u8])>, + ) -> Result { + Ok(Self { + sections: sections + .into_iter() + .map(|(s, d)| { + Ok(NamedMemorySection::new( + s.name()?.to_string(), + s.address() as usize, + s.kind(), + d, + )) + }) + .collect::>>()?, + }) + } + pub fn sections(&self) -> &[NamedMemorySection] { + &self.sections + } + pub fn get_section_containing( + &self, + address: usize, + ) -> Result<&NamedMemorySection<'data>, MemoryAccessError> { + self.sections + .iter() + .find(|section| { + address >= section.section.address + && address < section.section.address + section.section.data.len() + }) + .ok_or(MemoryAccessError::MemoryOutOfBoundsError) + } + pub fn find(&self, kind: object::SectionKind, filter: F) -> Option + where + F: Fn(usize, &[u8]) -> bool, + { + self.sections.iter().find_map(|section| { + if section.kind == kind { + section + .section + .data + .windows(4) + .enumerate() + .find_map(|(i, slice)| { + filter(section.section.address + i, slice) + .then_some(section.section.address + i) + }) + } else { + None + } + }) + } +} +impl Index for Memory<'_> { + type Output = u8; + fn index(&self, index: usize) -> &Self::Output { + self.sections + .iter() + .find_map(|section| section.section.data.get(index - section.section.address)) + .unwrap() + } +} +impl Index> for Memory<'_> { + type Output = [u8]; + fn index(&self, index: Range) -> &Self::Output { + self.sections + .iter() + .find_map(|section| { + if index.start >= section.section.address + && index.end <= section.section.address + section.section.data.len() + { + let relative_range = + index.start - section.section.address..index.end - section.section.address; + Some(§ion.section.data[relative_range]) + } else { + None + } + }) + .unwrap() + } +} + +pub trait Addressable { + fn rip(&self) -> usize; + fn ptr(&self) -> usize; + fn u32(&self) -> u32; +} +impl Addressable for patternsleuth_scanner::Capture<'_> { + fn rip(&self) -> usize { + (self.address + 4) + .checked_add_signed(i32::from_le_bytes(self.data.try_into().unwrap()) as isize) + .unwrap() + } + fn ptr(&self) -> usize { + usize::from_le_bytes(self.data.try_into().unwrap()) + } + fn u32(&self) -> u32 { + u32::from_le_bytes(self.data.try_into().unwrap()) + } +} + +pub trait Matchable<'data> { + fn captures( + &'data self, + pattern: &Pattern, + address: usize, + ) -> Result>>, MemoryAccessError>; +} + +impl<'data> Matchable<'data> for Memory<'data> { + fn captures( + &'data self, + pattern: &Pattern, + address: usize, + ) -> Result>>, MemoryAccessError> { + let s = self.get_section_containing(address)?; + // TODO bounds check data passed to captures + Ok(pattern.captures(s.data(), s.address(), address - s.address())) + } +} + +pub mod disassemble { + use std::{collections::HashSet, ops::Range}; + + use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, NasmFormatter}; + + use crate::{Image, MemoryAccessError, MemoryTrait}; + + pub fn function_range( + exe: &Image<'_>, + address: usize, + ) -> Result, MemoryAccessError> { + let min = address; + let mut max = min; + disassemble(exe, address, |inst| { + let cur = inst.ip() as usize; + if Some(address) != exe.get_root_function(cur)?.map(|f| f.range.start) { + return Ok(Control::Break); + } + max = max.max(cur + inst.len()); + Ok(Control::Continue) + })?; + Ok(min..max) + } + + pub fn disassemble_single<'mem, 'img: 'mem>( + exe: &'img Image<'mem>, + address: usize, + ) -> Result, MemoryAccessError> { + Ok(Decoder::with_ip( + 64, + exe.memory.range_from(address..)?, + address as u64, + DecoderOptions::NONE, + ) + .iter() + .next()) + } + + pub enum Control { + Continue, + Break, + Exit, + } + + pub fn disassemble<'mem, 'img: 'mem, F>( + exe: &'img Image<'mem>, + address: usize, + mut visitor: F, + ) -> Result<(), MemoryAccessError> + where + F: FnMut(&Instruction) -> Result, + { + struct Ctx<'mem, 'img: 'mem> { + exe: &'img Image<'mem>, + queue: Vec, + visited: HashSet, + address: usize, + block: &'mem [u8], + decoder: Decoder<'mem>, + instruction: Instruction, + } + + let block = exe.memory.range_from(address..)?; + let mut ctx = Ctx { + exe, + queue: Default::default(), + visited: Default::default(), + address, + block, + decoder: Decoder::with_ip(64, block, address as u64, DecoderOptions::NONE), + instruction: Default::default(), + }; + + impl Ctx<'_, '_> { + #[allow(unused)] + fn print(&self) { + let mut formatter = NasmFormatter::new(); + //formatter.options_mut().set_digit_separator("`"); + //formatter.options_mut().set_first_operand_char_index(10); + + let mut output = String::new(); + formatter.format(&self.instruction, &mut output); + + // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" + print!("{:016X} ", self.instruction.ip()); + let start_index = self.instruction.ip() as usize - self.address; + let instr_bytes = &self.block[start_index..start_index + self.instruction.len()]; + for b in instr_bytes.iter() { + print!("{:02X}", b); + } + if instr_bytes.len() < 0x10 { + for _ in 0..0x10 - instr_bytes.len() { + print!(" "); + } + } + println!(" {}", output); + } + fn start(&mut self, address: usize) -> Result<(), MemoryAccessError> { + //println!("starting at {address:x}"); + self.address = address; + self.block = self.exe.memory.range_from(self.address..)?; + self.decoder = + Decoder::with_ip(64, self.block, self.address as u64, DecoderOptions::NONE); + Ok(()) + } + /// Returns true if pop was successful + fn pop(&mut self) -> Result { + Ok(if let Some(next) = self.queue.pop() { + self.start(next)?; + true + } else { + false + }) + } + } + + while ctx.decoder.can_decode() { + ctx.decoder.decode_out(&mut ctx.instruction); + + let addr = ctx.instruction.ip() as usize; + + if ctx.visited.contains(&addr) { + if ctx.pop()? { + continue; + } else { + break; + } + } else { + ctx.visited.insert(addr); + match visitor(&ctx.instruction)? { + Control::Continue => {} + Control::Break => { + if ctx.pop()? { + continue; + } else { + break; + } + } + Control::Exit => { + break; + } + } + } + + /* + if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { + //println!(); + } + ctx.print(); + if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { + //println!(); + } + */ + + match ctx.instruction.flow_control() { + FlowControl::Next => {} + FlowControl::UnconditionalBranch => { + // TODO figure out how to handle tail calls + ctx.start(ctx.instruction.near_branch_target() as usize)?; + } + //FlowControl::IndirectBranch => todo!(), + FlowControl::ConditionalBranch => { + ctx.queue + .push(ctx.instruction.near_branch_target() as usize); + } + FlowControl::Return => { + if !ctx.pop()? { + break; + } + } + //FlowControl::Call => todo!(), + //FlowControl::IndirectCall => todo!(), + //FlowControl::Interrupt => todo!(), + //FlowControl::XbeginXabortXend => todo!(), + //FlowControl::Exception => todo!(), + _ => {} + } + } + Ok(()) + } +} diff --git a/patternsleuth/src/process/external.rs b/patternsleuth_image/src/process/external.rs similarity index 100% rename from patternsleuth/src/process/external.rs rename to patternsleuth_image/src/process/external.rs diff --git a/patternsleuth/src/process/internal.rs b/patternsleuth_image/src/process/internal.rs similarity index 100% rename from patternsleuth/src/process/internal.rs rename to patternsleuth_image/src/process/internal.rs diff --git a/patternsleuth/src/process/mod.rs b/patternsleuth_image/src/process/mod.rs similarity index 100% rename from patternsleuth/src/process/mod.rs rename to patternsleuth_image/src/process/mod.rs diff --git a/patternsleuth/src/symbols.rs b/patternsleuth_image/src/symbols.rs similarity index 100% rename from patternsleuth/src/symbols.rs rename to patternsleuth_image/src/symbols.rs diff --git a/patternsleuth/src/uesym.rs b/patternsleuth_image/src/uesym.rs similarity index 100% rename from patternsleuth/src/uesym.rs rename to patternsleuth_image/src/uesym.rs diff --git a/patternsleuth_resolvers/Cargo.toml b/patternsleuth_resolvers/Cargo.toml new file mode 100644 index 0000000..c6161fb --- /dev/null +++ b/patternsleuth_resolvers/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "patternsleuth_resolvers" +repository.workspace = true +authors.workspace = true +license.workspace = true +version.workspace = true +edition.workspace = true + +[dependencies] +patternsleuth_scanner = { path = "../patternsleuth_scanner" } +anyhow = { workspace = true } +memchr = { workspace = true } +object = { workspace = true } +rayon = { workspace = true } +strum = { workspace = true } +pdb = { workspace = true, optional = true } +msvc-demangler = { workspace = true, optional = true} +iced-x86.workspace = true +futures = "0.3.30" +futures-scopes = "0.2.0" +inventory = "0.3.14" +itertools.workspace = true +serde = { workspace = true, optional = true, features = ["derive"] } +typetag = { version = "0.2.15", optional = true } +gimli = { version = "0.28.1", optional = true } +tracing = "0.1.40" +patternsleuth_image = { version = "0.1.0", path = "../patternsleuth_image" } + +[target.'cfg(target_os = "linux")'.dependencies] +libc = { version = "0.2.152", optional = true } + +[target.'cfg(windows)'.dependencies] +windows = { workspace = true, optional = true, features = [ + "Win32_Foundation", + "Win32_System_LibraryLoader", + "Win32_System_ProcessStatus", + "Win32_System_Threading", + "Win32_System_Diagnostics_Debug", +] } + +[features] +default = [] +serde-resolvers = ["dep:serde", "dep:typetag"] +symbols = ["dep:pdb", "dep:msvc-demangler"] +process-external = ["image-pe", "dep:libc", "dep:windows"] +process-internal = ["dep:libc", "dep:windows"] +image-pe = [] +image-elf = ["dep:gimli"] diff --git a/patternsleuth/src/resolvers/mod.rs b/patternsleuth_resolvers/src/lib.rs similarity index 100% rename from patternsleuth/src/resolvers/mod.rs rename to patternsleuth_resolvers/src/lib.rs diff --git a/patternsleuth/src/resolvers/unreal/aes.rs b/patternsleuth_resolvers/src/unreal/aes.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/aes.rs rename to patternsleuth_resolvers/src/unreal/aes.rs diff --git a/patternsleuth/src/resolvers/unreal/blueprint_library.rs b/patternsleuth_resolvers/src/unreal/blueprint_library.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/blueprint_library.rs rename to patternsleuth_resolvers/src/unreal/blueprint_library.rs diff --git a/patternsleuth/src/resolvers/unreal/engine_version.rs b/patternsleuth_resolvers/src/unreal/engine_version.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/engine_version.rs rename to patternsleuth_resolvers/src/unreal/engine_version.rs diff --git a/patternsleuth/src/resolvers/unreal/fname.rs b/patternsleuth_resolvers/src/unreal/fname.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/fname.rs rename to patternsleuth_resolvers/src/unreal/fname.rs diff --git a/patternsleuth/src/resolvers/unreal/ftext.rs b/patternsleuth_resolvers/src/unreal/ftext.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/ftext.rs rename to patternsleuth_resolvers/src/unreal/ftext.rs diff --git a/patternsleuth/src/resolvers/unreal/fuobject_hash_tables.rs b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/fuobject_hash_tables.rs rename to patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs diff --git a/patternsleuth/src/resolvers/unreal/game_loop.rs b/patternsleuth_resolvers/src/unreal/game_loop.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/game_loop.rs rename to patternsleuth_resolvers/src/unreal/game_loop.rs diff --git a/patternsleuth/src/resolvers/unreal/gengine.rs b/patternsleuth_resolvers/src/unreal/gengine.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/gengine.rs rename to patternsleuth_resolvers/src/unreal/gengine.rs diff --git a/patternsleuth/src/resolvers/unreal/gmalloc.rs b/patternsleuth_resolvers/src/unreal/gmalloc.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/gmalloc.rs rename to patternsleuth_resolvers/src/unreal/gmalloc.rs diff --git a/patternsleuth/src/resolvers/unreal/guobject_array.rs b/patternsleuth_resolvers/src/unreal/guobject_array.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/guobject_array.rs rename to patternsleuth_resolvers/src/unreal/guobject_array.rs diff --git a/patternsleuth/src/resolvers/unreal/kismet.rs b/patternsleuth_resolvers/src/unreal/kismet.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/kismet.rs rename to patternsleuth_resolvers/src/unreal/kismet.rs diff --git a/patternsleuth/src/resolvers/unreal/mod.rs b/patternsleuth_resolvers/src/unreal/mod.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/mod.rs rename to patternsleuth_resolvers/src/unreal/mod.rs diff --git a/patternsleuth/src/resolvers/unreal/pak.rs b/patternsleuth_resolvers/src/unreal/pak.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/pak.rs rename to patternsleuth_resolvers/src/unreal/pak.rs diff --git a/patternsleuth/src/resolvers/unreal/save_game.rs b/patternsleuth_resolvers/src/unreal/save_game.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/save_game.rs rename to patternsleuth_resolvers/src/unreal/save_game.rs diff --git a/patternsleuth/src/resolvers/unreal/static_construct_object.rs b/patternsleuth_resolvers/src/unreal/static_construct_object.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/static_construct_object.rs rename to patternsleuth_resolvers/src/unreal/static_construct_object.rs diff --git a/patternsleuth/src/resolvers/unreal/static_find_object.rs b/patternsleuth_resolvers/src/unreal/static_find_object.rs similarity index 100% rename from patternsleuth/src/resolvers/unreal/static_find_object.rs rename to patternsleuth_resolvers/src/unreal/static_find_object.rs From d0e33a52b20c995fff5a112fe4f371c969fffa31 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 29 Jan 2025 19:25:32 -0600 Subject: [PATCH 02/19] patternsleuth_resolvers compiles --- Cargo.lock | 2 - patternsleuth_image/src/lib.rs | 182 ------------------ patternsleuth_resolvers/Cargo.toml | 23 +-- patternsleuth_resolvers/src/disassemble.rs | 176 +++++++++++++++++ patternsleuth_resolvers/src/lib.rs | 70 +++---- patternsleuth_resolvers/src/unreal/aes.rs | 2 +- .../src/unreal/blueprint_library.rs | 8 +- .../src/unreal/engine_version.rs | 8 +- patternsleuth_resolvers/src/unreal/fname.rs | 12 +- patternsleuth_resolvers/src/unreal/ftext.rs | 2 +- .../src/unreal/fuobject_hash_tables.rs | 2 +- .../src/unreal/game_loop.rs | 4 +- patternsleuth_resolvers/src/unreal/gengine.rs | 2 +- patternsleuth_resolvers/src/unreal/gmalloc.rs | 2 +- .../src/unreal/guobject_array.rs | 2 +- patternsleuth_resolvers/src/unreal/kismet.rs | 8 +- patternsleuth_resolvers/src/unreal/mod.rs | 9 +- patternsleuth_resolvers/src/unreal/pak.rs | 2 +- .../src/unreal/save_game.rs | 2 +- .../src/unreal/static_construct_object.rs | 5 +- .../src/unreal/static_find_object.rs | 8 +- 21 files changed, 251 insertions(+), 280 deletions(-) create mode 100644 patternsleuth_resolvers/src/disassemble.rs diff --git a/Cargo.lock b/Cargo.lock index e8bc922..ba0fae4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2691,7 +2691,6 @@ dependencies = [ "iced-x86", "inventory", "itertools 0.12.0", - "libc", "memchr", "msvc-demangler", "object", @@ -2703,7 +2702,6 @@ dependencies = [ "strum", "tracing", "typetag", - "windows 0.52.0", ] [[package]] diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index e7024fb..2391ade 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -538,185 +538,3 @@ impl<'data> Matchable<'data> for Memory<'data> { Ok(pattern.captures(s.data(), s.address(), address - s.address())) } } - -pub mod disassemble { - use std::{collections::HashSet, ops::Range}; - - use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, NasmFormatter}; - - use crate::{Image, MemoryAccessError, MemoryTrait}; - - pub fn function_range( - exe: &Image<'_>, - address: usize, - ) -> Result, MemoryAccessError> { - let min = address; - let mut max = min; - disassemble(exe, address, |inst| { - let cur = inst.ip() as usize; - if Some(address) != exe.get_root_function(cur)?.map(|f| f.range.start) { - return Ok(Control::Break); - } - max = max.max(cur + inst.len()); - Ok(Control::Continue) - })?; - Ok(min..max) - } - - pub fn disassemble_single<'mem, 'img: 'mem>( - exe: &'img Image<'mem>, - address: usize, - ) -> Result, MemoryAccessError> { - Ok(Decoder::with_ip( - 64, - exe.memory.range_from(address..)?, - address as u64, - DecoderOptions::NONE, - ) - .iter() - .next()) - } - - pub enum Control { - Continue, - Break, - Exit, - } - - pub fn disassemble<'mem, 'img: 'mem, F>( - exe: &'img Image<'mem>, - address: usize, - mut visitor: F, - ) -> Result<(), MemoryAccessError> - where - F: FnMut(&Instruction) -> Result, - { - struct Ctx<'mem, 'img: 'mem> { - exe: &'img Image<'mem>, - queue: Vec, - visited: HashSet, - address: usize, - block: &'mem [u8], - decoder: Decoder<'mem>, - instruction: Instruction, - } - - let block = exe.memory.range_from(address..)?; - let mut ctx = Ctx { - exe, - queue: Default::default(), - visited: Default::default(), - address, - block, - decoder: Decoder::with_ip(64, block, address as u64, DecoderOptions::NONE), - instruction: Default::default(), - }; - - impl Ctx<'_, '_> { - #[allow(unused)] - fn print(&self) { - let mut formatter = NasmFormatter::new(); - //formatter.options_mut().set_digit_separator("`"); - //formatter.options_mut().set_first_operand_char_index(10); - - let mut output = String::new(); - formatter.format(&self.instruction, &mut output); - - // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" - print!("{:016X} ", self.instruction.ip()); - let start_index = self.instruction.ip() as usize - self.address; - let instr_bytes = &self.block[start_index..start_index + self.instruction.len()]; - for b in instr_bytes.iter() { - print!("{:02X}", b); - } - if instr_bytes.len() < 0x10 { - for _ in 0..0x10 - instr_bytes.len() { - print!(" "); - } - } - println!(" {}", output); - } - fn start(&mut self, address: usize) -> Result<(), MemoryAccessError> { - //println!("starting at {address:x}"); - self.address = address; - self.block = self.exe.memory.range_from(self.address..)?; - self.decoder = - Decoder::with_ip(64, self.block, self.address as u64, DecoderOptions::NONE); - Ok(()) - } - /// Returns true if pop was successful - fn pop(&mut self) -> Result { - Ok(if let Some(next) = self.queue.pop() { - self.start(next)?; - true - } else { - false - }) - } - } - - while ctx.decoder.can_decode() { - ctx.decoder.decode_out(&mut ctx.instruction); - - let addr = ctx.instruction.ip() as usize; - - if ctx.visited.contains(&addr) { - if ctx.pop()? { - continue; - } else { - break; - } - } else { - ctx.visited.insert(addr); - match visitor(&ctx.instruction)? { - Control::Continue => {} - Control::Break => { - if ctx.pop()? { - continue; - } else { - break; - } - } - Control::Exit => { - break; - } - } - } - - /* - if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { - //println!(); - } - ctx.print(); - if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { - //println!(); - } - */ - - match ctx.instruction.flow_control() { - FlowControl::Next => {} - FlowControl::UnconditionalBranch => { - // TODO figure out how to handle tail calls - ctx.start(ctx.instruction.near_branch_target() as usize)?; - } - //FlowControl::IndirectBranch => todo!(), - FlowControl::ConditionalBranch => { - ctx.queue - .push(ctx.instruction.near_branch_target() as usize); - } - FlowControl::Return => { - if !ctx.pop()? { - break; - } - } - //FlowControl::Call => todo!(), - //FlowControl::IndirectCall => todo!(), - //FlowControl::Interrupt => todo!(), - //FlowControl::XbeginXabortXend => todo!(), - //FlowControl::Exception => todo!(), - _ => {} - } - } - Ok(()) - } -} diff --git a/patternsleuth_resolvers/Cargo.toml b/patternsleuth_resolvers/Cargo.toml index c6161fb..6915428 100644 --- a/patternsleuth_resolvers/Cargo.toml +++ b/patternsleuth_resolvers/Cargo.toml @@ -26,23 +26,8 @@ gimli = { version = "0.28.1", optional = true } tracing = "0.1.40" patternsleuth_image = { version = "0.1.0", path = "../patternsleuth_image" } -[target.'cfg(target_os = "linux")'.dependencies] -libc = { version = "0.2.152", optional = true } - -[target.'cfg(windows)'.dependencies] -windows = { workspace = true, optional = true, features = [ - "Win32_Foundation", - "Win32_System_LibraryLoader", - "Win32_System_ProcessStatus", - "Win32_System_Threading", - "Win32_System_Diagnostics_Debug", -] } - [features] -default = [] -serde-resolvers = ["dep:serde", "dep:typetag"] -symbols = ["dep:pdb", "dep:msvc-demangler"] -process-external = ["image-pe", "dep:libc", "dep:windows"] -process-internal = ["dep:libc", "dep:windows"] -image-pe = [] -image-elf = ["dep:gimli"] +default = ["image-pe", "image-elf"] +serde-resolvers = ["dep:serde", "dep:typetag", "patternsleuth_image/serde-resolvers"] +image-pe = ["patternsleuth_image/image-pe"] +image-elf = ["patternsleuth_image/image-elf"] diff --git a/patternsleuth_resolvers/src/disassemble.rs b/patternsleuth_resolvers/src/disassemble.rs new file mode 100644 index 0000000..986b2fa --- /dev/null +++ b/patternsleuth_resolvers/src/disassemble.rs @@ -0,0 +1,176 @@ +use std::{collections::HashSet, ops::Range}; + +use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, NasmFormatter}; + +use crate::{Image, MemoryAccessError, MemoryTrait}; + +pub fn function_range(exe: &Image<'_>, address: usize) -> Result, MemoryAccessError> { + let min = address; + let mut max = min; + disassemble(exe, address, |inst| { + let cur = inst.ip() as usize; + if Some(address) != exe.get_root_function(cur)?.map(|f| f.range.start) { + return Ok(Control::Break); + } + max = max.max(cur + inst.len()); + Ok(Control::Continue) + })?; + Ok(min..max) +} + +pub fn disassemble_single<'mem, 'img: 'mem>( + exe: &'img Image<'mem>, + address: usize, +) -> Result, MemoryAccessError> { + Ok(Decoder::with_ip( + 64, + exe.memory.range_from(address..)?, + address as u64, + DecoderOptions::NONE, + ) + .iter() + .next()) +} + +pub enum Control { + Continue, + Break, + Exit, +} + +pub fn disassemble<'mem, 'img: 'mem, F>( + exe: &'img Image<'mem>, + address: usize, + mut visitor: F, +) -> Result<(), MemoryAccessError> +where + F: FnMut(&Instruction) -> Result, +{ + struct Ctx<'mem, 'img: 'mem> { + exe: &'img Image<'mem>, + queue: Vec, + visited: HashSet, + address: usize, + block: &'mem [u8], + decoder: Decoder<'mem>, + instruction: Instruction, + } + + let block = exe.memory.range_from(address..)?; + let mut ctx = Ctx { + exe, + queue: Default::default(), + visited: Default::default(), + address, + block, + decoder: Decoder::with_ip(64, block, address as u64, DecoderOptions::NONE), + instruction: Default::default(), + }; + + impl Ctx<'_, '_> { + #[allow(unused)] + fn print(&self) { + let mut formatter = NasmFormatter::new(); + //formatter.options_mut().set_digit_separator("`"); + //formatter.options_mut().set_first_operand_char_index(10); + + let mut output = String::new(); + formatter.format(&self.instruction, &mut output); + + // Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]" + print!("{:016X} ", self.instruction.ip()); + let start_index = self.instruction.ip() as usize - self.address; + let instr_bytes = &self.block[start_index..start_index + self.instruction.len()]; + for b in instr_bytes.iter() { + print!("{:02X}", b); + } + if instr_bytes.len() < 0x10 { + for _ in 0..0x10 - instr_bytes.len() { + print!(" "); + } + } + println!(" {}", output); + } + fn start(&mut self, address: usize) -> Result<(), MemoryAccessError> { + //println!("starting at {address:x}"); + self.address = address; + self.block = self.exe.memory.range_from(self.address..)?; + self.decoder = + Decoder::with_ip(64, self.block, self.address as u64, DecoderOptions::NONE); + Ok(()) + } + /// Returns true if pop was successful + fn pop(&mut self) -> Result { + Ok(if let Some(next) = self.queue.pop() { + self.start(next)?; + true + } else { + false + }) + } + } + + while ctx.decoder.can_decode() { + ctx.decoder.decode_out(&mut ctx.instruction); + + let addr = ctx.instruction.ip() as usize; + + if ctx.visited.contains(&addr) { + if ctx.pop()? { + continue; + } else { + break; + } + } else { + ctx.visited.insert(addr); + match visitor(&ctx.instruction)? { + Control::Continue => {} + Control::Break => { + if ctx.pop()? { + continue; + } else { + break; + } + } + Control::Exit => { + break; + } + } + } + + /* + if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { + //println!(); + } + ctx.print(); + if !matches!(ctx.instruction.flow_control(), FlowControl::Next) { + //println!(); + } + */ + + match ctx.instruction.flow_control() { + FlowControl::Next => {} + FlowControl::UnconditionalBranch => { + // TODO figure out how to handle tail calls + ctx.start(ctx.instruction.near_branch_target() as usize)?; + } + //FlowControl::IndirectBranch => todo!(), + FlowControl::ConditionalBranch => { + ctx.queue + .push(ctx.instruction.near_branch_target() as usize); + } + FlowControl::Return => { + if !ctx.pop()? { + break; + } + } + //FlowControl::Call => todo!(), + //FlowControl::IndirectCall => todo!(), + //FlowControl::Interrupt => todo!(), + //FlowControl::XbeginXabortXend => todo!(), + //FlowControl::Exception => todo!(), + _ => {} + } + } + Ok(()) +} diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index a4d9a62..e47e167 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -1,6 +1,10 @@ +pub mod disassemble; pub mod unreal; -use crate::{Image, MemoryAccessError}; +use image::Image; +use patternsleuth_image::image; +use patternsleuth_image::{MemoryAccessError, MemoryTrait}; + use futures::{ channel::oneshot, executor::LocalPool, @@ -84,7 +88,7 @@ impl From for ResolveError { #[macro_export] macro_rules! _bail_out { ($msg:expr) => { - return Err($crate::resolvers::ResolveError::Msg($msg.into())); + return Err($crate::ResolveError::Msg($msg.into())); }; } pub use _bail_out as bail_out; @@ -189,7 +193,7 @@ macro_rules! _matcharm_generator { let img = &$ctx.image().image_type; let mut res = None; $( - $crate::resolvers::cfg_image::$img_ident! { + $crate::cfg_image::$img_ident! { if matches!(img, $crate::image::$enum_name_it::$img_ident(_)) { res = Some($name::$img_ident($ctx).await); } @@ -222,7 +226,7 @@ macro_rules! _impl_resolver { (all, $name:ident, |$ctx:ident| async $x:block ) => { $crate::_impl_resolver_inner!($name, |$ctx| async $x); - impl $crate::resolvers::Singleton for $name { + impl $crate::Singleton for $name { fn get(&self) -> Option { None } @@ -230,10 +234,10 @@ macro_rules! _impl_resolver { }; ($arch:ident, $name:ident, |$ctx:ident| async $x:block ) => { - $crate::resolvers::cfg_image::$arch! { - impl $name where $name: $crate::resolvers::PleaseAddCollectForMe { + $crate::cfg_image::$arch! { + impl $name where $name: $crate::PleaseAddCollectForMe { #[allow(non_snake_case)] - pub async fn $arch($ctx: &$crate::resolvers::AsyncContext<'_>) -> $crate::resolvers::Result<$name> $x + pub async fn $arch($ctx: &$crate::AsyncContext<'_>) -> $crate::Result<$name> $x } } }; @@ -243,17 +247,17 @@ macro_rules! _impl_resolver { $crate::image::image_type_reflection!(all, impl_resolver; generate; {ctx, $name}) }); - impl $crate::resolvers::Singleton for $name { + impl $crate::Singleton for $name { fn get(&self) -> Option { None } } - impl $crate::resolvers::PleaseAddCollectForMe for $name {} + impl $crate::PleaseAddCollectForMe for $name {} }; (generate, $enum_name_it:ident { $( $img_ident:ident( $img_ty:ty, $img_feature:literal )),* $(,)? }, {$ctx:ident, $name:ident}) => { - $crate::resolvers::matcharm_generator!( + $crate::matcharm_generator!( $enum_name_it { $( $img_ident( $img_ty, $img_feature )),* }, { $ctx, $name } ) @@ -270,7 +274,7 @@ macro_rules! _impl_resolver_singleton { $x }); - impl $crate::resolvers::Singleton for $name { + impl $crate::Singleton for $name { fn get(&self) -> Option { Some(self.0) } @@ -278,10 +282,10 @@ macro_rules! _impl_resolver_singleton { }; ($arch:ident, $name:ident, |$ctx:ident| async $x:block ) => { - $crate::resolvers::cfg_image::$arch! { - impl $name where $name: $crate::resolvers::PleaseAddCollectForMe { + $crate::cfg_image::$arch! { + impl $name where $name: $crate::PleaseAddCollectForMe { #[allow(non_snake_case)] - async fn $arch($ctx: &$crate::resolvers::AsyncContext<'_>) -> $crate::resolvers::Result<$name> $x + async fn $arch($ctx: &$crate::AsyncContext<'_>) -> $crate::Result<$name> $x } } }; @@ -294,17 +298,17 @@ macro_rules! _impl_resolver_singleton { $crate::image::image_type_reflection!(all, impl_resolver_singleton; generate; {ctx, $name}) }); - impl $crate::resolvers::Singleton for $name { + impl $crate::Singleton for $name { fn get(&self) -> Option { Some(self.0) } } - impl $crate::resolvers::PleaseAddCollectForMe for $name {} + impl $crate::PleaseAddCollectForMe for $name {} }; (generate, $enum_name_it:ident { $( $img_ident:ident( $img_ty:ty, $img_feature:literal )),* $(,)? }, {$ctx:ident, $name:ident}) => { - $crate::resolvers::matcharm_generator!( + $crate::matcharm_generator!( $enum_name_it { $( $img_ident( $img_ty, $img_feature )),* }, { $ctx, $name } ) @@ -313,30 +317,30 @@ macro_rules! _impl_resolver_singleton { #[macro_export] macro_rules! _impl_resolver_inner { ( $name:ident, |$ctx:ident| async $x:block ) => { - $crate::resolvers::inventory::submit! { - $crate::resolvers::NamedResolver { name: stringify!($name), getter: $name::dyn_resolver } + $crate::inventory::submit! { + $crate::NamedResolver { name: stringify!($name), getter: $name::dyn_resolver } } - #[cfg_attr(feature = "serde-resolvers", $crate::resolvers::typetag::serde)] - impl $crate::resolvers::Resolution for $name {} + #[cfg_attr(feature = "serde-resolvers", $crate::typetag::serde)] + impl $crate::Resolution for $name {} impl $name { - pub fn resolver() -> &'static $crate::resolvers::ResolverFactory<$name> { - static GLOBAL: ::std::sync::OnceLock<&$crate::resolvers::ResolverFactory<$name>> = ::std::sync::OnceLock::new(); + pub fn resolver() -> &'static $crate::ResolverFactory<$name> { + static GLOBAL: ::std::sync::OnceLock<&$crate::ResolverFactory<$name>> = ::std::sync::OnceLock::new(); - GLOBAL.get_or_init(|| &$crate::resolvers::ResolverFactory { - factory: |$ctx: &$crate::resolvers::AsyncContext| -> $crate::resolvers::futures::future::BoxFuture<$crate::resolvers::Result<$name>> { + GLOBAL.get_or_init(|| &$crate::ResolverFactory { + factory: |$ctx: &$crate::AsyncContext| -> $crate::futures::future::BoxFuture<$crate::Result<$name>> { Box::pin(async $x) }, }) } - pub fn dyn_resolver() -> &'static $crate::resolvers::DynResolverFactory { - static GLOBAL: ::std::sync::OnceLock<&$crate::resolvers::DynResolverFactory> = ::std::sync::OnceLock::new(); + pub fn dyn_resolver() -> &'static $crate::DynResolverFactory { + static GLOBAL: ::std::sync::OnceLock<&$crate::DynResolverFactory> = ::std::sync::OnceLock::new(); - GLOBAL.get_or_init(|| &$crate::resolvers::DynResolverFactory { - factory: |$ctx: &$crate::resolvers::AsyncContext| -> $crate::resolvers::futures::future::BoxFuture<$crate::resolvers::Result<::std::sync::Arc>> { + GLOBAL.get_or_init(|| &$crate::DynResolverFactory { + factory: |$ctx: &$crate::AsyncContext| -> $crate::futures::future::BoxFuture<$crate::Result<::std::sync::Arc>> { Box::pin(async { - $ctx.resolve(Self::resolver()).await.map(|ok| -> ::std::sync::Arc { ok }) + $ctx.resolve(Self::resolver()).await.map(|ok| -> ::std::sync::Arc { ok }) }) }, }) @@ -368,7 +372,7 @@ macro_rules! _impl_try_collector { #[allow(non_snake_case)] let ( $( $member_name, )* - ) = $crate::resolvers::futures::try_join!( + ) = $crate::futures::try_join!( $( ctx.resolve($resolver::resolver()), )* )?; Ok($struct_name { @@ -394,14 +398,14 @@ macro_rules! _impl_collector { $struct_vis struct $struct_name { $( $(#[$inner $($args)*])* - $member_vis $member_name: $crate::resolvers::Result<::std::sync::Arc<$resolver>>, + $member_vis $member_name: $crate::Result<::std::sync::Arc<$resolver>>, )* } $crate::_impl_resolver!(all, $struct_name, |ctx| async { #[allow(non_snake_case)] let ( $( $member_name, )* - ) = $crate::resolvers::futures::join!( + ) = $crate::futures::join!( $( ctx.resolve($resolver::resolver()), )* ); Ok($struct_name { diff --git a/patternsleuth_resolvers/src/unreal/aes.rs b/patternsleuth_resolvers/src/unreal/aes.rs index bdae28c..21d134c 100644 --- a/patternsleuth_resolvers/src/unreal/aes.rs +++ b/patternsleuth_resolvers/src/unreal/aes.rs @@ -5,7 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - resolvers::{impl_resolver, Result}, + {impl_resolver, Result}, MemoryTrait, }; diff --git a/patternsleuth_resolvers/src/unreal/blueprint_library.rs b/patternsleuth_resolvers/src/unreal/blueprint_library.rs index 654136e..bbc747f 100644 --- a/patternsleuth_resolvers/src/unreal/blueprint_library.rs +++ b/patternsleuth_resolvers/src/unreal/blueprint_library.rs @@ -2,12 +2,10 @@ use std::fmt::Debug; use futures::{future::join_all, join}; +use patternsleuth_image::{Addressable as _, Matchable as _}; use patternsleuth_scanner::Pattern; -use crate::{ - resolvers::{ensure_one, impl_resolver, impl_resolver_singleton, Context}, - Addressable, Matchable, -}; +use crate::{ensure_one, impl_resolver, impl_resolver_singleton, Context}; #[derive(Debug, PartialEq)] #[cfg_attr( @@ -181,7 +179,7 @@ impl_resolver!(all, BlueprintLibraryInit, |ctx| async { pub struct UFunctionBind(pub usize); impl_resolver_singleton!(collect, UFunctionBind); impl_resolver_singleton!(PEImage, UFunctionBind, |ctx| async { - use crate::resolvers::unreal::util; + use crate::unreal::util; let string = async { let strings = ctx diff --git a/patternsleuth_resolvers/src/unreal/engine_version.rs b/patternsleuth_resolvers/src/unreal/engine_version.rs index 0a40f05..19ff4b1 100644 --- a/patternsleuth_resolvers/src/unreal/engine_version.rs +++ b/patternsleuth_resolvers/src/unreal/engine_version.rs @@ -3,11 +3,11 @@ use std::fmt::{Debug, Display}; use futures::future::join_all; use itertools::Itertools; +use patternsleuth_image::{Addressable as _, Matchable as _}; use patternsleuth_scanner::Pattern; use crate::{ - resolvers::{bail_out, impl_resolver, try_ensure_one}, - MemoryTrait, + MemoryTrait, {bail_out, impl_resolver, try_ensure_one}, }; #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -86,7 +86,7 @@ pub struct EngineVersionStrings { impl_resolver!(collect, EngineVersionStrings); // "++UE5+Release-{}.{}" impl_resolver!(ElfImage, EngineVersionStrings, |ctx| async { - use crate::resolvers::{ensure_one, unreal::util}; + use crate::{ensure_one, unreal::util}; let pattern_name = util::utf16_pattern("++UE5+Release-"); let name_scan = ctx.scan(pattern_name).await; @@ -137,7 +137,7 @@ impl_resolver!(ElfImage, EngineVersionStrings, |ctx| async { }); impl_resolver!(PEImage, EngineVersionStrings, |ctx| async { - use crate::{Addressable, Matchable, MemoryTrait}; + use crate::MemoryTrait; use std::collections::HashSet; let patterns = [ diff --git a/patternsleuth_resolvers/src/unreal/fname.rs b/patternsleuth_resolvers/src/unreal/fname.rs index 3b372b2..2283325 100644 --- a/patternsleuth_resolvers/src/unreal/fname.rs +++ b/patternsleuth_resolvers/src/unreal/fname.rs @@ -2,12 +2,10 @@ use std::fmt::Debug; use futures::future::join_all; +use patternsleuth_image::MemoryTrait as _; use patternsleuth_scanner::Pattern; -use crate::{ - resolvers::{ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, - MemoryTrait, -}; +use crate::{ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}; /// public: __cdecl FName::FName(wchar_t const *, enum EFindName) #[derive(Debug, PartialEq)] @@ -29,7 +27,7 @@ FEngineLoop::LoadPreInitModules: RenderCore */ impl_resolver_singleton!(ElfImage, FNameCtorWchar, |ctx| async { - use crate::resolvers::ResolveError; + use crate::ResolveError; use std::collections::HashSet; let strings = [ @@ -116,7 +114,7 @@ impl_resolver_singleton!(ElfImage, FNameCtorWchar, |ctx| async { }); impl_resolver_singleton!(PEImage, FNameCtorWchar, |ctx| async { - use crate::{resolvers::Context, MemoryTrait}; + use crate::{Context, MemoryTrait}; use futures::join; use iced_x86::{Code, Decoder, DecoderOptions}; @@ -219,7 +217,7 @@ impl_resolver_singleton!(ElfImage, FNameToString, |ctx| async { }); impl_resolver_singleton!(PEImage, FNameToString, |ctx| async { - use crate::{resolvers::Context, MemoryTrait}; + use crate::{Context, MemoryTrait}; use futures::join; use iced_x86::{Code, Decoder, DecoderOptions}; diff --git a/patternsleuth_resolvers/src/unreal/ftext.rs b/patternsleuth_resolvers/src/unreal/ftext.rs index b73e183..5b57efa 100644 --- a/patternsleuth_resolvers/src/unreal/ftext.rs +++ b/patternsleuth_resolvers/src/unreal/ftext.rs @@ -5,7 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - resolvers::{impl_resolver_singleton, try_ensure_one}, + {impl_resolver_singleton, try_ensure_one}, MemoryTrait, }; diff --git a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs index 87e9c27..9366593 100644 --- a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs +++ b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs @@ -5,7 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - resolvers::{impl_resolver_singleton, try_ensure_one, Result}, + {impl_resolver_singleton, try_ensure_one, Result}, MemoryTrait, }; diff --git a/patternsleuth_resolvers/src/unreal/game_loop.rs b/patternsleuth_resolvers/src/unreal/game_loop.rs index 9a10083..2f0e516 100644 --- a/patternsleuth_resolvers/src/unreal/game_loop.rs +++ b/patternsleuth_resolvers/src/unreal/game_loop.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use futures::future::join_all; -use crate::resolvers::{ensure_one, impl_resolver_singleton, unreal::util}; +use crate::{ensure_one, impl_resolver_singleton, unreal::util}; #[derive(Debug, PartialEq)] #[cfg_attr( @@ -52,7 +52,7 @@ pub struct UGameEngineTick(pub usize); impl_resolver_singleton!(collect, UGameEngineTick); impl_resolver_singleton!(PEImage, UGameEngineTick, |ctx| async { - use crate::resolvers::Result; + use crate::Result; use patternsleuth_scanner::Pattern; let strings = ctx diff --git a/patternsleuth_resolvers/src/unreal/gengine.rs b/patternsleuth_resolvers/src/unreal/gengine.rs index a328d61..c664081 100644 --- a/patternsleuth_resolvers/src/unreal/gengine.rs +++ b/patternsleuth_resolvers/src/unreal/gengine.rs @@ -6,7 +6,7 @@ use itertools::Itertools as _; use crate::{ disassemble::{disassemble, Control}, image::Image, - resolvers::{impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, }; #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/gmalloc.rs b/patternsleuth_resolvers/src/unreal/gmalloc.rs index 78f9c1d..17089d2 100644 --- a/patternsleuth_resolvers/src/unreal/gmalloc.rs +++ b/patternsleuth_resolvers/src/unreal/gmalloc.rs @@ -7,7 +7,7 @@ use patternsleuth_scanner::Pattern; use crate::{ disassemble::{disassemble, Control}, - resolvers::{impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, MemoryTrait, }; diff --git a/patternsleuth_resolvers/src/unreal/guobject_array.rs b/patternsleuth_resolvers/src/unreal/guobject_array.rs index 406a704..e3c4902 100644 --- a/patternsleuth_resolvers/src/unreal/guobject_array.rs +++ b/patternsleuth_resolvers/src/unreal/guobject_array.rs @@ -5,7 +5,7 @@ use futures::{future::join_all, try_join}; use patternsleuth_scanner::Pattern; use crate::{ - resolvers::{ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, MemoryTrait, }; diff --git a/patternsleuth_resolvers/src/unreal/kismet.rs b/patternsleuth_resolvers/src/unreal/kismet.rs index 4738db4..2220d19 100644 --- a/patternsleuth_resolvers/src/unreal/kismet.rs +++ b/patternsleuth_resolvers/src/unreal/kismet.rs @@ -2,14 +2,10 @@ use std::fmt::Debug; use futures::future::join_all; use iced_x86::{Decoder, DecoderOptions, Instruction}; +use patternsleuth_image::{Addressable as _, Matchable as _, MemoryTrait as _}; use patternsleuth_scanner::Pattern; -use crate::{ - resolvers::{ - bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result, - }, - Addressable, Matchable, MemoryTrait, -}; +use crate::{bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result}; /// public: void __cdecl UObject::SkipFunction(struct FFrame &, void *const, class UFunction *) #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/mod.rs b/patternsleuth_resolvers/src/unreal/mod.rs index d5c5cbb..a8d3576 100644 --- a/patternsleuth_resolvers/src/unreal/mod.rs +++ b/patternsleuth_resolvers/src/unreal/mod.rs @@ -22,19 +22,18 @@ use std::{ use futures::future::join_all; use iced_x86::FlowControl; use itertools::Itertools; +use patternsleuth_image::{Addressable as _, Matchable as _}; use patternsleuth_scanner::Pattern; use crate::{ disassemble::{disassemble, Control}, - resolvers::{ - bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result, - }, - Addressable, Image, Matchable, MemoryTrait, + Image, MemoryTrait, + {bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result}, }; #[allow(unused)] pub mod util { - use crate::resolvers::AsyncContext; + use crate::AsyncContext; use super::*; diff --git a/patternsleuth_resolvers/src/unreal/pak.rs b/patternsleuth_resolvers/src/unreal/pak.rs index b58feba..351a6e1 100644 --- a/patternsleuth_resolvers/src/unreal/pak.rs +++ b/patternsleuth_resolvers/src/unreal/pak.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use futures::future::join_all; -use crate::resolvers::{ensure_one, impl_resolver_singleton, unreal::util}; +use crate::{ensure_one, impl_resolver_singleton, unreal::util}; #[derive(Debug, PartialEq)] #[cfg_attr( diff --git a/patternsleuth_resolvers/src/unreal/save_game.rs b/patternsleuth_resolvers/src/unreal/save_game.rs index 8a6bdf7..4e78a75 100644 --- a/patternsleuth_resolvers/src/unreal/save_game.rs +++ b/patternsleuth_resolvers/src/unreal/save_game.rs @@ -4,7 +4,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; -use crate::resolvers::{ensure_one, impl_resolver_singleton}; +use crate::{ensure_one, impl_resolver_singleton}; /// public: static bool __cdecl UGameplayStatics::SaveGameToMemory(class USaveGame *, class TArray > &) #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/static_construct_object.rs b/patternsleuth_resolvers/src/unreal/static_construct_object.rs index 6b9fca3..1965b1e 100644 --- a/patternsleuth_resolvers/src/unreal/static_construct_object.rs +++ b/patternsleuth_resolvers/src/unreal/static_construct_object.rs @@ -5,8 +5,8 @@ use patternsleuth_scanner::Pattern; use crate::{ disassemble::{disassemble, Control}, - resolvers::{ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, MemoryTrait, + {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, }; /// class UObject * __cdecl StaticConstructObject_Internal(struct FStaticConstructObjectParameters const &) @@ -125,8 +125,7 @@ impl_resolver_singleton!(PEImage, StaticConstructObjectInternalString, |ctx| asy use crate::{ disassemble::disassemble_single, - resolvers::{bail_out, Context, Result}, - Image, MemoryTrait, + Image, MemoryTrait, {bail_out, Context, Result}, }; let strings = join_all( diff --git a/patternsleuth_resolvers/src/unreal/static_find_object.rs b/patternsleuth_resolvers/src/unreal/static_find_object.rs index d0eea5c..4a05a54 100644 --- a/patternsleuth_resolvers/src/unreal/static_find_object.rs +++ b/patternsleuth_resolvers/src/unreal/static_find_object.rs @@ -1,9 +1,9 @@ -use crate::resolvers::{ensure_one, impl_resolver_singleton, unreal::util}; +use crate::{ensure_one, impl_resolver_singleton, unreal::util}; /// ``` -/// class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, bool, enum EObjectFlags, enum EInternalObjectFlags) -/// class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, enum EObjectFlags, enum EInternalObjectFlags) -/// class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, bool, enum EObjectFlags) +/// //class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, bool, enum EObjectFlags, enum EInternalObjectFlags) +/// //class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, enum EObjectFlags, enum EInternalObjectFlags) +/// //class UObject * __cdecl StaticFindObjectFast(class UClass *, class UObject *, class FName, bool, bool, enum EObjectFlags) /// ``` #[derive(Debug, PartialEq)] #[cfg_attr( From f21cab0787b505c37e8ac844cbc60c73395c93da Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 29 Jan 2025 19:34:45 -0600 Subject: [PATCH 03/19] clean up deps --- Cargo.lock | 17 ----------------- patternsleuth/src/lib.rs | 2 +- patternsleuth_image/Cargo.toml | 11 +---------- patternsleuth_resolvers/Cargo.toml | 10 +--------- patternsleuth_resolvers/src/unreal/aes.rs | 3 +-- patternsleuth_resolvers/src/unreal/ftext.rs | 3 +-- .../src/unreal/fuobject_hash_tables.rs | 3 +-- patternsleuth_resolvers/src/unreal/gmalloc.rs | 2 +- .../src/unreal/guobject_array.rs | 2 +- 9 files changed, 8 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba0fae4..0e7e9c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2660,23 +2660,14 @@ name = "patternsleuth_image" version = "0.1.0" dependencies = [ "anyhow", - "futures", - "futures-scopes", "gimli", - "iced-x86", - "inventory", "itertools 0.12.0", "libc", - "memchr", "msvc-demangler", "object", "patternsleuth_scanner", "pdb", - "rayon", "serde", - "strum", - "tracing", - "typetag", "windows 0.52.0", ] @@ -2684,22 +2675,14 @@ dependencies = [ name = "patternsleuth_resolvers" version = "0.1.0" dependencies = [ - "anyhow", "futures", "futures-scopes", - "gimli", "iced-x86", "inventory", "itertools 0.12.0", - "memchr", - "msvc-demangler", - "object", "patternsleuth_image", "patternsleuth_scanner", - "pdb", - "rayon", "serde", - "strum", "tracing", "typetag", ] diff --git a/patternsleuth/src/lib.rs b/patternsleuth/src/lib.rs index 4700540..1aa7100 100644 --- a/patternsleuth/src/lib.rs +++ b/patternsleuth/src/lib.rs @@ -1,2 +1,2 @@ +pub use patternsleuth_image::process; pub use patternsleuth_scanner as scanner; -pub use patternsleuth_image::process as process; diff --git a/patternsleuth_image/Cargo.toml b/patternsleuth_image/Cargo.toml index 3591a1e..f851bf5 100644 --- a/patternsleuth_image/Cargo.toml +++ b/patternsleuth_image/Cargo.toml @@ -9,21 +9,12 @@ edition.workspace = true [dependencies] patternsleuth_scanner = { path = "../patternsleuth_scanner" } anyhow = { workspace = true } -memchr = { workspace = true } object = { workspace = true } -rayon = { workspace = true } -strum = { workspace = true } pdb = { workspace = true, optional = true } msvc-demangler = { workspace = true, optional = true} -iced-x86.workspace = true -futures = "0.3.30" -futures-scopes = "0.2.0" -inventory = "0.3.14" itertools.workspace = true serde = { workspace = true, optional = true, features = ["derive"] } -typetag = { version = "0.2.15", optional = true } gimli = { version = "0.28.1", optional = true } -tracing = "0.1.40" [target.'cfg(target_os = "linux")'.dependencies] libc = { version = "0.2.152", optional = true } @@ -39,7 +30,7 @@ windows = { workspace = true, optional = true, features = [ [features] default = [] -serde-resolvers = ["dep:serde", "dep:typetag"] +serde-resolvers = ["dep:serde"] symbols = ["dep:pdb", "dep:msvc-demangler"] process-external = ["image-pe", "dep:libc", "dep:windows"] process-internal = ["dep:libc", "dep:windows"] diff --git a/patternsleuth_resolvers/Cargo.toml b/patternsleuth_resolvers/Cargo.toml index 6915428..42fbbea 100644 --- a/patternsleuth_resolvers/Cargo.toml +++ b/patternsleuth_resolvers/Cargo.toml @@ -8,13 +8,7 @@ edition.workspace = true [dependencies] patternsleuth_scanner = { path = "../patternsleuth_scanner" } -anyhow = { workspace = true } -memchr = { workspace = true } -object = { workspace = true } -rayon = { workspace = true } -strum = { workspace = true } -pdb = { workspace = true, optional = true } -msvc-demangler = { workspace = true, optional = true} +patternsleuth_image = { path = "../patternsleuth_image" } iced-x86.workspace = true futures = "0.3.30" futures-scopes = "0.2.0" @@ -22,9 +16,7 @@ inventory = "0.3.14" itertools.workspace = true serde = { workspace = true, optional = true, features = ["derive"] } typetag = { version = "0.2.15", optional = true } -gimli = { version = "0.28.1", optional = true } tracing = "0.1.40" -patternsleuth_image = { version = "0.1.0", path = "../patternsleuth_image" } [features] default = ["image-pe", "image-elf"] diff --git a/patternsleuth_resolvers/src/unreal/aes.rs b/patternsleuth_resolvers/src/unreal/aes.rs index 21d134c..8d23866 100644 --- a/patternsleuth_resolvers/src/unreal/aes.rs +++ b/patternsleuth_resolvers/src/unreal/aes.rs @@ -5,8 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - {impl_resolver, Result}, - MemoryTrait, + MemoryTrait, {impl_resolver, Result}, }; #[derive(PartialEq, Eq, PartialOrd, Ord)] diff --git a/patternsleuth_resolvers/src/unreal/ftext.rs b/patternsleuth_resolvers/src/unreal/ftext.rs index 5b57efa..b72fece 100644 --- a/patternsleuth_resolvers/src/unreal/ftext.rs +++ b/patternsleuth_resolvers/src/unreal/ftext.rs @@ -5,8 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - {impl_resolver_singleton, try_ensure_one}, - MemoryTrait, + MemoryTrait, {impl_resolver_singleton, try_ensure_one}, }; /// private: __cdecl FText::FText(class FString &&) diff --git a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs index 9366593..76e628e 100644 --- a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs +++ b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs @@ -5,8 +5,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; use crate::{ - {impl_resolver_singleton, try_ensure_one, Result}, - MemoryTrait, + MemoryTrait, {impl_resolver_singleton, try_ensure_one, Result}, }; /// public: static class FUObjectHashTables & __cdecl FUObjectHashTables::Get(void) diff --git a/patternsleuth_resolvers/src/unreal/gmalloc.rs b/patternsleuth_resolvers/src/unreal/gmalloc.rs index 17089d2..706c4ab 100644 --- a/patternsleuth_resolvers/src/unreal/gmalloc.rs +++ b/patternsleuth_resolvers/src/unreal/gmalloc.rs @@ -7,8 +7,8 @@ use patternsleuth_scanner::Pattern; use crate::{ disassemble::{disassemble, Control}, - {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, MemoryTrait, + {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, }; #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/guobject_array.rs b/patternsleuth_resolvers/src/unreal/guobject_array.rs index e3c4902..b3facec 100644 --- a/patternsleuth_resolvers/src/unreal/guobject_array.rs +++ b/patternsleuth_resolvers/src/unreal/guobject_array.rs @@ -5,8 +5,8 @@ use futures::{future::join_all, try_join}; use patternsleuth_scanner::Pattern; use crate::{ - {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, MemoryTrait, + {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, }; #[derive(Debug, PartialEq)] From 9f768a1b11ebd6c379a9d640791118c5f930dd26 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 29 Jan 2025 19:41:01 -0600 Subject: [PATCH 04/19] patternsleuth_cli compiles --- Cargo.lock | 4 +++- patternsleuth_cli/Cargo.toml | 4 +++- patternsleuth_cli/src/db.rs | 7 ++++--- patternsleuth_cli/src/disassemble.rs | 2 +- patternsleuth_cli/src/main.rs | 19 ++++++++++--------- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e7e9c1..743fdfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2641,7 +2641,9 @@ dependencies = [ "msvc-demangler", "object", "patricia_tree", - "patternsleuth", + "patternsleuth_image", + "patternsleuth_resolvers", + "patternsleuth_scanner", "pdb", "prettytable-rs", "rayon", diff --git a/patternsleuth_cli/Cargo.toml b/patternsleuth_cli/Cargo.toml index 9a66e29..9ab3627 100644 --- a/patternsleuth_cli/Cargo.toml +++ b/patternsleuth_cli/Cargo.toml @@ -11,7 +11,9 @@ name = "patternsleuth" path = "src/main.rs" [dependencies] -patternsleuth = { path = "../patternsleuth", features = ["process-external", "symbols", "serde-resolvers", "image-pe", "image-elf"] } +patternsleuth_image = { path = "../patternsleuth_image", features = ["process-external", "symbols"] } +patternsleuth_resolvers = { path = "../patternsleuth_resolvers", features = ["serde-resolvers", "image-pe", "image-elf"] } +patternsleuth_scanner = { path = "../patternsleuth_scanner" } anyhow = { workspace = true } clap = { workspace = true } colored = { workspace = true } diff --git a/patternsleuth_cli/src/db.rs b/patternsleuth_cli/src/db.rs index d8859f3..8c4805d 100644 --- a/patternsleuth_cli/src/db.rs +++ b/patternsleuth_cli/src/db.rs @@ -6,7 +6,8 @@ use std::{ use anyhow::Result; use itertools::Itertools; -use patternsleuth::{image::Image, scanner::Pattern, PatternConfig}; +use patternsleuth_image::{image::Image, scanner::Pattern, PatternConfig}; +use patternsleuth_resolvers::resolve_many; use prettytable::{Cell, Row, Table}; use rayon::prelude::*; use rusqlite::{Connection, OptionalExtension}; @@ -297,11 +298,11 @@ pub(crate) fn view(command: CommandViewSymbol) -> Result<()> { games.insert(name.to_string()); - let resolution = exe.resolve_many(&resolvers); + let resolution = resolve_many(&exe, &resolvers); println!("{resolution:#x?}"); for res in resolution.into_iter().flatten() { let start = res.get().unwrap(); - let bounds = patternsleuth::disassemble::function_range(&exe, start)?; + let bounds = patternsleuth_resolvers::disassemble::function_range(&exe, start)?; functions.push(Function { game: exe_path.to_string_lossy().to_string(), address: start, diff --git a/patternsleuth_cli/src/disassemble.rs b/patternsleuth_cli/src/disassemble.rs index 860481b..fa73d41 100644 --- a/patternsleuth_cli/src/disassemble.rs +++ b/patternsleuth_cli/src/disassemble.rs @@ -4,7 +4,7 @@ use colored::{ColoredString, Colorize}; use iced_x86::{ Decoder, DecoderOptions, Formatter, FormatterOutput, FormatterTextKind, IntelFormatter, OpKind, }; -use patternsleuth::{image::Image, scanner::Pattern, MemoryTrait}; +use patternsleuth_image::{image::Image, scanner::Pattern, MemoryTrait}; #[derive(Default)] struct Output { diff --git a/patternsleuth_cli/src/main.rs b/patternsleuth_cli/src/main.rs index 7ca5d4b..2a1f74e 100644 --- a/patternsleuth_cli/src/main.rs +++ b/patternsleuth_cli/src/main.rs @@ -15,12 +15,13 @@ use clap::Parser; use indicatif::ProgressBar; use itertools::Itertools; use patricia_tree::StringPatriciaMap; -use patternsleuth::image::Image; -use patternsleuth::resolvers::{resolvers, NamedResolver}; +use patternsleuth_image::image::Image; +use patternsleuth_image::{PatternConfig, Resolution}; +use patternsleuth_resolvers::{resolve_many, resolvers, NamedResolver}; -use patternsleuth::scanner::Xref; -use patternsleuth::symbols::Symbol; -use patternsleuth::{scanner::Pattern, PatternConfig, Resolution}; +use patternsleuth_image::symbols::Symbol; +use patternsleuth_scanner::Pattern; +use patternsleuth_scanner::Xref; #[derive(Parser)] enum Commands { @@ -357,7 +358,7 @@ fn scan(command: CommandScan) -> Result<()> { ( Cow::Owned(format!("PID={pid}")), - patternsleuth::process::external::read_image_from_pid(*pid)?, + patternsleuth_image::process::external::read_image_from_pid(*pid)?, ) } }; @@ -501,7 +502,7 @@ fn scan(command: CommandScan) -> Result<()> { }; let resolution = tracing::info_span!("scan", game = game_name) - .in_scope(|| exe.resolve_many(&dyn_resolvers)); + .in_scope(|| resolve_many(&exe, &dyn_resolvers)); for (resolver, resolution) in resolvers.iter().zip(&resolution) { table.add_row(Row::new( @@ -694,7 +695,7 @@ fn report(command: CommandReport) -> Result<()> { } }; - let resolution = exe.resolve_many(&resolvers); + let resolution = resolve_many(&exe, &resolvers); let map = command .resolver @@ -734,7 +735,7 @@ fn report(command: CommandReport) -> Result<()> { } fn diff_report(command: CommandDiffReport) -> Result<()> { use colored::Colorize; - use patternsleuth::resolvers::{Resolution, ResolveError}; + use patternsleuth_resolvers::{Resolution, ResolveError}; use prettytable::{Cell, Row, Table}; type Report = BTreeMap, ResolveError>>>; From 844028263b6d22441f30d5212bd4b77b1a1f1f8c Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 29 Jan 2025 19:46:12 -0600 Subject: [PATCH 05/19] clean up more deps and remove patternsleuth crate --- Cargo.lock | 75 ++++++------------------------------ Cargo.toml | 1 - examples/dll_hook/Cargo.toml | 2 +- patternsleuth/Cargo.toml | 48 ----------------------- patternsleuth/src/lib.rs | 2 - patternsleuth_cli/Cargo.toml | 5 --- 6 files changed, 12 insertions(+), 121 deletions(-) delete mode 100644 patternsleuth/Cargo.toml delete mode 100644 patternsleuth/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 743fdfa..e5e8b70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ "indexmap 2.1.0", "itertools 0.12.0", "parking_lot 0.12.1", - "patternsleuth", + "patternsleuth_image", "regex", "retour", "simple-log", @@ -2598,32 +2598,6 @@ dependencies = [ "bitflags 2.4.1", ] -[[package]] -name = "patternsleuth" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures", - "futures-scopes", - "gimli", - "iced-x86", - "inventory", - "itertools 0.12.0", - "libc", - "memchr", - "msvc-demangler", - "object", - "patternsleuth_image", - "patternsleuth_scanner", - "pdb", - "rayon", - "serde", - "strum", - "tracing", - "typetag", - "windows 0.52.0", -] - [[package]] name = "patternsleuth_cli" version = "0.1.0" @@ -2636,22 +2610,17 @@ dependencies = [ "iced-x86", "indicatif", "itertools 0.12.0", - "libc", - "memchr", "msvc-demangler", - "object", "patricia_tree", "patternsleuth_image", "patternsleuth_resolvers", "patternsleuth_scanner", - "pdb", "prettytable-rs", "rayon", "regex", "rusqlite", "serde", "serde_json", - "strum", "time", "tracing", "tracing-subscriber", @@ -3397,28 +3366,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[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.48", -] - [[package]] name = "syn" version = "1.0.109" @@ -3608,9 +3555,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3619,9 +3566,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3630,9 +3577,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3651,9 +3598,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -3775,9 +3722,9 @@ checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" diff --git a/Cargo.toml b/Cargo.toml index 5da20cc..379a0bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] resolver = "2" members = [ - "patternsleuth", "patternsleuth_cli", "patternsleuth_scanner", "patternsleuth_image", diff --git a/examples/dll_hook/Cargo.toml b/examples/dll_hook/Cargo.toml index 72a55a5..45f5942 100644 --- a/examples/dll_hook/Cargo.toml +++ b/examples/dll_hook/Cargo.toml @@ -19,7 +19,7 @@ egui-winit = { version = "0.24.0", default-features = false } indexmap = "2.1.0" itertools.workspace = true parking_lot = "0.12.1" -patternsleuth = { path = "../../patternsleuth", features = ["process-internal", "image-pe"] } +patternsleuth_image = { path = "../../patternsleuth_image", features = ["process-internal", "image-pe"] } regex.workspace = true retour = { git = "https://github.com/Hpmason/retour-rs", features = ["static-detour"] } simple-log = "1.6.0" diff --git a/patternsleuth/Cargo.toml b/patternsleuth/Cargo.toml deleted file mode 100644 index 4ba52fb..0000000 --- a/patternsleuth/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "patternsleuth" -repository.workspace = true -authors.workspace = true -license.workspace = true -version.workspace = true -edition.workspace = true - -[dependencies] -patternsleuth_scanner = { path = "../patternsleuth_scanner" } -anyhow = { workspace = true } -memchr = { workspace = true } -object = { workspace = true } -rayon = { workspace = true } -strum = { workspace = true } -pdb = { workspace = true, optional = true } -msvc-demangler = { workspace = true, optional = true} -iced-x86.workspace = true -futures = "0.3.30" -futures-scopes = "0.2.0" -inventory = "0.3.14" -itertools.workspace = true -serde = { workspace = true, optional = true, features = ["derive"] } -typetag = { version = "0.2.15", optional = true } -gimli = { version = "0.28.1", optional = true } -tracing = "0.1.40" -patternsleuth_image = { version = "0.1.0", path = "../patternsleuth_image" } - -[target.'cfg(target_os = "linux")'.dependencies] -libc = { version = "0.2.152", optional = true } - -[target.'cfg(windows)'.dependencies] -windows = { workspace = true, optional = true, features = [ - "Win32_Foundation", - "Win32_System_LibraryLoader", - "Win32_System_ProcessStatus", - "Win32_System_Threading", - "Win32_System_Diagnostics_Debug", -] } - -[features] -default = [] -serde-resolvers = ["dep:serde", "dep:typetag"] -symbols = ["dep:pdb", "dep:msvc-demangler"] -process-external = ["image-pe", "dep:libc", "dep:windows"] -process-internal = ["dep:libc", "dep:windows"] -image-pe = [] -image-elf = ["dep:gimli"] diff --git a/patternsleuth/src/lib.rs b/patternsleuth/src/lib.rs deleted file mode 100644 index 1aa7100..0000000 --- a/patternsleuth/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub use patternsleuth_image::process; -pub use patternsleuth_scanner as scanner; diff --git a/patternsleuth_cli/Cargo.toml b/patternsleuth_cli/Cargo.toml index 9ab3627..8d045e0 100644 --- a/patternsleuth_cli/Cargo.toml +++ b/patternsleuth_cli/Cargo.toml @@ -20,19 +20,14 @@ colored = { workspace = true } globset = { workspace = true } iced-x86 = { workspace = true } itertools = { workspace = true } -memchr = { workspace = true } msvc-demangler = { workspace = true } -object = { workspace = true } -pdb = { workspace = true } prettytable-rs = { workspace = true } rayon.workspace = true regex = { workspace = true } -strum = { workspace = true } indicatif = { version = "0.17.7", features = ["rayon"] } rusqlite = { version = "0.30.0", features = ["bundled"] } crossbeam = "0.8.4" patricia_tree = "0.8.0" -libc = "0.2.152" serde = { workspace = true, features = ["rc"] } serde_json = "1.0.111" time = { version = "0.3.31", features = ["formatting", "macros", "local-offset"] } From b6722a61aaa4145c720a77901d38fb32f2007a67 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Thu, 30 Jan 2025 13:00:16 -0600 Subject: [PATCH 06/19] misc --- patternsleuth_cli/build.rs | 3 +-- patternsleuth_image/src/image/mod.rs | 6 ++---- .../src/unreal/static_construct_object.rs | 3 +-- patternsleuth_scanner/src/lib.rs | 3 +-- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/patternsleuth_cli/build.rs b/patternsleuth_cli/build.rs index 0fc47ce..9a8675a 100644 --- a/patternsleuth_cli/build.rs +++ b/patternsleuth_cli/build.rs @@ -14,8 +14,7 @@ fn main() { .args(["diff-index", "--quiet", "HEAD"]) .status() .ok() - .map(|status| !status.success()) - .unwrap_or(false) + .is_some_and(|status| !status.success()) { println!("cargo:rustc-env=GIT_DIRTY=true"); } diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index efa6650..72d4e34 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -107,8 +107,7 @@ impl<'data> Image<'data> { .filter_map(|scan| { scan.scan .section - .map(|s| s == section.kind()) - .unwrap_or(true) + .is_none_or(|s| s == section.kind()) .then(|| { scan.scan .scan_type @@ -124,8 +123,7 @@ impl<'data> Image<'data> { .filter_map(|scan| { scan.scan .section - .map(|s| s == section.kind()) - .unwrap_or(true) + .is_none_or(|s| s == section.kind()) .then(|| scan.scan.scan_type.get_xref().map(|xref| (scan, xref))) .flatten() }) diff --git a/patternsleuth_resolvers/src/unreal/static_construct_object.rs b/patternsleuth_resolvers/src/unreal/static_construct_object.rs index 1965b1e..eaa8c96 100644 --- a/patternsleuth_resolvers/src/unreal/static_construct_object.rs +++ b/patternsleuth_resolvers/src/unreal/static_construct_object.rs @@ -198,8 +198,7 @@ impl_resolver_singleton!(PEImage, StaticConstructObjectInternalString, |ctx| asy if img .memory .range(ptr..ptr + cmp.len()) - .map(|data| data == cmp) - .unwrap_or(false) + .is_ok_and(|data| data == cmp) { is = true; return Ok(Control::Exit); diff --git a/patternsleuth_scanner/src/lib.rs b/patternsleuth_scanner/src/lib.rs index bb63fee..018da66 100644 --- a/patternsleuth_scanner/src/lib.rs +++ b/patternsleuth_scanner/src/lib.rs @@ -234,8 +234,7 @@ impl Pattern { .checked_add_signed(i32::from_le_bytes( data[index + offset..index + offset + 4].try_into().unwrap(), ) as isize) - .map(|x| x == xref.0) - .unwrap_or(false) + .is_some_and(|x| x == xref.0) }) } pub fn captures<'data>( From 702fd90e932de8e09137d13500bee9457e204d30 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Tue, 25 Feb 2025 23:21:35 -0600 Subject: [PATCH 07/19] gig memory trait refactor --- patternsleuth_cli/src/main.rs | 12 +- patternsleuth_image/src/image/elf.rs | 4 +- patternsleuth_image/src/image/mod.rs | 2 +- patternsleuth_image/src/image/pe.rs | 16 +-- patternsleuth_image/src/lib.rs | 114 +++++++++++------- patternsleuth_image/src/process/external.rs | 2 +- patternsleuth_resolvers/src/disassemble.rs | 18 +-- patternsleuth_resolvers/src/lib.rs | 34 +++--- .../src/unreal/engine_version.rs | 1 + patternsleuth_resolvers/src/unreal/gengine.rs | 2 +- patternsleuth_resolvers/src/unreal/mod.rs | 12 +- .../src/unreal/static_construct_object.rs | 4 +- 12 files changed, 125 insertions(+), 96 deletions(-) diff --git a/patternsleuth_cli/src/main.rs b/patternsleuth_cli/src/main.rs index 2a1f74e..a6be3f4 100644 --- a/patternsleuth_cli/src/main.rs +++ b/patternsleuth_cli/src/main.rs @@ -330,7 +330,7 @@ fn scan(command: CommandScan) -> Result<()> { #[allow(unused_assignments)] let mut bin_data = None; - let (name, exe) = match game { + let (name, img) = match game { GameEntry::File(GameFileEntry { name, exe_path }) => { output.println(format!("{:?} {:?}", name, exe_path.display())); @@ -365,7 +365,7 @@ fn scan(command: CommandScan) -> Result<()> { games.insert(name.to_string()); - let scan = exe.scan(&patterns)?; + let scan = img.scan(&patterns)?; // group results by Sig let folded_scans = scan @@ -394,7 +394,7 @@ fn scan(command: CommandScan) -> Result<()> { "{}\n{}", m.0.name, disassemble::disassemble( - &exe, + &img, m.1.address, m.0.scan.scan_type.get_pattern() ) @@ -417,7 +417,7 @@ fn scan(command: CommandScan) -> Result<()> { // sort by pattern name, then match address .sorted_by_key(|&data| data.0) .map(|(m, counts)| { - let dis = disassemble::disassemble(&exe, m.address, None); + let dis = disassemble::disassemble(&img, m.address, None); let mut lines = vec![]; for (name, count) in counts.iter().sorted_by_key(|e| e.0) { @@ -469,7 +469,7 @@ fn scan(command: CommandScan) -> Result<()> { format!("{:016x} {:?}{}", m.1.address, m.0, count) .normal() .to_string(), - exe.symbols + img.symbols .as_ref() .and_then(|symbols| symbols.get(&m.1.address)), ) @@ -502,7 +502,7 @@ fn scan(command: CommandScan) -> Result<()> { }; let resolution = tracing::info_span!("scan", game = game_name) - .in_scope(|| resolve_many(&exe, &dyn_resolvers)); + .in_scope(|| resolve_many(&img, &dyn_resolvers)); for (resolver, resolution) in resolvers.iter().zip(&resolution) { table.add_row(Row::new( diff --git a/patternsleuth_image/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs index 92e45f7..1e72775 100644 --- a/patternsleuth_image/src/image/elf.rs +++ b/patternsleuth_image/src/image/elf.rs @@ -79,7 +79,7 @@ impl ElfImage { // read_inner impl ElfImage { /// Read and parse ELF object, using data from memory - pub fn read_inner_memory<'data, P: AsRef>( + fn read_inner_memory<'data, P: AsRef>( base_address: usize, #[allow(unused_variables)] exe_path: Option

, linked: bool, @@ -233,7 +233,7 @@ impl ElfImage { }; Ok(Image { base_address, - memory, + memory: Box::new(memory), #[cfg(feature = "symbols")] symbols, imports: HashMap::default(), diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index 72d4e34..82e05ab 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -32,7 +32,7 @@ pub use _image_type_reflection as image_type_reflection; pub struct Image<'data> { pub base_address: usize, - pub memory: Memory<'data>, + pub memory: Box + 'data>, #[cfg(feature = "symbols")] pub symbols: Option>, pub imports: HashMap>, diff --git a/patternsleuth_image/src/image/pe.rs b/patternsleuth_image/src/image/pe.rs index cb9e53c..f32b1f8 100644 --- a/patternsleuth_image/src/image/pe.rs +++ b/patternsleuth_image/src/image/pe.rs @@ -7,7 +7,7 @@ use itertools::Itertools; use super::{Image, ImageType}; #[cfg(feature = "symbols")] use crate::symbols; -use crate::{Memory, MemoryAccessError, MemoryTrait, RuntimeFunction}; +use crate::{MemTraitNew, Memory, MemoryAccessError, MemoryTrait, RuntimeFunction}; use object::Object; pub struct PEImage { @@ -65,9 +65,9 @@ impl PEImage { let section = image.memory.get_section_containing(unwind_addr)?; - let has_chain_info = section.section.index(unwind_addr)? >> 3 == 0x4; + let has_chain_info = section.index(unwind_addr)? >> 3 == 0x4; if has_chain_info { - let unwind_code_count = section.section.index(unwind_addr + 2)?; + let unwind_code_count = section.index(unwind_addr + 2)?; unwind_addr += 4 + 2 * unwind_code_count as usize; if unwind_addr % 4 != 0 { @@ -169,7 +169,7 @@ impl Image<'_> { #[allow(irrefutable_let_patterns)] if let ImageType::PEImage(ref mut pe) = self.image_type { for i in pe.exception_directory_range.clone().step_by(12) { - let f = RuntimeFunction::read(&self.memory, self.base_address, i)?; + let f = RuntimeFunction::read(self.memory.as_ref(), self.base_address, i)?; pe.exception_children_cache.insert(f.range.start, vec![]); let Ok(section) = self.memory.get_section_containing(f.unwind) else { @@ -179,9 +179,9 @@ impl Image<'_> { }; let mut unwind = f.unwind; - let has_chain_info = section.section.index(unwind)? >> 3 == 0x4; + let has_chain_info = section.index(unwind)? >> 3 == 0x4; if has_chain_info { - let unwind_code_count = section.section.index(unwind + 2)?; + let unwind_code_count = section.index(unwind + 2)?; unwind += 4 + 2 * unwind_code_count as usize; if unwind % 4 != 0 { @@ -224,7 +224,7 @@ impl PEImage { base_address: usize, #[allow(unused_variables)] exe_path: Option

, cache_functions: bool, - memory: Memory<'data>, + memory: Box + 'data>, object: object::File<'_>, ) -> Result, anyhow::Error> { #[cfg(feature = "symbols")] @@ -314,6 +314,6 @@ impl PEImage { ) -> Result, anyhow::Error> { let base_address = base_addr.unwrap_or(object.relative_address_base() as usize); let memory = Memory::new(&object)?; - Self::read_inner_memory(base_address, exe_path, cache_functions, memory, object) + Self::read_inner_memory(base_address, exe_path, cache_functions, Box::new(memory), object) } } diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index 2391ade..ff5c7f8 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -22,14 +22,6 @@ use object::{File, Object, ObjectSection}; use image::Image; -pub struct ResolveContext<'data, 'pattern> { - pub exe: &'data Image<'data>, - pub memory: &'data Memory<'data>, - pub section: String, - pub match_address: usize, - pub scan: &'pattern Scan, -} - #[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] pub struct Resolution { pub address: usize, @@ -133,7 +125,7 @@ pub struct RuntimeFunction { } impl RuntimeFunction { pub fn read<'data>( - memory: &impl MemoryTrait<'data>, + memory: &(impl MemoryTrait<'data> + ?Sized), base_address: usize, address: usize, ) -> Result { @@ -153,6 +145,24 @@ impl RuntimeFunction { } } +pub trait MemTraitNew<'data>: MemoryTrait<'data> + Matchable<'data> + Index + Index, Output = [u8]> + Send + Sync { + fn sections(&self) -> Box> + '_>; + fn get_section_containing( + &self, + address: usize, + ) -> Result<&dyn SectionTraitNew<'data>, MemoryAccessError> { + self.sections() + .find(|section| { + address >= section.address() && address < section.address() + section.data().len() + }) + .ok_or(MemoryAccessError::MemoryOutOfBoundsError) + } +} +pub trait SectionTraitNew<'a>: MemoryBlockTrait<'a> + MemoryTrait<'a> + Send + Sync { + fn name(&self) -> &str; + fn kind(&self) -> object::SectionKind; +} + #[derive(Debug, Clone, PartialEq)] #[cfg_attr( feature = "serde-resolvers", @@ -193,7 +203,7 @@ pub trait MemoryBlockTrait<'data> { /// Return starting address of block fn address(&self) -> usize; /// Returned contained memory - fn data(&self) -> &[u8]; + fn data(&self) -> &'data [u8]; } /// Potentially sparse section of memory @@ -201,12 +211,12 @@ pub trait MemoryTrait<'data> { /// Return u8 at `address` fn index(&self, address: usize) -> Result; /// Return slice of u8 at `range` - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError>; + fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError>; /// Return slice of u8 from start of `range` to end of block - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError>; + fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError>; /// Return slice of u8 from end of `range` to start of block (not useful because start of block /// is unknown to caller) - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError>; + fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError>; /// Return i16 at `address` fn i16_le(&self, address: usize) -> Result { @@ -289,15 +299,15 @@ impl<'data, T: MemoryBlockTrait<'data>> MemoryTrait<'data> for T { // TODO bounds Ok(self.data()[address - self.address()]) } - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { + fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[range.start - self.address()..range.end - self.address()]) } - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { + fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[range.start - self.address()..]) } - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { + fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[..range.end - self.address()]) } @@ -307,26 +317,26 @@ impl<'data> MemoryTrait<'data> for Memory<'data> { fn index(&self, address: usize) -> Result { self.get_section_containing(address)?.index(address) } - fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { + fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError> { self.get_section_containing(range.start)?.range(range) } - fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { + fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError> { self.get_section_containing(range.start)?.range_from(range) } - fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { + fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError> { self.get_section_containing(range.end)?.range_to(range) } } pub struct MemorySection<'data> { address: usize, - data: Cow<'data, [u8]>, + data: &'data [u8], } impl<'data> MemoryBlockTrait<'data> for MemorySection<'data> { fn address(&self) -> usize { self.address } - fn data(&self) -> &[u8] { + fn data(&self) -> &'data [u8] { &self.data } } @@ -338,18 +348,18 @@ pub struct NamedMemorySection<'data> { } impl<'data> NamedMemorySection<'data> { - fn new>>( + fn new( name: String, address: usize, kind: object::SectionKind, - data: T, + data: &'data [u8], ) -> Self { Self { name, kind, section: MemorySection { address, - data: data.into(), + data, }, } } @@ -378,14 +388,31 @@ impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { fn address(&self) -> usize { self.section.address() } - fn data(&self) -> &[u8] { + fn data(&self) -> &'data [u8] { self.section.data() } } -pub struct Memory<'data> { +struct Memory<'data> { sections: Vec>, } +impl<'data> MemTraitNew<'data> for Memory<'data> { + fn sections(&self) -> Box> + '_> { + Box::new( + self.sections + .iter() + .map(|s| -> &dyn SectionTraitNew<'data> { s }), + ) + } +} +impl<'data> SectionTraitNew<'data> for NamedMemorySection<'data> { + fn name(&self) -> &str { + &self.name + } + fn kind(&self) -> object::SectionKind { + self.kind + } +} impl<'data> Memory<'data> { pub fn new(object: &File<'data>) -> Result { @@ -404,19 +431,20 @@ impl<'data> Memory<'data> { }) } pub fn new_external_data(sections: Vec<(object::Section<'_, '_>, Vec)>) -> Result { - Ok(Self { - sections: sections - .into_iter() - .map(|(s, d)| { - Ok(NamedMemorySection::new( - s.name()?.to_string(), - s.address() as usize, - s.kind(), - d, - )) - }) - .collect::>>()?, - }) + todo!() + //Ok(Self { + // sections: sections + // .into_iter() + // .map(|(s, d)| { + // Ok(NamedMemorySection::new( + // s.name()?.to_string(), + // s.address() as usize, + // s.kind(), + // d, + // )) + // }) + // .collect::>>()?, + //}) } pub fn new_internal_data( sections: Vec<(object::Section<'_, '_>, &'data [u8])>, @@ -521,18 +549,18 @@ impl Addressable for patternsleuth_scanner::Capture<'_> { pub trait Matchable<'data> { fn captures( - &'data self, + &self, pattern: &Pattern, address: usize, - ) -> Result>>, MemoryAccessError>; + ) -> Result>>, MemoryAccessError>; } impl<'data> Matchable<'data> for Memory<'data> { fn captures( - &'data self, + &self, pattern: &Pattern, address: usize, - ) -> Result>>, MemoryAccessError> { + ) -> Result>>, MemoryAccessError> { let s = self.get_section_containing(address)?; // TODO bounds check data passed to captures Ok(pattern.captures(s.data(), s.address(), address - s.address())) diff --git a/patternsleuth_image/src/process/external.rs b/patternsleuth_image/src/process/external.rs index 98281cd..34410e8 100644 --- a/patternsleuth_image/src/process/external.rs +++ b/patternsleuth_image/src/process/external.rs @@ -87,7 +87,7 @@ mod linux { object.relative_address_base() as usize, None, false, - memory, + Box::new(memory), object, ) } diff --git a/patternsleuth_resolvers/src/disassemble.rs b/patternsleuth_resolvers/src/disassemble.rs index 986b2fa..c0753af 100644 --- a/patternsleuth_resolvers/src/disassemble.rs +++ b/patternsleuth_resolvers/src/disassemble.rs @@ -4,7 +4,7 @@ use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, Nas use crate::{Image, MemoryAccessError, MemoryTrait}; -pub fn function_range(exe: &Image<'_>, address: usize) -> Result, MemoryAccessError> { +pub fn function_range(exe: &Image, address: usize) -> Result, MemoryAccessError> { let min = address; let mut max = min; disassemble(exe, address, |inst| { @@ -18,8 +18,8 @@ pub fn function_range(exe: &Image<'_>, address: usize) -> Result, M Ok(min..max) } -pub fn disassemble_single<'mem, 'img: 'mem>( - exe: &'img Image<'mem>, +pub fn disassemble_single( + exe: &Image, address: usize, ) -> Result, MemoryAccessError> { Ok(Decoder::with_ip( @@ -38,21 +38,21 @@ pub enum Control { Exit, } -pub fn disassemble<'mem, 'img: 'mem, F>( - exe: &'img Image<'mem>, +pub fn disassemble( + exe: &Image, address: usize, mut visitor: F, ) -> Result<(), MemoryAccessError> where F: FnMut(&Instruction) -> Result, { - struct Ctx<'mem, 'img: 'mem> { - exe: &'img Image<'mem>, + struct Ctx<'data, 'img> { + exe: &'img Image<'data>, queue: Vec, visited: HashSet, address: usize, - block: &'mem [u8], - decoder: Decoder<'mem>, + block: &'data [u8], + decoder: Decoder<'data>, instruction: Instruction, } diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index e47e167..26544a9 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -172,11 +172,11 @@ impl PartialEq for dyn Resolution { } pub struct DynResolverFactory { - pub factory: for<'ctx> fn(&'ctx AsyncContext<'_>) -> DynResolver<'ctx>, + pub factory: for<'ctx> fn(&'ctx AsyncContext<'_, '_>) -> DynResolver<'ctx>, } pub struct ResolverFactory { - pub factory: for<'ctx> fn(&'ctx AsyncContext<'_>) -> Resolver<'ctx, T>, + pub factory: for<'ctx> fn(&'ctx AsyncContext<'_, '_>) -> Resolver<'ctx, T>, } pub use ::futures; @@ -237,7 +237,7 @@ macro_rules! _impl_resolver { $crate::cfg_image::$arch! { impl $name where $name: $crate::PleaseAddCollectForMe { #[allow(non_snake_case)] - pub async fn $arch($ctx: &$crate::AsyncContext<'_>) -> $crate::Result<$name> $x + pub async fn $arch($ctx: &$crate::AsyncContext<'_, '_>) -> $crate::Result<$name> $x } } }; @@ -285,7 +285,7 @@ macro_rules! _impl_resolver_singleton { $crate::cfg_image::$arch! { impl $name where $name: $crate::PleaseAddCollectForMe { #[allow(non_snake_case)] - async fn $arch($ctx: &$crate::AsyncContext<'_>) -> $crate::Result<$name> $x + async fn $arch($ctx: &$crate::AsyncContext<'_, '_>) -> $crate::Result<$name> $x } } }; @@ -444,18 +444,18 @@ struct AsyncContextInnerWrite { queue: Vec<(Pattern, oneshot::Sender)>, } -struct AsyncContextInnerRead<'data> { +struct AsyncContextInnerRead<'img, 'data> { write: Mutex, - image: &'data Image<'data>, + image: &'img Image<'data>, } #[derive(Clone)] -pub struct AsyncContext<'data> { - read: Arc>, +pub struct AsyncContext<'img, 'data> { + read: Arc>, } -impl<'data> AsyncContext<'data> { - fn new(image: &'data Image<'data>) -> Self { +impl<'img, 'data> AsyncContext<'img, 'data> { + fn new(image: &'img Image<'data>) -> Self { Self { read: Arc::new(AsyncContextInnerRead { write: Default::default(), @@ -463,7 +463,7 @@ impl<'data> AsyncContext<'data> { }), } } - pub fn image(&self) -> &Image<'_> { + pub fn image(&self) -> &'img Image<'data> { self.read.image } pub async fn scan(&self, pattern: Pattern) -> Vec { @@ -542,9 +542,9 @@ impl<'data> AsyncContext<'data> { } #[tracing::instrument(level = "debug", skip_all, fields(stages))] -pub fn eval(image: &Image<'_>, f: F) -> T +pub fn eval<'img, 'data, F, T: Send + Sync>(image: &'img Image<'data>, f: F) -> T where - F: for<'ctx> FnOnce(&'ctx AsyncContext<'_>) -> BoxFuture<'ctx, T> + Send + Sync, + F: for<'ctx> FnOnce(&'ctx AsyncContext<'_, '_>) -> BoxFuture<'ctx, T> + Send + Sync, { { tracing::debug!("starting eval"); @@ -625,16 +625,16 @@ where } } -pub fn resolve( - image: &Image<'_>, +pub fn resolve<'data, 'img, T: Send + Sync>( + image: &'img Image<'data>, resolver: &'static ResolverFactory, ) -> Result { eval(image, |ctx| Box::pin(async { ctx.resolve(resolver).await })) .map(|ok| Arc::::into_inner(ok).unwrap()) } -pub fn resolve_many( - image: &Image<'_>, +pub fn resolve_many<'data, 'img>( + image: &'img Image<'data>, resolvers: &[fn() -> &'static DynResolverFactory], ) -> Vec>> { let fns = resolvers.iter().map(|r| r().factory).collect::>(); diff --git a/patternsleuth_resolvers/src/unreal/engine_version.rs b/patternsleuth_resolvers/src/unreal/engine_version.rs index 19ff4b1..9bb217a 100644 --- a/patternsleuth_resolvers/src/unreal/engine_version.rs +++ b/patternsleuth_resolvers/src/unreal/engine_version.rs @@ -163,6 +163,7 @@ impl_resolver!(PEImage, EngineVersionStrings, |ctx| async { for (_, pattern, addresses) in res { for a in addresses { let caps = mem.captures(&pattern, a)?.unwrap(); + let date = caps[1].rip(); if mem .range(date..date + 6) diff --git a/patternsleuth_resolvers/src/unreal/gengine.rs b/patternsleuth_resolvers/src/unreal/gengine.rs index c664081..4f38186 100644 --- a/patternsleuth_resolvers/src/unreal/gengine.rs +++ b/patternsleuth_resolvers/src/unreal/gengine.rs @@ -20,7 +20,7 @@ impl_resolver_singleton!(PEImage, GEngine, |ctx| async { let strings = ctx.scan(util::utf16_pattern("rhi.DumpMemory\0")).await; let refs = util::scan_xrefs(ctx, &strings).await; - fn for_each(img: &Image<'_>, addr: usize) -> Result> { + fn for_each(img: &Image, addr: usize) -> Result> { let Some(root) = img.get_root_function(addr)? else { return Ok(None); }; diff --git a/patternsleuth_resolvers/src/unreal/mod.rs b/patternsleuth_resolvers/src/unreal/mod.rs index a8d3576..1e15955 100644 --- a/patternsleuth_resolvers/src/unreal/mod.rs +++ b/patternsleuth_resolvers/src/unreal/mod.rs @@ -54,7 +54,7 @@ pub mod util { Pattern::from_bytes(utf16(string)).unwrap() } pub async fn scan_xrefs( - ctx: &AsyncContext<'_>, + ctx: &AsyncContext<'_, '_>, addresses: impl IntoIterator + Copy, ) -> Vec { let refs_indirect = join_all( @@ -95,7 +95,7 @@ pub mod util { } pub async fn scan_xcalls( - ctx: &AsyncContext<'_>, + ctx: &AsyncContext<'_, '_>, addresses: impl IntoIterator + Copy, ) -> Vec { let refs_indirect = join_all( @@ -123,7 +123,7 @@ pub mod util { refs.into_iter().flatten().collect() } - pub fn root_functions<'a, I>(ctx: &AsyncContext<'_>, addresses: I) -> Result> + pub fn root_functions<'a, I>(ctx: &AsyncContext<'_, '_>, addresses: I) -> Result> where I: IntoIterator + Copy, { @@ -136,7 +136,7 @@ pub mod util { .collect()) } - pub fn find_calls(img: &Image<'_>, f: usize) -> Result> { + pub fn find_calls<'a>(img: &Image, f: usize) -> Result> { let mut calls = vec![]; disassemble(img, f, |inst| { @@ -171,8 +171,8 @@ pub mod util { Ok(calls) } - pub fn find_path( - img: &Image<'_>, + pub fn find_path<'a>( + img: &'a Image<'a>, f: usize, depth: usize, searched: &mut HashSet, diff --git a/patternsleuth_resolvers/src/unreal/static_construct_object.rs b/patternsleuth_resolvers/src/unreal/static_construct_object.rs index eaa8c96..2beded7 100644 --- a/patternsleuth_resolvers/src/unreal/static_construct_object.rs +++ b/patternsleuth_resolvers/src/unreal/static_construct_object.rs @@ -175,7 +175,7 @@ impl_resolver_singleton!(PEImage, StaticConstructObjectInternalString, |ctx| asy .into_iter() .flatten(); - fn check_is_new_object(img: &Image<'_>, f: usize) -> Result { + fn check_is_new_object(img: &Image, f: usize) -> Result { let cmp = "NewObject with empty name can't" .encode_utf16() .flat_map(u16::to_le_bytes) @@ -231,7 +231,7 @@ impl_resolver_singleton!(PEImage, StaticConstructObjectInternalString, |ctx| asy Ok(false) } - fn check_is_static_construct(img: &Image<'_>, f: usize) -> Result { + fn check_is_static_construct(img: &Image, f: usize) -> Result { let mut is = false; disassemble(img, f, |inst| { let cur = inst.ip() as usize; From 68dc9e5d5745208cc0fcd2819ed9e765ac49a65c Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Tue, 25 Feb 2025 23:58:06 -0600 Subject: [PATCH 08/19] refactor part 2 --- Cargo.toml | 2 +- examples/dll_hook/src/gui.rs | 2 +- examples/dll_hook/src/lib.rs | 8 +-- examples/dll_hook/src/ue.rs | 2 +- patternsleuth_cli/src/db.rs | 4 +- patternsleuth_cli/src/disassemble.rs | 2 +- patternsleuth_cli/src/main.rs | 18 ++--- patternsleuth_image/src/image/elf.rs | 6 +- patternsleuth_image/src/image/pe.rs | 14 ++-- patternsleuth_image/src/lib.rs | 70 ++++++++++--------- patternsleuth_image/src/process/external.rs | 15 ++-- patternsleuth_image/src/process/internal.rs | 6 +- patternsleuth_image/src/uesym.rs | 4 +- patternsleuth_resolvers/src/disassemble.rs | 12 ++-- patternsleuth_resolvers/src/lib.rs | 14 ++-- patternsleuth_resolvers/src/unreal/aes.rs | 19 +++-- .../src/unreal/blueprint_library.rs | 4 +- .../src/unreal/engine_version.rs | 7 +- patternsleuth_resolvers/src/unreal/fname.rs | 7 +- patternsleuth_resolvers/src/unreal/ftext.rs | 69 +++++++++++++----- .../src/unreal/fuobject_hash_tables.rs | 4 +- patternsleuth_resolvers/src/unreal/gengine.rs | 4 +- patternsleuth_resolvers/src/unreal/gmalloc.rs | 7 +- .../src/unreal/guobject_array.rs | 5 +- patternsleuth_resolvers/src/unreal/kismet.rs | 17 +++-- patternsleuth_resolvers/src/unreal/mod.rs | 10 +-- .../src/unreal/save_game.rs | 2 +- .../src/unreal/static_construct_object.rs | 32 ++++----- patternsleuth_scanner/benches/scan.rs | 4 +- patternsleuth_scanner/src/lib.rs | 8 +-- 30 files changed, 214 insertions(+), 164 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 379a0bd..d1b4a1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ repository = "https://github.com/trumank/patternsleuth" authors = [ "trumank" ] license = "MIT OR Apache-2.0" version = "0.1.0" -edition = "2021" +edition = "2024" [profile.release] incremental = true diff --git a/examples/dll_hook/src/gui.rs b/examples/dll_hook/src/gui.rs index 799529e..b7d5b05 100644 --- a/examples/dll_hook/src/gui.rs +++ b/examples/dll_hook/src/gui.rs @@ -1,4 +1,4 @@ -use std::sync::{mpsc::Receiver, Arc, OnceLock, RwLock}; +use std::sync::{Arc, OnceLock, RwLock, mpsc::Receiver}; use eframe::egui; diff --git a/examples/dll_hook/src/lib.rs b/examples/dll_hook/src/lib.rs index fdebbdc..5497958 100644 --- a/examples/dll_hook/src/lib.rs +++ b/examples/dll_hook/src/lib.rs @@ -8,11 +8,12 @@ mod ue; use std::path::PathBuf; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result, anyhow}; use patternsleuth::resolvers::impl_try_collector; -use patternsleuth::resolvers::unreal::blueprint_library::UFunctionBind; use patternsleuth::resolvers::unreal::UObjectBaseUtilityGetPathName; +use patternsleuth::resolvers::unreal::blueprint_library::UFunctionBind; use patternsleuth::resolvers::unreal::{ + KismetSystemLibrary, fname::FNameToString, game_loop::{FEngineLoopInit, UGameEngineTick}, gmalloc::GMalloc, @@ -20,9 +21,8 @@ use patternsleuth::resolvers::unreal::{ FUObjectArrayAllocateUObjectIndex, FUObjectArrayFreeUObjectIndex, GUObjectArray, }, kismet::{FFrameStep, FFrameStepExplicitProperty, FFrameStepViaExec}, - KismetSystemLibrary, }; -use simple_log::{error, info, LogConfigBuilder}; +use simple_log::{LogConfigBuilder, error, info}; use windows::Win32::{ Foundation::HMODULE, System::{ diff --git a/examples/dll_hook/src/ue.rs b/examples/dll_hook/src/ue.rs index 3789384..c7fc89a 100644 --- a/examples/dll_hook/src/ue.rs +++ b/examples/dll_hook/src/ue.rs @@ -6,7 +6,7 @@ use std::{ }; use windows::Win32::System::Threading::{ - EnterCriticalSection, LeaveCriticalSection, CRITICAL_SECTION, + CRITICAL_SECTION, EnterCriticalSection, LeaveCriticalSection, }; use crate::globals; diff --git a/patternsleuth_cli/src/db.rs b/patternsleuth_cli/src/db.rs index 8c4805d..31f55fc 100644 --- a/patternsleuth_cli/src/db.rs +++ b/patternsleuth_cli/src/db.rs @@ -6,14 +6,14 @@ use std::{ use anyhow::Result; use itertools::Itertools; -use patternsleuth_image::{image::Image, scanner::Pattern, PatternConfig}; +use patternsleuth_image::{PatternConfig, image::Image, scanner::Pattern}; use patternsleuth_resolvers::resolve_many; use prettytable::{Cell, Row, Table}; use rayon::prelude::*; use rusqlite::{Connection, OptionalExtension}; use crate::{ - disassemble, get_games, CommandAutoGen, CommandBuildIndex, CommandViewSymbol, GameFileEntry, + CommandAutoGen, CommandBuildIndex, CommandViewSymbol, GameFileEntry, disassemble, get_games, }; fn generate_patterns_for_symbol(symbol: &str) -> Result> { diff --git a/patternsleuth_cli/src/disassemble.rs b/patternsleuth_cli/src/disassemble.rs index fa73d41..df734b7 100644 --- a/patternsleuth_cli/src/disassemble.rs +++ b/patternsleuth_cli/src/disassemble.rs @@ -4,7 +4,7 @@ use colored::{ColoredString, Colorize}; use iced_x86::{ Decoder, DecoderOptions, Formatter, FormatterOutput, FormatterTextKind, IntelFormatter, OpKind, }; -use patternsleuth_image::{image::Image, scanner::Pattern, MemoryTrait}; +use patternsleuth_image::{image::Image, scanner::Pattern}; #[derive(Default)] struct Output { diff --git a/patternsleuth_cli/src/main.rs b/patternsleuth_cli/src/main.rs index a6be3f4..6ca69a0 100644 --- a/patternsleuth_cli/src/main.rs +++ b/patternsleuth_cli/src/main.rs @@ -7,17 +7,17 @@ use std::fs; use std::path::{Path, PathBuf}; use std::str::FromStr; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; +use clap::Parser; use clap::builder::{ IntoResettable, PossibleValue, PossibleValuesParser, TypedValueParser, ValueParser, }; -use clap::Parser; use indicatif::ProgressBar; use itertools::Itertools; use patricia_tree::StringPatriciaMap; use patternsleuth_image::image::Image; use patternsleuth_image::{PatternConfig, Resolution}; -use patternsleuth_resolvers::{resolve_many, resolvers, NamedResolver}; +use patternsleuth_resolvers::{NamedResolver, resolve_many, resolvers}; use patternsleuth_image::symbols::Symbol; use patternsleuth_scanner::Pattern; @@ -214,7 +214,7 @@ fn find_ext, E: AsRef>(dir: P, ext: &[E]) -> Result Result<()> { - use tracing_subscriber::{fmt, fmt::format::FmtSpan, EnvFilter}; + use tracing_subscriber::{EnvFilter, fmt, fmt::format::FmtSpan}; fmt() .compact() @@ -292,7 +292,7 @@ fn scan(command: CommandScan) -> Result<()> { use colored::Colorize; use indicatif::ProgressIterator; use itertools::join; - use prettytable::{format, row, Cell, Row, Table}; + use prettytable::{Cell, Row, Table, format, row}; enum Output { Stdout, @@ -403,7 +403,7 @@ fn scan(command: CommandScan) -> Result<()> { } cells.push(Cell::new(&table.to_string())); } else if command.disassemble_merged { - cells.push(Cell::new({ + cells.push(Cell::new(&{ let cells = sig_scans .iter() .fold( @@ -440,10 +440,10 @@ fn scan(command: CommandScan) -> Result<()> { table.add_row(Row::new(cells)); - &table.to_string() + table.to_string() })); } else { - cells.push(Cell::new({ + cells.push(Cell::new(&{ let mut lines = sig_scans .iter() // group and count matches by (pattern name, address) @@ -485,7 +485,7 @@ fn scan(command: CommandScan) -> Result<()> { )); } } - &join(lines.iter().map(|(line, _)| line), "\n").to_string() + join(lines.iter().map(|(line, _)| line), "\n").to_string() })); } } else { diff --git a/patternsleuth_image/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs index 1e72775..5c2c86e 100644 --- a/patternsleuth_image/src/image/elf.rs +++ b/patternsleuth_image/src/image/elf.rs @@ -7,10 +7,10 @@ use gimli::{BaseAddresses, CieOrFde, EhFrame, EhFrameHdr, NativeEndian, UnwindSe #[cfg(feature = "symbols")] use crate::uesym; -use anyhow::{bail, Context, Error, Result}; +use anyhow::{Context, Error, Result, bail}; use object::{ - elf::ProgramHeader64, read::elf::ElfFile64, read::elf::ProgramHeader, Endianness, File, Object, - ObjectSection, SectionKind, + Endianness, File, Object, ObjectSection, SectionKind, elf::ProgramHeader64, + read::elf::ElfFile64, read::elf::ProgramHeader, }; pub struct ElfImage { diff --git a/patternsleuth_image/src/image/pe.rs b/patternsleuth_image/src/image/pe.rs index f32b1f8..686fb75 100644 --- a/patternsleuth_image/src/image/pe.rs +++ b/patternsleuth_image/src/image/pe.rs @@ -1,13 +1,13 @@ use std::collections::{HashMap, HashSet}; use std::ops::Range; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use itertools::Itertools; use super::{Image, ImageType}; #[cfg(feature = "symbols")] use crate::symbols; -use crate::{MemTraitNew, Memory, MemoryAccessError, MemoryTrait, RuntimeFunction}; +use crate::{MemTraitNew, Memory, MemoryAccessError, RuntimeFunction}; use object::Object; pub struct PEImage { @@ -255,9 +255,9 @@ impl PEImage { let get_imports = || -> Result<_> { Ok(match object { object::File::Pe64(ref inner) => { + use object::LittleEndian as LE; use object::pe::ImageNtHeaders64; use object::read::pe::ImageThunkData; - use object::LittleEndian as LE; let mut imports: HashMap> = Default::default(); @@ -314,6 +314,12 @@ impl PEImage { ) -> Result, anyhow::Error> { let base_address = base_addr.unwrap_or(object.relative_address_base() as usize); let memory = Memory::new(&object)?; - Self::read_inner_memory(base_address, exe_path, cache_functions, Box::new(memory), object) + Self::read_inner_memory( + base_address, + exe_path, + cache_functions, + Box::new(memory), + object, + ) } } diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index ff5c7f8..c35ccff 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -17,7 +17,7 @@ use std::{ path::Path, }; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use object::{File, Object, ObjectSection}; use image::Image; @@ -145,7 +145,14 @@ impl RuntimeFunction { } } -pub trait MemTraitNew<'data>: MemoryTrait<'data> + Matchable<'data> + Index + Index, Output = [u8]> + Send + Sync { +pub trait MemTraitNew<'data>: + MemoryTrait<'data> + + Matchable<'data> + + Index + + Index, Output = [u8]> + + Send + + Sync +{ fn sections(&self) -> Box> + '_>; fn get_section_containing( &self, @@ -203,7 +210,7 @@ pub trait MemoryBlockTrait<'data> { /// Return starting address of block fn address(&self) -> usize; /// Returned contained memory - fn data(&self) -> &'data [u8]; + fn data(&self) -> &[u8]; } /// Potentially sparse section of memory @@ -211,12 +218,12 @@ pub trait MemoryTrait<'data> { /// Return u8 at `address` fn index(&self, address: usize) -> Result; /// Return slice of u8 at `range` - fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError>; + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError>; /// Return slice of u8 from start of `range` to end of block - fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError>; + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError>; /// Return slice of u8 from end of `range` to start of block (not useful because start of block /// is unknown to caller) - fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError>; + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError>; /// Return i16 at `address` fn i16_le(&self, address: usize) -> Result { @@ -299,15 +306,15 @@ impl<'data, T: MemoryBlockTrait<'data>> MemoryTrait<'data> for T { // TODO bounds Ok(self.data()[address - self.address()]) } - fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError> { + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[range.start - self.address()..range.end - self.address()]) } - fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError> { + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[range.start - self.address()..]) } - fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError> { + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { // TODO bounds Ok(&self.data()[..range.end - self.address()]) } @@ -317,26 +324,26 @@ impl<'data> MemoryTrait<'data> for Memory<'data> { fn index(&self, address: usize) -> Result { self.get_section_containing(address)?.index(address) } - fn range(&self, range: Range) -> Result<&'data [u8], MemoryAccessError> { + fn range(&self, range: Range) -> Result<&[u8], MemoryAccessError> { self.get_section_containing(range.start)?.range(range) } - fn range_from(&self, range: RangeFrom) -> Result<&'data [u8], MemoryAccessError> { + fn range_from(&self, range: RangeFrom) -> Result<&[u8], MemoryAccessError> { self.get_section_containing(range.start)?.range_from(range) } - fn range_to(&self, range: RangeTo) -> Result<&'data [u8], MemoryAccessError> { + fn range_to(&self, range: RangeTo) -> Result<&[u8], MemoryAccessError> { self.get_section_containing(range.end)?.range_to(range) } } pub struct MemorySection<'data> { address: usize, - data: &'data [u8], + data: Cow<'data, [u8]>, } impl<'data> MemoryBlockTrait<'data> for MemorySection<'data> { fn address(&self) -> usize { self.address } - fn data(&self) -> &'data [u8] { + fn data(&self) -> &[u8] { &self.data } } @@ -348,18 +355,18 @@ pub struct NamedMemorySection<'data> { } impl<'data> NamedMemorySection<'data> { - fn new( + fn new>>( name: String, address: usize, kind: object::SectionKind, - data: &'data [u8], + data: D, ) -> Self { Self { name, kind, section: MemorySection { address, - data, + data: data.into(), }, } } @@ -388,7 +395,7 @@ impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { fn address(&self) -> usize { self.section.address() } - fn data(&self) -> &'data [u8] { + fn data(&self) -> &[u8] { self.section.data() } } @@ -431,20 +438,19 @@ impl<'data> Memory<'data> { }) } pub fn new_external_data(sections: Vec<(object::Section<'_, '_>, Vec)>) -> Result { - todo!() - //Ok(Self { - // sections: sections - // .into_iter() - // .map(|(s, d)| { - // Ok(NamedMemorySection::new( - // s.name()?.to_string(), - // s.address() as usize, - // s.kind(), - // d, - // )) - // }) - // .collect::>>()?, - //}) + Ok(Self { + sections: sections + .into_iter() + .map(|(s, d)| { + Ok(NamedMemorySection::new( + s.name()?.to_string(), + s.address() as usize, + s.kind(), + d, + )) + }) + .collect::>>()?, + }) } pub fn new_internal_data( sections: Vec<(object::Section<'_, '_>, &'data [u8])>, diff --git a/patternsleuth_image/src/process/external.rs b/patternsleuth_image/src/process/external.rs index 34410e8..f93982d 100644 --- a/patternsleuth_image/src/process/external.rs +++ b/patternsleuth_image/src/process/external.rs @@ -5,10 +5,10 @@ pub use linux::*; mod linux { use std::ops::Range; - use anyhow::{bail, Context, Result}; + use anyhow::{Context, Result, bail}; use object::{Object, ObjectSection}; - use crate::{image, Image, Memory}; + use crate::{Image, Memory, image}; fn read_process_mem(pid: i32, address: usize, buffer: &mut [u8]) -> Result { unsafe { @@ -42,7 +42,14 @@ mod linux { .with_context(|| format!("could not read process maps (PID={pid})"))?; for line in maps.lines() { let mut split = line.splitn(6, |c: char| c.is_whitespace()); - if let [Some(range), Some(_permissions), Some(_offset), Some(_device), Some(_inode), Some(path)] = [ + if let [ + Some(range), + Some(_permissions), + Some(_offset), + Some(_device), + Some(_inode), + Some(path), + ] = [ split.next(), split.next(), split.next(), @@ -112,7 +119,7 @@ pub use windows::*; #[cfg(windows)] mod windows { - use anyhow::{bail, Result}; + use anyhow::{Result, bail}; use object::{Object, ObjectSection}; use crate::image::pe::PEImage; diff --git a/patternsleuth_image/src/process/internal.rs b/patternsleuth_image/src/process/internal.rs index c948633..78966e7 100644 --- a/patternsleuth_image/src/process/internal.rs +++ b/patternsleuth_image/src/process/internal.rs @@ -8,7 +8,7 @@ mod linux { use anyhow::Result; use crate::Image; - use libc::{dl_iterate_phdr, Elf64_Addr, Elf64_Phdr, Elf64_Sxword, Elf64_Xword, PT_LOAD}; + use libc::{Elf64_Addr, Elf64_Phdr, Elf64_Sxword, Elf64_Xword, PT_LOAD, dl_iterate_phdr}; #[repr(C)] #[derive(Debug)] @@ -46,7 +46,9 @@ mod linux { if name.is_empty() { // find the main //eprintln!("Base addr from iter = {:08x}", (*info).dlpi_addr); - *image = *info; + unsafe { + *image = *info; + } } 0 } diff --git a/patternsleuth_image/src/uesym.rs b/patternsleuth_image/src/uesym.rs index 3028ede..f2c05fc 100644 --- a/patternsleuth_image/src/uesym.rs +++ b/patternsleuth_image/src/uesym.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Result}; -use object::{from_bytes, slice_from_bytes, Pod}; +use anyhow::{Result, anyhow}; +use object::{Pod, from_bytes, slice_from_bytes}; use std::{collections::HashMap, path::Path}; use crate::symbols::Symbol; diff --git a/patternsleuth_resolvers/src/disassemble.rs b/patternsleuth_resolvers/src/disassemble.rs index c0753af..2d1bbb2 100644 --- a/patternsleuth_resolvers/src/disassemble.rs +++ b/patternsleuth_resolvers/src/disassemble.rs @@ -2,7 +2,7 @@ use std::{collections::HashSet, ops::Range}; use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, NasmFormatter}; -use crate::{Image, MemoryAccessError, MemoryTrait}; +use crate::{Image, MemoryAccessError}; pub fn function_range(exe: &Image, address: usize) -> Result, MemoryAccessError> { let min = address; @@ -38,11 +38,7 @@ pub enum Control { Exit, } -pub fn disassemble( - exe: &Image, - address: usize, - mut visitor: F, -) -> Result<(), MemoryAccessError> +pub fn disassemble(exe: &Image, address: usize, mut visitor: F) -> Result<(), MemoryAccessError> where F: FnMut(&Instruction) -> Result, { @@ -51,8 +47,8 @@ where queue: Vec, visited: HashSet, address: usize, - block: &'data [u8], - decoder: Decoder<'data>, + block: &'img [u8], // these lifetimes are NOT 'data because of oddities with how Cow<'data> is handled in Image + decoder: Decoder<'img>, instruction: Instruction, } diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index 26544a9..cadf943 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -2,17 +2,17 @@ pub mod disassemble; pub mod unreal; use image::Image; +use patternsleuth_image::MemoryAccessError; use patternsleuth_image::image; -use patternsleuth_image::{MemoryAccessError, MemoryTrait}; use futures::{ channel::oneshot, executor::LocalPool, - future::{join_all, BoxFuture}, + future::{BoxFuture, join_all}, }; use futures_scopes::{ - relay::{new_relay_scope, RelayScopeLocalSpawning}, ScopedSpawnExt, SpawnScope, + relay::{RelayScopeLocalSpawning, new_relay_scope}, }; use patternsleuth_scanner::Pattern; use std::{ @@ -625,16 +625,16 @@ where } } -pub fn resolve<'data, 'img, T: Send + Sync>( - image: &'img Image<'data>, +pub fn resolve( + image: &Image<'_>, resolver: &'static ResolverFactory, ) -> Result { eval(image, |ctx| Box::pin(async { ctx.resolve(resolver).await })) .map(|ok| Arc::::into_inner(ok).unwrap()) } -pub fn resolve_many<'data, 'img>( - image: &'img Image<'data>, +pub fn resolve_many( + image: &Image<'_>, resolvers: &[fn() -> &'static DynResolverFactory], ) -> Vec>> { let fns = resolvers.iter().map(|r| r().factory).collect::>(); diff --git a/patternsleuth_resolvers/src/unreal/aes.rs b/patternsleuth_resolvers/src/unreal/aes.rs index 8d23866..e98a05d 100644 --- a/patternsleuth_resolvers/src/unreal/aes.rs +++ b/patternsleuth_resolvers/src/unreal/aes.rs @@ -4,9 +4,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; -use crate::{ - MemoryTrait, {impl_resolver, Result}, -}; +use crate::{Result, impl_resolver}; #[derive(PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -51,9 +49,18 @@ impl_resolver!(PEImage, AESKeys, |ctx| async { C, } let patterns = [ - (KeyType::A, "C7 45 D0 ?? ?? ?? ?? C7 45 D4 ?? ?? ?? ?? ?? ?? ?? ?? C7 45 D8 ?? ?? ?? ?? C7 45 DC ?? ?? ?? ?? C7 45 E0 ?? ?? ?? ?? C7 45 E4 ?? ?? ?? ?? C7 45 E8 ?? ?? ?? ?? C7 45 EC ?? ?? ?? ??"), - (KeyType::B, "C7 01 ?? ?? ?? ?? C7 41 04 ?? ?? ?? ?? C7 41 08 ?? ?? ?? ?? C7 41 0C ?? ?? ?? ?? C7 41 10 ?? ?? ?? ?? C7 41 14 ?? ?? ?? ?? C7 41 18 ?? ?? ?? ?? C7 41 1C ?? ?? ?? ?? C3"), - (KeyType::C, "C7 45 D0 ?? ?? ?? ?? C7 45 D4 ?? ?? ?? ?? C7 45 D8 ?? ?? ?? ?? C7 45 DC ?? ?? ?? ?? 0F 10 45 D0 C7 45 E0 ?? ?? ?? ?? C7 45 E4 ?? ?? ?? ?? C7 45 E8 ?? ?? ?? ?? C7 45 EC ?? ?? ?? ??"), + ( + KeyType::A, + "C7 45 D0 ?? ?? ?? ?? C7 45 D4 ?? ?? ?? ?? ?? ?? ?? ?? C7 45 D8 ?? ?? ?? ?? C7 45 DC ?? ?? ?? ?? C7 45 E0 ?? ?? ?? ?? C7 45 E4 ?? ?? ?? ?? C7 45 E8 ?? ?? ?? ?? C7 45 EC ?? ?? ?? ??", + ), + ( + KeyType::B, + "C7 01 ?? ?? ?? ?? C7 41 04 ?? ?? ?? ?? C7 41 08 ?? ?? ?? ?? C7 41 0C ?? ?? ?? ?? C7 41 10 ?? ?? ?? ?? C7 41 14 ?? ?? ?? ?? C7 41 18 ?? ?? ?? ?? C7 41 1C ?? ?? ?? ?? C3", + ), + ( + KeyType::C, + "C7 45 D0 ?? ?? ?? ?? C7 45 D4 ?? ?? ?? ?? C7 45 D8 ?? ?? ?? ?? C7 45 DC ?? ?? ?? ?? 0F 10 45 D0 C7 45 E0 ?? ?? ?? ?? C7 45 E4 ?? ?? ?? ?? C7 45 E8 ?? ?? ?? ?? C7 45 EC ?? ?? ?? ??", + ), ]; let res = join_all( diff --git a/patternsleuth_resolvers/src/unreal/blueprint_library.rs b/patternsleuth_resolvers/src/unreal/blueprint_library.rs index bbc747f..61b305a 100644 --- a/patternsleuth_resolvers/src/unreal/blueprint_library.rs +++ b/patternsleuth_resolvers/src/unreal/blueprint_library.rs @@ -2,10 +2,10 @@ use std::fmt::Debug; use futures::{future::join_all, join}; -use patternsleuth_image::{Addressable as _, Matchable as _}; +use patternsleuth_image::Addressable as _; use patternsleuth_scanner::Pattern; -use crate::{ensure_one, impl_resolver, impl_resolver_singleton, Context}; +use crate::{Context, ensure_one, impl_resolver, impl_resolver_singleton}; #[derive(Debug, PartialEq)] #[cfg_attr( diff --git a/patternsleuth_resolvers/src/unreal/engine_version.rs b/patternsleuth_resolvers/src/unreal/engine_version.rs index 9bb217a..b14df09 100644 --- a/patternsleuth_resolvers/src/unreal/engine_version.rs +++ b/patternsleuth_resolvers/src/unreal/engine_version.rs @@ -3,12 +3,10 @@ use std::fmt::{Debug, Display}; use futures::future::join_all; use itertools::Itertools; -use patternsleuth_image::{Addressable as _, Matchable as _}; +use patternsleuth_image::Addressable as _; use patternsleuth_scanner::Pattern; -use crate::{ - MemoryTrait, {bail_out, impl_resolver, try_ensure_one}, -}; +use crate::{bail_out, impl_resolver, try_ensure_one}; #[derive(PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -137,7 +135,6 @@ impl_resolver!(ElfImage, EngineVersionStrings, |ctx| async { }); impl_resolver!(PEImage, EngineVersionStrings, |ctx| async { - use crate::MemoryTrait; use std::collections::HashSet; let patterns = [ diff --git a/patternsleuth_resolvers/src/unreal/fname.rs b/patternsleuth_resolvers/src/unreal/fname.rs index 2283325..07db845 100644 --- a/patternsleuth_resolvers/src/unreal/fname.rs +++ b/patternsleuth_resolvers/src/unreal/fname.rs @@ -2,10 +2,9 @@ use std::fmt::Debug; use futures::future::join_all; -use patternsleuth_image::MemoryTrait as _; use patternsleuth_scanner::Pattern; -use crate::{ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}; +use crate::{Result, ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util}; /// public: __cdecl FName::FName(wchar_t const *, enum EFindName) #[derive(Debug, PartialEq)] @@ -114,7 +113,7 @@ impl_resolver_singleton!(ElfImage, FNameCtorWchar, |ctx| async { }); impl_resolver_singleton!(PEImage, FNameCtorWchar, |ctx| async { - use crate::{Context, MemoryTrait}; + use crate::Context; use futures::join; use iced_x86::{Code, Decoder, DecoderOptions}; @@ -217,7 +216,7 @@ impl_resolver_singleton!(ElfImage, FNameToString, |ctx| async { }); impl_resolver_singleton!(PEImage, FNameToString, |ctx| async { - use crate::{Context, MemoryTrait}; + use crate::Context; use futures::join; use iced_x86::{Code, Decoder, DecoderOptions}; diff --git a/patternsleuth_resolvers/src/unreal/ftext.rs b/patternsleuth_resolvers/src/unreal/ftext.rs index b72fece..812b6f8 100644 --- a/patternsleuth_resolvers/src/unreal/ftext.rs +++ b/patternsleuth_resolvers/src/unreal/ftext.rs @@ -4,9 +4,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; -use crate::{ - MemoryTrait, {impl_resolver_singleton, try_ensure_one}, -}; +use crate::{impl_resolver_singleton, try_ensure_one}; /// private: __cdecl FText::FText(class FString &&) #[derive(Debug, PartialEq)] @@ -22,22 +20,61 @@ impl_resolver_singleton!(all, FTextFString, |ctx| async { Indirect, } let patterns = [ - (Directness::Indirect, "40 53 48 83 ec ?? 48 8b d9 e8 | ?? ?? ?? ?? 83 4b ?? 12 48 8b c3 48 83 ?? ?? 5b c3"), - (Directness::Indirect, "eb 12 48 8d ?? 24 ?? e8 | ?? ?? ?? ?? ?? 02 00 00 00 48 8b 10 48 89 17"), - (Directness::Indirect, "eb 12 48 8d ?? 24 ?? e8 | ?? ?? ?? ?? ?? 02 00 00 00 48 8b 10 89"), - (Directness::Direct, "48 89 5C 24 10 48 89 6C 24 18 56 57 41 54 41 56 41 57 48 83 EC 40 45 33 E4 48 8B F1 41 8B DC 4C 8B F2 89 5C 24 70 41 8D 4C 24 70 E8 ?? ?? ?? FF 48 8B F8 48 85 C0 0F 84 ?? 00 00 00 49 63 5E 08 ?? 8B ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 8B ?? EB 2E 45 33 C0 48 8D 4C 24 20 8B D3 E8"), - (Directness::Direct, "48 89 5C 24 10 48 89 6C 24 18 57 48 83 EC 50 33 ED 48 8D 05 ?? ?? ?? 03 48 8B F9 48 89 6C 24 38 48 8B DA 48 89 6C 24 48 48 89 44 24 30 8D 4D 60 48 89 44 24 40 E8 ?? ?? ?? FF 4C 8B C0 48 85 C0 74 65 0F 10 44 24 30 C7 40 08 01 00 00 00 0F 10 4C 24 40 C7 40 0C 01 00 00 00 48 8D 05 ?? ?? ?? 03 49 89 00"), - (Directness::Direct, "48 89 5C 24 10 48 89 6C 24 18 56 57 41 54 41 56 41 57 48 83 EC 50 45 33 E4 48 8B F9 41 8B DC 4C 8B F2 89 9C 24 80 00 00 00 41 8D 4C 24 70 E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 98 00 00 00 49 63 5E 08 41 8B EC 4D 8B 3E 4C 89 64 24 20 89 5C 24 28 85 DB 75 05 45 8B FC EB 2E 45 33 C0 48 8D 4C 24 20 8B"), - (Directness::Direct, "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 54 41 56 41 57 48 83 EC 40 4C 8B F1 48 8B F2"), - (Directness::Direct, "48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 45 33 E4 48 8B F1"), - (Directness::Direct, "40 53 56 48 83 EC 48 33 DB 48 89 6C 24 68 48 8B F1 48 89 7C 24 70 4C 89 74 24 78 4C 8B F2 89 5C 24 60 8D 4B 70 E8 ?? ?? ?? FF 48 8B F8 48 85 C0 0F 84 9E 00 00 00 49 63 5E 08 33 ED 4C 89 7C 24 40 4D 8B 3E 48 89 6C 24 20 89 5C 24 28 85 DB 75 05 45 33 FF EB 2E 45 33 C0 48 8D 4C 24 20 8B D3 E8"), - (Directness::Direct, "41 57 41 56 41 54 56 57 55 53 48 83 EC 40 48 89 D7 48 89 CE 48 8B 0D"), + ( + Directness::Indirect, + "40 53 48 83 ec ?? 48 8b d9 e8 | ?? ?? ?? ?? 83 4b ?? 12 48 8b c3 48 83 ?? ?? 5b c3", + ), + ( + Directness::Indirect, + "eb 12 48 8d ?? 24 ?? e8 | ?? ?? ?? ?? ?? 02 00 00 00 48 8b 10 48 89 17", + ), + ( + Directness::Indirect, + "eb 12 48 8d ?? 24 ?? e8 | ?? ?? ?? ?? ?? 02 00 00 00 48 8b 10 89", + ), + ( + Directness::Direct, + "48 89 5C 24 10 48 89 6C 24 18 56 57 41 54 41 56 41 57 48 83 EC 40 45 33 E4 48 8B F1 41 8B DC 4C 8B F2 89 5C 24 70 41 8D 4C 24 70 E8 ?? ?? ?? FF 48 8B F8 48 85 C0 0F 84 ?? 00 00 00 49 63 5E 08 ?? 8B ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 8B ?? EB 2E 45 33 C0 48 8D 4C 24 20 8B D3 E8", + ), + ( + Directness::Direct, + "48 89 5C 24 10 48 89 6C 24 18 57 48 83 EC 50 33 ED 48 8D 05 ?? ?? ?? 03 48 8B F9 48 89 6C 24 38 48 8B DA 48 89 6C 24 48 48 89 44 24 30 8D 4D 60 48 89 44 24 40 E8 ?? ?? ?? FF 4C 8B C0 48 85 C0 74 65 0F 10 44 24 30 C7 40 08 01 00 00 00 0F 10 4C 24 40 C7 40 0C 01 00 00 00 48 8D 05 ?? ?? ?? 03 49 89 00", + ), + ( + Directness::Direct, + "48 89 5C 24 10 48 89 6C 24 18 56 57 41 54 41 56 41 57 48 83 EC 50 45 33 E4 48 8B F9 41 8B DC 4C 8B F2 89 9C 24 80 00 00 00 41 8D 4C 24 70 E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 98 00 00 00 49 63 5E 08 41 8B EC 4D 8B 3E 4C 89 64 24 20 89 5C 24 28 85 DB 75 05 45 8B FC EB 2E 45 33 C0 48 8D 4C 24 20 8B", + ), + ( + Directness::Direct, + "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 54 41 56 41 57 48 83 EC 40 4C 8B F1 48 8B F2", + ), + ( + Directness::Direct, + "48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 45 33 E4 48 8B F1", + ), + ( + Directness::Direct, + "40 53 56 48 83 EC 48 33 DB 48 89 6C 24 68 48 8B F1 48 89 7C 24 70 4C 89 74 24 78 4C 8B F2 89 5C 24 60 8D 4B 70 E8 ?? ?? ?? FF 48 8B F8 48 85 C0 0F 84 9E 00 00 00 49 63 5E 08 33 ED 4C 89 7C 24 40 4D 8B 3E 48 89 6C 24 20 89 5C 24 28 85 DB 75 05 45 33 FF EB 2E 45 33 C0 48 8D 4C 24 20 8B D3 E8", + ), + ( + Directness::Direct, + "41 57 41 56 41 54 56 57 55 53 48 83 EC 40 48 89 D7 48 89 CE 48 8B 0D", + ), // FText::AsCultureInvariant on Linux - (Directness::Indirect, "48 85 c9 74 13 f0 83 41 08 01 eb 0c 48 89 df e8 | ?? ?? ?? ?? 48 8d 43 10"), + ( + Directness::Indirect, + "48 85 c9 74 13 f0 83 41 08 01 eb 0c 48 89 df e8 | ?? ?? ?? ?? 48 8d 43 10", + ), // FText::FText signature - (Directness::Direct, "41 57 41 56 53 48 83 ec 20 49 89 f7 49 89 fe 0f 57 c0 0f 29 44 24 10 0f 29 04 24 48 8d 5c 24 10 48 89 e7 e8 ?? ?? ?? ?? 48 89 df e8 ?? ?? ?? ?? bf 60 00 00 00 e8"), + ( + Directness::Direct, + "41 57 41 56 53 48 83 ec 20 49 89 f7 49 89 fe 0f 57 c0 0f 29 44 24 10 0f 29 04 24 48 8d 5c 24 10 48 89 e7 e8 ?? ?? ?? ?? 48 89 df e8 ?? ?? ?? ?? bf 60 00 00 00 e8", + ), // UE 5.4 - (Directness::Direct, "48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 33 F6 48 8B D9 48 89 44 24"), + ( + Directness::Direct, + "48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 33 F6 48 8B D9 48 89 44 24", + ), ]; let res = join_all( diff --git a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs index 76e628e..f6a927e 100644 --- a/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs +++ b/patternsleuth_resolvers/src/unreal/fuobject_hash_tables.rs @@ -4,9 +4,7 @@ use futures::future::join_all; use patternsleuth_scanner::Pattern; -use crate::{ - MemoryTrait, {impl_resolver_singleton, try_ensure_one, Result}, -}; +use crate::{Result, impl_resolver_singleton, try_ensure_one}; /// public: static class FUObjectHashTables & __cdecl FUObjectHashTables::Get(void) #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/gengine.rs b/patternsleuth_resolvers/src/unreal/gengine.rs index 4f38186..c4a2213 100644 --- a/patternsleuth_resolvers/src/unreal/gengine.rs +++ b/patternsleuth_resolvers/src/unreal/gengine.rs @@ -4,9 +4,9 @@ use iced_x86::Register; use itertools::Itertools as _; use crate::{ - disassemble::{disassemble, Control}, + disassemble::{Control, disassemble}, image::Image, - {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + {Result, impl_resolver_singleton, try_ensure_one, unreal::util}, }; #[derive(Debug, PartialEq)] diff --git a/patternsleuth_resolvers/src/unreal/gmalloc.rs b/patternsleuth_resolvers/src/unreal/gmalloc.rs index 706c4ab..e2bf6f7 100644 --- a/patternsleuth_resolvers/src/unreal/gmalloc.rs +++ b/patternsleuth_resolvers/src/unreal/gmalloc.rs @@ -6,9 +6,8 @@ use itertools::Itertools; use patternsleuth_scanner::Pattern; use crate::{ - disassemble::{disassemble, Control}, - MemoryTrait, - {impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + disassemble::{Control, disassemble}, + {Result, impl_resolver_singleton, try_ensure_one, unreal::util}, }; #[derive(Debug, PartialEq)] @@ -37,7 +36,7 @@ impl_resolver_singleton!(all, GMallocPatterns, |ctx| async { "48 ?? ?? f0 ?? 0f b1 ?? | ?? ?? ?? ?? 74 ?? ?? 85 ?? 74 ?? ?? 8b", // Purgatory "eb 03 ?? 8b ?? 48 8b ?? f0 ?? 0f b1 ?? | ?? ?? ?? ?? 74 ?? ?? 85 ?? 74 ?? ?? 8b", // Purg_notX "e8 ?? ?? ?? ?? 48 8b ?? f0 ?? 0f b1 ?? | ?? ?? ?? ?? 74 ?? ?? 85 ?? 74 ?? ?? 8b", // Purg_withX - "48 85 C9 74 2E 53 48 83 EC 20 48 8B D9 48 8B ?? | ?? ?? ?? ?? 48 85 C9", // A + "48 85 C9 74 2E 53 48 83 EC 20 48 8B D9 48 8B ?? | ?? ?? ?? ?? 48 85 C9", // A "75 ?? E8 ?? ?? ?? ?? 48 8b 0d | ?? ?? ?? ?? 48 8b ?? 48 ?? ?? ff 50 ?? 48 83 c4 ?? ?? c3", // bnew1 "48 85 C9 74 ?? 4C 8B 05 | ?? ?? ?? ?? 4D 85 C0 0F 84", // altshort "48 ?? ?? ?? ?? ?? ?? e8 ?? ?? ?? ?? 48 8b 0d | ?? ?? ?? ?? 48 8b 01 ff 50 ?? 84 c0 75 ?? b9 38 00 00 00", // gcreatemallocshort diff --git a/patternsleuth_resolvers/src/unreal/guobject_array.rs b/patternsleuth_resolvers/src/unreal/guobject_array.rs index b3facec..4abfecf 100644 --- a/patternsleuth_resolvers/src/unreal/guobject_array.rs +++ b/patternsleuth_resolvers/src/unreal/guobject_array.rs @@ -4,10 +4,7 @@ use futures::{future::join_all, try_join}; use patternsleuth_scanner::Pattern; -use crate::{ - MemoryTrait, - {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, -}; +use crate::{Result, ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util}; #[derive(Debug, PartialEq)] #[cfg_attr( diff --git a/patternsleuth_resolvers/src/unreal/kismet.rs b/patternsleuth_resolvers/src/unreal/kismet.rs index 2220d19..7086272 100644 --- a/patternsleuth_resolvers/src/unreal/kismet.rs +++ b/patternsleuth_resolvers/src/unreal/kismet.rs @@ -2,10 +2,10 @@ use std::fmt::Debug; use futures::future::join_all; use iced_x86::{Decoder, DecoderOptions, Instruction}; -use patternsleuth_image::{Addressable as _, Matchable as _, MemoryTrait as _}; +use patternsleuth_image::Addressable as _; use patternsleuth_scanner::Pattern; -use crate::{bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result}; +use crate::{Result, bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one}; /// public: void __cdecl UObject::SkipFunction(struct FFrame &, void *const, class UFunction *) #[derive(Debug, PartialEq)] @@ -17,9 +17,8 @@ pub struct UObjectSkipFunction(pub usize); impl_resolver_singleton!(all, UObjectSkipFunction, |ctx| async { let patterns = [ "40 55 41 54 41 55 41 56 41 57 48 83 EC 30 48 8D 6C 24 20 48 89 5D 40 48 89 75 48 48 89 7D 50 48 8B 05 ?? ?? ?? ?? 48 33 C5 48 89 45 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 4D 8B ?? ?? 8B ?? 85 ?? 75 05 41 8B FC EB ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 ?? E0", - // linux - "55 01001??? 89 e5 01000??? 57 01000??? 56 01000??? 55 01000??? 54 53 50 01001??? 89 cf 01001??? 89 d6 01001??? 89 f3 01001??? 63 51 58 01001??? 85 d2 74 ?? 01001??? 63 67 5c 01001??? 83 fc 10 7f ?? 01001??? 89 e4 01001??? 8d 42 1e 01001??? 83 e0 f0 01001??? 29 c4 01001??? 89 e4 eb ??" + "55 01001??? 89 e5 01000??? 57 01000??? 56 01000??? 55 01000??? 54 53 50 01001??? 89 cf 01001??? 89 d6 01001??? 89 f3 01001??? 63 51 58 01001??? 85 d2 74 ?? 01001??? 63 67 5c 01001??? 83 fc 10 7f ?? 01001??? 89 e4 01001??? 8d 42 1e 01001??? 83 e0 f0 01001??? 29 c4 01001??? 89 e4 eb ??", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; @@ -112,10 +111,10 @@ impl_resolver_singleton!(all, FFrameStep, |ctx| async { pub struct FFrameStepExplicitProperty(pub usize); impl_resolver_singleton!(all, FFrameStepExplicitProperty, |ctx| async { let patterns = [ - "41 8B 40 40 4D 8B C8 4C 8B D1 48 0F BA E0 08 73 ?? 48 8B ?? ?? ?? ?? 00 ?? ?? ?? ?? ?? ?? ?? 00 48 8B 40 10 4C 39 08 75 F7 48 8B 48 08 49 89 4A 38 ?? ?? ?? 40 ?? ?? ?? ?? ?? 4C ?? 41 ?? 49", - "48 89 5C 24 ?? 48 89 ?? 24 ?? 57 48 83 EC 20 41 8B 40 40 49 8B D8 48 8B ?? 48 8B F9 48 0F BA E0 08 73 ?? 48 8B ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 8B 40 10 48 39 18 75 F7 48 8B ?? 08 48 89 ?? 38 48", - // linux - "41 57 41 56 53 48 89 d3 49 89 ff f6 42 41 01 75 ?? 01001??? 89 f6 01001??? 8b 47 28 01001??? 63 4b 4c 01001??? 01 c1 01001??? 89 4f 38 01001??? 89 47 40 01001??? 8b 03 01001??? 89 df ff 90 88 00 00 00 84 c0 74 ?? 01001??? 8b 43 08 b9 00 00 00 24 23 48 10 01001??? 8b 7f 40 81 f9 00 00 00 04", + "41 8B 40 40 4D 8B C8 4C 8B D1 48 0F BA E0 08 73 ?? 48 8B ?? ?? ?? ?? 00 ?? ?? ?? ?? ?? ?? ?? 00 48 8B 40 10 4C 39 08 75 F7 48 8B 48 08 49 89 4A 38 ?? ?? ?? 40 ?? ?? ?? ?? ?? 4C ?? 41 ?? 49", + "48 89 5C 24 ?? 48 89 ?? 24 ?? 57 48 83 EC 20 41 8B 40 40 49 8B D8 48 8B ?? 48 8B F9 48 0F BA E0 08 73 ?? 48 8B ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 8B 40 10 48 39 18 75 F7 48 8B ?? 08 48 89 ?? 38 48", + // linux + "41 57 41 56 53 48 89 d3 49 89 ff f6 42 41 01 75 ?? 01001??? 89 f6 01001??? 8b 47 28 01001??? 63 4b 4c 01001??? 01 c1 01001??? 89 4f 38 01001??? 89 47 40 01001??? 8b 03 01001??? 89 df ff 90 88 00 00 00 84 c0 74 ?? 01001??? 8b 43 08 b9 00 00 00 24 23 48 10 01001??? 8b 7f 40 81 f9 00 00 00 04", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; @@ -141,7 +140,7 @@ impl_resolver!(all, FFrameStepViaExec, |ctx| async { "48 89 5C 24 08 48 89 74 24 10 57 48 83 EC ?? 33 FF 33 C0 49 8B F0 48 8B DA 48 8B CA 48 89 7C 24 20 48 89 7C 24 28 48 39 42 20 74 10 48 8B 52 18 4C 8D 44 24 20 E8 [ ?? ?? ?? ?? ] EB 1C 4C 8B 82 80 00 00 00 49 8B 40 ?? 48 89 82 80 00 00 00 48 8D 54 24 20 E8 [ ?? ?? ?? ?? ] 48 8B 43 20 48 8D 4C 24 20 48 85 C0", "48 89 5C 24 08 48 89 74 24 10 57 48 83 EC ?? 33 FF 49 8B F0 48 8B DA 48 89 7C 24 20 48 ?? ?? ?? ?? ?? ?? ?? 48 39 7A 20 74 10 48 8B 52 18 4C 8D 44 24 20 E8 [ ?? ?? ?? ?? ] EB 1C 4C 8B 82 80 00 00 00 49 8B 40 ?? 48 89 82 80 00 00 00 48 8D 54 24 20 E8 [ ?? ?? ?? ?? ] 48 8B 43 20 48 8D 4C 24 20 48 85 C0 40 0F", "48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 30 33 ?? 49 8B F0 48 89 ?? 24 20 48 8B ?? 48 89 ?? 24 28 E8 [ ?? ?? ?? ?? ] 48 8B ?? 48 39 ?? 20 74 10 48 8B ?? 18 4C 8D 44 24 20 E8 ?? ?? ?? ?? EB 1C 4C 8B ?? ?? 00 00 00 48 8D 54 24 20 49 8B 40 20 48 89 ?? ?? 00 00 00 E8 [ ?? ?? ?? ?? ] 48 8B ?? 20 48", - "41 56 53 01001??? 83 ec 18 01001??? 89 d6 01001??? 89 f3 0f 57 c0 0f 29 04 ??100??? e8 ?? ?? ?? ?? 01001??? 83 7b 20 00 74 ?? 01001??? 8b 73 18 01001??? 89 e2 01001??? 89 df e8 [ ?? ?? ?? ?? ] eb ?? 01001??? 8b 93 88 00 00 00 01001??? 8b 42 20 01001??? 89 83 88 00 00 00 01001??? 89 e6 01001??? 89 df e8 [ ?? ?? ?? ?? ]" + "41 56 53 01001??? 83 ec 18 01001??? 89 d6 01001??? 89 f3 0f 57 c0 0f 29 04 ??100??? e8 ?? ?? ?? ?? 01001??? 83 7b 20 00 74 ?? 01001??? 8b 73 18 01001??? 89 e2 01001??? 89 df e8 [ ?? ?? ?? ?? ] eb ?? 01001??? 8b 93 88 00 00 00 01001??? 8b 42 20 01001??? 89 83 88 00 00 00 01001??? 89 e6 01001??? 89 df e8 [ ?? ?? ?? ?? ]", ]; let res = join_all( diff --git a/patternsleuth_resolvers/src/unreal/mod.rs b/patternsleuth_resolvers/src/unreal/mod.rs index 1e15955..c836931 100644 --- a/patternsleuth_resolvers/src/unreal/mod.rs +++ b/patternsleuth_resolvers/src/unreal/mod.rs @@ -22,13 +22,13 @@ use std::{ use futures::future::join_all; use iced_x86::FlowControl; use itertools::Itertools; -use patternsleuth_image::{Addressable as _, Matchable as _}; +use patternsleuth_image::Addressable as _; use patternsleuth_scanner::Pattern; use crate::{ - disassemble::{disassemble, Control}, - Image, MemoryTrait, - {bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one, Result}, + Image, + disassemble::{Control, disassemble}, + {Result, bail_out, ensure_one, impl_resolver, impl_resolver_singleton, try_ensure_one}, }; #[allow(unused)] @@ -136,7 +136,7 @@ pub mod util { .collect()) } - pub fn find_calls<'a>(img: &Image, f: usize) -> Result> { + pub fn find_calls(img: &Image, f: usize) -> Result> { let mut calls = vec![]; disassemble(img, f, |inst| { diff --git a/patternsleuth_resolvers/src/unreal/save_game.rs b/patternsleuth_resolvers/src/unreal/save_game.rs index 4e78a75..1754b46 100644 --- a/patternsleuth_resolvers/src/unreal/save_game.rs +++ b/patternsleuth_resolvers/src/unreal/save_game.rs @@ -92,7 +92,7 @@ impl_resolver_singleton!(all, UGameplayStaticsLoadGameFromSlot, |ctx| async { "48 89 5C 24 08 55 56 57 48 8D AC 24 ?? FF FF FF 48 81 EC ?? 01 00 00 48 8B D9 ?? ?? ?? ?? ?? ?? ?? ?? ?? 8B F2 48 85 C9 75 26 8D 4F 08 E8 ?? ?? ?? FF 48 8B C8 48 85 C0 74 0C 48 8D 05 ?? ?? ?? ?? 48 89 01 EB 03 48 8B CF 48 89 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 48 8B C8 48 85 C0 0F 84 ?? ?? 00 00 8B 43", "48 89 5C 24 18 55 56 57 48 8D AC 24 ?? FF FF FF 48 81 EC ?? ?? 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? 00 00 00 48 8B D9 33 FF 48 8B 0D ?? ?? ?? ?? 8B F2 48 85 C9 75 26 8D 4F 08 E8 ?? ?? ?? ?? 48 8B C8 48 85 C0 74 0C 48 8D 05 ?? ?? ?? ?? 48 89 01 EB 03 48 8B CF 48 89 0D ?? ?? ?? ?? 48 8B 01", // linux - "55 53 48 83 ec 18 89 f5 01001??? 89 fb 0f 57 c0 0f 29 04 ??100100 01001??? 8b 3d ?? ?? ?? ?? 01001??? 85 ff 74 ?? 01001??? 8b 07 ff 50 48 01001??? 85 c0 75 ?? eb ?? bf 08 00 00 00 e8 ?? ?? ?? ?? 01001??? 89 c7 01001??? c7 00 ?? ?? ?? ?? 01001??? 89 05 ?? ?? ?? ?? 01001??? 8b 07 ff 01010??? ?? 01001??? 85 c0 74 ??" + "55 53 48 83 ec 18 89 f5 01001??? 89 fb 0f 57 c0 0f 29 04 ??100100 01001??? 8b 3d ?? ?? ?? ?? 01001??? 85 ff 74 ?? 01001??? 8b 07 ff 50 48 01001??? 85 c0 75 ?? eb ?? bf 08 00 00 00 e8 ?? ?? ?? ?? 01001??? 89 c7 01001??? c7 00 ?? ?? ?? ?? 01001??? 89 05 ?? ?? ?? ?? 01001??? 8b 07 ff 01010??? ?? 01001??? 85 c0 74 ??", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; diff --git a/patternsleuth_resolvers/src/unreal/static_construct_object.rs b/patternsleuth_resolvers/src/unreal/static_construct_object.rs index 2beded7..f94baf3 100644 --- a/patternsleuth_resolvers/src/unreal/static_construct_object.rs +++ b/patternsleuth_resolvers/src/unreal/static_construct_object.rs @@ -4,9 +4,8 @@ use futures::{future::join_all, join}; use patternsleuth_scanner::Pattern; use crate::{ - disassemble::{disassemble, Control}, - MemoryTrait, - {ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util, Result}, + disassemble::{Control, disassemble}, + {Result, ensure_one, impl_resolver_singleton, try_ensure_one, unreal::util}, }; /// class UObject * __cdecl StaticConstructObject_Internal(struct FStaticConstructObjectParameters const &) @@ -41,18 +40,18 @@ impl_resolver_singleton!(all, StaticConstructObjectInternalPatterns, |ctx| async "E8 | ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? C0 E9 ?? 32 88 ?? ?? ?? ?? 80 E1 01 30 88 ?? ?? ?? ?? 48", "E8 | ?? ?? ?? ?? 48 8B D8 48 39 75 30 74 15", /* - 03f4df3f c6 44 24 MOV byte ptr [RSP + local_88 ],0x0 - 30 00 - 03f4df44 0f 57 c0 XORPS XMM0 ,XMM0 - 03f4df47 0f 11 44 MOVUPS xmmword ptr [RSP + local_80[0] ],XMM0 - 24 38 - 03f4df4c 4c 89 ff MOV RDI ,R15 - 03f4df4f e8 2c b6 CALL StaticConstructObject_Internal undefined StaticConstructObject_ - 02 03 - 03f4df54 48 89 c3 MOV RBX ,RAX - - */ - "c6 44 24 30 00 0f 57 c0 0f 11 44 24 38 4c 89 ff e8 | ?? ?? ?? ?? 48 89" + 03f4df3f c6 44 24 MOV byte ptr [RSP + local_88 ],0x0 + 30 00 + 03f4df44 0f 57 c0 XORPS XMM0 ,XMM0 + 03f4df47 0f 11 44 MOVUPS xmmword ptr [RSP + local_80[0] ],XMM0 + 24 38 + 03f4df4c 4c 89 ff MOV RDI ,R15 + 03f4df4f e8 2c b6 CALL StaticConstructObject_Internal undefined StaticConstructObject_ + 02 03 + 03f4df54 48 89 c3 MOV RBX ,RAX + + */ + "c6 44 24 30 00 0f 57 c0 0f 11 44 24 38 4c 89 ff e8 | ?? ?? ?? ?? 48 89", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; @@ -124,8 +123,9 @@ impl_resolver_singleton!(PEImage, StaticConstructObjectInternalString, |ctx| asy use itertools::Itertools; use crate::{ + Image, disassemble::disassemble_single, - Image, MemoryTrait, {bail_out, Context, Result}, + {Context, Result, bail_out}, }; let strings = join_all( diff --git a/patternsleuth_scanner/benches/scan.rs b/patternsleuth_scanner/benches/scan.rs index ce4ebc7..efef510 100644 --- a/patternsleuth_scanner/benches/scan.rs +++ b/patternsleuth_scanner/benches/scan.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use patternsleuth_scanner::*; fn gig(c: &mut Criterion) { @@ -9,7 +9,7 @@ fn gig(c: &mut Criterion) { let needle = b"\xf9\x82\xdb\xdb\x2d\x32\x6f\x15\x11\x44\x54\xf4\xc8\xaa\xd1\x72\x53\x96\xa5\x7b\x22\x24\x94\x7f\xec\x28\xc7\xe0\x5e\xd4\xae\x39"; - data.extend((0..size - needle.len()).map(|_| rng.gen::())); + data.extend((0..size - needle.len()).map(|_| rng.r#gen::())); data.extend(needle); let pattern = Pattern::new("f9 82 db db 2d ?? 6f 15 ?? 44 54 f4 c8 aa d1 72 53 ?? a5 7b 22 24 94 7f ec 28 ?? e0 5e d4 ae 39").unwrap(); diff --git a/patternsleuth_scanner/src/lib.rs b/patternsleuth_scanner/src/lib.rs index 018da66..1cae147 100644 --- a/patternsleuth_scanner/src/lib.rs +++ b/patternsleuth_scanner/src/lib.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{Context, Error, Result, bail}; #[derive(Clone, Eq, PartialEq)] pub struct PatternSimple { @@ -35,8 +35,8 @@ fn fmt_byte(f: &mut std::fmt::Formatter<'_>, sig: u8, mask: u8) -> std::fmt::Res write!(f, "{:X}?", sig >> 4)?; } else { for bit in (0..8).rev() { - if mask >> bit & 1 == 1 { - write!(f, "{}", sig >> bit & 1)?; + if (mask >> bit) & 1 == 1 { + write!(f, "{}", (sig >> bit) & 1)?; } else { write!(f, "?")?; } @@ -876,7 +876,7 @@ mod test { let patterns = [&Pattern::new("01 02").unwrap()]; // obtuse generator to test every combination of chunk boundaries - let data: Vec<_> = std::iter::repeat([1, 2, 3]).take(32).flatten().collect(); + let data: Vec<_> = std::iter::repeat_n([1, 2, 3], 32).flatten().collect(); let matches: Vec<_> = (0..3) .map(|offset| { (0..len / 3) From 87d284d7c97c664f9eaaebf2c6910ea94d16c432 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 26 Feb 2025 15:56:55 -0600 Subject: [PATCH 09/19] Refactor PEImage building --- patternsleuth_cli/src/db.rs | 6 +- patternsleuth_image/src/image/elf.rs | 2 +- patternsleuth_image/src/image/mod.rs | 23 +-- patternsleuth_image/src/image/pe.rs | 144 +++++++++++++----- patternsleuth_image/src/lib.rs | 133 ++++------------ patternsleuth_image/src/process/external.rs | 13 +- patternsleuth_image/src/process/internal.rs | 7 +- patternsleuth_resolvers/Cargo.toml | 2 +- patternsleuth_resolvers/src/lib.rs | 10 +- patternsleuth_resolvers/src/unreal/gengine.rs | 2 +- 10 files changed, 166 insertions(+), 176 deletions(-) diff --git a/patternsleuth_cli/src/db.rs b/patternsleuth_cli/src/db.rs index 31f55fc..f58dae7 100644 --- a/patternsleuth_cli/src/db.rs +++ b/patternsleuth_cli/src/db.rs @@ -264,7 +264,7 @@ pub(crate) fn view(command: CommandViewSymbol) -> Result<()> { functions.push(Function { game: function.path, address: function.start, - data: img.memory[function.start..function.end].to_vec(), + data: img.memory.range(function.start..function.end)?.to_vec(), }); } @@ -306,7 +306,7 @@ pub(crate) fn view(command: CommandViewSymbol) -> Result<()> { functions.push(Function { game: exe_path.to_string_lossy().to_string(), address: start, - data: exe.memory[bounds].to_vec(), + data: exe.memory.range(bounds)?.to_vec(), }); } } @@ -649,7 +649,7 @@ pub(crate) fn build(command: CommandBuildIndex) -> Result<()> { |function| -> Result<()> { let range = function; - let bytes = &exe.memory[range.clone()]; + let bytes = exe.memory.range(range.clone())?; tx.send(Insert::Function(( exe_path.to_string_lossy().to_string(), diff --git a/patternsleuth_image/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs index 5c2c86e..88ad6aa 100644 --- a/patternsleuth_image/src/image/elf.rs +++ b/patternsleuth_image/src/image/elf.rs @@ -133,7 +133,7 @@ impl ElfImage { .iter() .find(|s| s.name == ".text") .context("Cannot find .text section")? - .address(); + .address; let ehframe_hdr_start = base_address + p.p_vaddr as usize; let bases = BaseAddresses::default() .set_eh_frame_hdr(ehframe_hdr_start as _) diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index 82e05ab..865baf6 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -29,10 +29,11 @@ image_type_dispatch! { } pub use _image_type_reflection as image_type_reflection; +use pe::PEImageBuilder; pub struct Image<'data> { pub base_address: usize, - pub memory: Box + 'data>, + pub memory: Box + 'data>, #[cfg(feature = "symbols")] pub symbols: Option>, pub imports: HashMap>, @@ -55,28 +56,16 @@ impl<'data> Image<'data> { ElfImage::read_inner(base_addr, exe_path, cache_functions, object) } #[cfg(feature = "image-pe")] - object::File::Pe64(_) => { - PEImage::read_inner(base_addr, exe_path, cache_functions, object) - } + object::File::Pe64(_) => PEImageBuilder::new() + .memory_from_object(object)? + .exe_path(exe_path.as_ref().map(AsRef::as_ref)) + .build(), _ => Err(Error::msg("Unsupported file format")), } } pub fn builder() -> ImageBuilder { Default::default() } - //pub fn resolve( - // &self, - // resolver: &'static resolvers::ResolverFactory, - //) -> resolvers::Result { - // resolvers::resolve(self, resolver) - //} - - //pub fn resolve_many( - // &self, - // resolvers: &[fn() -> &'static resolvers::DynResolverFactory], - //) -> Vec>> { - // resolvers::resolve_many(self, resolvers) - //} pub fn scan<'patterns, S>( &self, diff --git a/patternsleuth_image/src/image/pe.rs b/patternsleuth_image/src/image/pe.rs index 686fb75..916d53e 100644 --- a/patternsleuth_image/src/image/pe.rs +++ b/patternsleuth_image/src/image/pe.rs @@ -7,7 +7,7 @@ use itertools::Itertools; use super::{Image, ImageType}; #[cfg(feature = "symbols")] use crate::symbols; -use crate::{MemTraitNew, Memory, MemoryAccessError, RuntimeFunction}; +use crate::{Memory, MemoryAccessError, RuntimeFunction, SectionedMemoryTrait}; use object::Object; pub struct PEImage { @@ -218,25 +218,15 @@ impl Image<'_> { } } -impl PEImage { - /// Read and parse ELF object, using data from memory - pub fn read_inner_memory<'data, P: AsRef>( - base_address: usize, - #[allow(unused_variables)] exe_path: Option

, - cache_functions: bool, - memory: Box + 'data>, - object: object::File<'_>, - ) -> Result, anyhow::Error> { - #[cfg(feature = "symbols")] - let symbols = if let Some(exe_path) = exe_path { - let pdb_path = exe_path.as_ref().with_extension("pdb"); - pdb_path - .exists() - .then(|| symbols::dump_pdb_symbols(pdb_path, base_address)) - .transpose()? - } else { - None - }; +/// Helper struct to extract info from object::File without hitting lifetime issues with memory +struct PEImagePartial { + base_address: usize, + imports: HashMap>, + exception_directory_range: Range, +} +impl PEImagePartial { + fn new(object: object::File<'_>) -> Result { + let base_address = object.relative_address_base() as usize; let get_ex_dir = || -> Result> { Ok(match object { @@ -287,15 +277,41 @@ impl PEImage { _ => bail!("not a PE file"), }) }; + Ok(Self { + base_address, + imports: get_imports().unwrap_or_default(), + exception_directory_range: get_ex_dir().unwrap_or_default(), + }) + } +} + +impl PEImage { + fn read_inner_memory<'data, P: AsRef>( + base_address: usize, + #[allow(unused_variables)] exe_path: Option

, + cache_functions: bool, + memory: Box + 'data>, + partial: PEImagePartial, + ) -> Result, anyhow::Error> { + #[cfg(feature = "symbols")] + let symbols = if let Some(exe_path) = exe_path { + let pdb_path = exe_path.as_ref().with_extension("pdb"); + pdb_path + .exists() + .then(|| symbols::dump_pdb_symbols(pdb_path, base_address)) + .transpose()? + } else { + None + }; let mut new = Image { base_address, memory, #[cfg(feature = "symbols")] symbols, - imports: get_imports().unwrap_or_default(), + imports: partial.imports, image_type: ImageType::PEImage(PEImage { - exception_directory_range: get_ex_dir().unwrap_or_default(), + exception_directory_range: partial.exception_directory_range, exception_children_cache: Default::default(), }), }; @@ -305,21 +321,79 @@ impl PEImage { } Ok(new) } +} - pub fn read_inner>( - base_addr: Option, - exe_path: Option

, - cache_functions: bool, - object: object::File<'_>, - ) -> Result, anyhow::Error> { - let base_address = base_addr.unwrap_or(object.relative_address_base() as usize); - let memory = Memory::new(&object)?; - Self::read_inner_memory( - base_address, - exe_path, +pub struct PEImageBuilder<'data, 'path> { + partial: Option, + memory: Option + 'data>>, + exe_path: Option<&'path std::path::Path>, + cache_functions: bool, +} +impl Default for PEImageBuilder<'_, '_> { + fn default() -> Self { + Self::new() + } +} +impl<'data, 'path> PEImageBuilder<'data, 'path> { + pub fn new() -> Self { + Self { + partial: None, + memory: None, + exe_path: None, + cache_functions: false, + } + } + pub fn cache_functions(self, cache_functions: bool) -> Self { + Self { cache_functions, - Box::new(memory), - object, + ..self + } + } + pub fn exe_path(self, exe_path: Option<&'path std::path::Path>) -> Self { + Self { exe_path, ..self } + } + pub fn memory(self, memory: Box + 'data>) -> Self { + Self { + memory: Some(memory), + ..self + } + } + pub fn object(self, object: object::File<'_>) -> Result { + let partial = PEImagePartial::new(object)?; + Ok(Self { + partial: Some(partial), + ..self + }) + } + pub fn object_from_memory( + self, + memory: Box + 'data>, + base_address: usize, + ) -> Result { + let object = object::File::parse(memory.range_from(base_address..)?)?; + let partial = PEImagePartial::new(object)?; + Ok(Self { + partial: Some(partial), + memory: Some(memory), + ..self + }) + } + pub fn memory_from_object(self, object: object::File<'data>) -> Result { + let memory = Box::new(Memory::new(&object)?); + let partial = PEImagePartial::new(object)?; + Ok(Self { + partial: Some(partial), + memory: Some(memory), + ..self + }) + } + pub fn build(self) -> Result> { + PEImage::read_inner_memory( + self.partial.as_ref().unwrap().base_address, + self.exe_path, + self.cache_functions, + self.memory.unwrap(), + self.partial.unwrap(), ) } } diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index c35ccff..040bb5b 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -13,7 +13,7 @@ use scanner::{Pattern, Xref}; use std::{ borrow::Cow, collections::HashMap, - ops::{Index, Range, RangeFrom, RangeTo}, + ops::{Range, RangeFrom, RangeTo}, path::Path, }; @@ -145,19 +145,12 @@ impl RuntimeFunction { } } -pub trait MemTraitNew<'data>: - MemoryTrait<'data> - + Matchable<'data> - + Index - + Index, Output = [u8]> - + Send - + Sync -{ - fn sections(&self) -> Box> + '_>; +pub trait SectionedMemoryTrait<'data>: MemoryTrait<'data> + Send + Sync { + fn sections(&self) -> Box> + '_>; fn get_section_containing( &self, address: usize, - ) -> Result<&dyn SectionTraitNew<'data>, MemoryAccessError> { + ) -> Result<&dyn SectionTrait<'data>, MemoryAccessError> { self.sections() .find(|section| { address >= section.address() && address < section.address() + section.data().len() @@ -165,7 +158,7 @@ pub trait MemTraitNew<'data>: .ok_or(MemoryAccessError::MemoryOutOfBoundsError) } } -pub trait SectionTraitNew<'a>: MemoryBlockTrait<'a> + MemoryTrait<'a> + Send + Sync { +pub trait SectionTrait<'a>: MemoryBlockTrait<'a> + MemoryTrait<'a> + Send + Sync { fn name(&self) -> &str; fn kind(&self) -> object::SectionKind; } @@ -299,6 +292,15 @@ pub trait MemoryTrait<'data> { Ok(String::from_utf16(data)?) } + + fn captures( + &self, + pattern: &Pattern, + address: usize, + ) -> Result>>, MemoryAccessError> { + // TODO bounds check data passed to captures + Ok(pattern.captures(self.range_from(address..)?, address, 0)) + } } impl<'data, T: MemoryBlockTrait<'data>> MemoryTrait<'data> for T { @@ -335,23 +337,11 @@ impl<'data> MemoryTrait<'data> for Memory<'data> { } } -pub struct MemorySection<'data> { - address: usize, - data: Cow<'data, [u8]>, -} -impl<'data> MemoryBlockTrait<'data> for MemorySection<'data> { - fn address(&self) -> usize { - self.address - } - fn data(&self) -> &[u8] { - &self.data - } -} - pub struct NamedMemorySection<'data> { name: String, kind: object::SectionKind, - section: MemorySection<'data>, + address: usize, + data: Cow<'data, [u8]>, } impl<'data> NamedMemorySection<'data> { @@ -364,10 +354,8 @@ impl<'data> NamedMemorySection<'data> { Self { name, kind, - section: MemorySection { - address, - data: data.into(), - }, + address, + data: data.into(), } } } @@ -378,41 +366,35 @@ impl NamedMemorySection<'_> { pub fn kind(&self) -> object::SectionKind { self.kind } - pub fn address(&self) -> usize { - self.section.address() - } - pub fn data(&self) -> &[u8] { - self.section.data() - } pub fn len(&self) -> usize { - self.section.data.len() + self.data.len() } pub fn is_empty(&self) -> bool { - self.section.data.is_empty() + self.data.is_empty() } } impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { fn address(&self) -> usize { - self.section.address() + self.address } fn data(&self) -> &[u8] { - self.section.data() + &self.data } } struct Memory<'data> { sections: Vec>, } -impl<'data> MemTraitNew<'data> for Memory<'data> { - fn sections(&self) -> Box> + '_> { +impl<'data> SectionedMemoryTrait<'data> for Memory<'data> { + fn sections(&self) -> Box> + '_> { Box::new( self.sections .iter() - .map(|s| -> &dyn SectionTraitNew<'data> { s }), + .map(|s| -> &dyn SectionTrait<'data> { s }), ) } } -impl<'data> SectionTraitNew<'data> for NamedMemorySection<'data> { +impl<'data> SectionTrait<'data> for NamedMemorySection<'data> { fn name(&self) -> &str { &self.name } @@ -479,8 +461,7 @@ impl<'data> Memory<'data> { self.sections .iter() .find(|section| { - address >= section.section.address - && address < section.section.address + section.section.data.len() + address >= section.address && address < section.address + section.data.len() }) .ok_or(MemoryAccessError::MemoryOutOfBoundsError) } @@ -490,49 +471,15 @@ impl<'data> Memory<'data> { { self.sections.iter().find_map(|section| { if section.kind == kind { - section - .section - .data - .windows(4) - .enumerate() - .find_map(|(i, slice)| { - filter(section.section.address + i, slice) - .then_some(section.section.address + i) - }) + section.data.windows(4).enumerate().find_map(|(i, slice)| { + filter(section.address + i, slice).then_some(section.address + i) + }) } else { None } }) } } -impl Index for Memory<'_> { - type Output = u8; - fn index(&self, index: usize) -> &Self::Output { - self.sections - .iter() - .find_map(|section| section.section.data.get(index - section.section.address)) - .unwrap() - } -} -impl Index> for Memory<'_> { - type Output = [u8]; - fn index(&self, index: Range) -> &Self::Output { - self.sections - .iter() - .find_map(|section| { - if index.start >= section.section.address - && index.end <= section.section.address + section.section.data.len() - { - let relative_range = - index.start - section.section.address..index.end - section.section.address; - Some(§ion.section.data[relative_range]) - } else { - None - } - }) - .unwrap() - } -} pub trait Addressable { fn rip(&self) -> usize; @@ -552,23 +499,3 @@ impl Addressable for patternsleuth_scanner::Capture<'_> { u32::from_le_bytes(self.data.try_into().unwrap()) } } - -pub trait Matchable<'data> { - fn captures( - &self, - pattern: &Pattern, - address: usize, - ) -> Result>>, MemoryAccessError>; -} - -impl<'data> Matchable<'data> for Memory<'data> { - fn captures( - &self, - pattern: &Pattern, - address: usize, - ) -> Result>>, MemoryAccessError> { - let s = self.get_section_containing(address)?; - // TODO bounds check data passed to captures - Ok(pattern.captures(s.data(), s.address(), address - s.address())) - } -} diff --git a/patternsleuth_image/src/process/external.rs b/patternsleuth_image/src/process/external.rs index f93982d..8ecc06a 100644 --- a/patternsleuth_image/src/process/external.rs +++ b/patternsleuth_image/src/process/external.rs @@ -8,7 +8,7 @@ mod linux { use anyhow::{Context, Result, bail}; use object::{Object, ObjectSection}; - use crate::{Image, Memory, image}; + use crate::{Image, Memory, image::pe::PEImageBuilder}; fn read_process_mem(pid: i32, address: usize, buffer: &mut [u8]) -> Result { unsafe { @@ -90,13 +90,10 @@ mod linux { let memory = Memory::new_external_data(sections)?; - image::pe::PEImage::read_inner_memory::( - object.relative_address_base() as usize, - None, - false, - Box::new(memory), - object, - ) + PEImageBuilder::new() + .object(object)? + .memory(Box::new(memory)) + .build() } } diff --git a/patternsleuth_image/src/process/internal.rs b/patternsleuth_image/src/process/internal.rs index 78966e7..d7b3436 100644 --- a/patternsleuth_image/src/process/internal.rs +++ b/patternsleuth_image/src/process/internal.rs @@ -124,7 +124,7 @@ mod windows { Threading::GetCurrentProcess, }; - use crate::image::pe::PEImage; + use crate::image::pe::PEImageBuilder; use crate::{Image, Memory}; pub fn read_image<'data>() -> Result> { @@ -162,6 +162,9 @@ mod windows { let memory = Memory::new_internal_data(sections)?; - PEImage::read_inner_memory::(image_base_address, None, false, memory, object) + PEImageBuilder::new() + .object(object)? + .memory(Box::new(memory)) + .build() } } diff --git a/patternsleuth_resolvers/Cargo.toml b/patternsleuth_resolvers/Cargo.toml index 42fbbea..90cda0f 100644 --- a/patternsleuth_resolvers/Cargo.toml +++ b/patternsleuth_resolvers/Cargo.toml @@ -19,7 +19,7 @@ typetag = { version = "0.2.15", optional = true } tracing = "0.1.40" [features] -default = ["image-pe", "image-elf"] +default = [] serde-resolvers = ["dep:serde", "dep:typetag", "patternsleuth_image/serde-resolvers"] image-pe = ["patternsleuth_image/image-pe"] image-elf = ["patternsleuth_image/image-elf"] diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index cadf943..a0f376c 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -1,9 +1,9 @@ pub mod disassemble; pub mod unreal; -use image::Image; +use _image::Image; use patternsleuth_image::MemoryAccessError; -use patternsleuth_image::image; +pub use patternsleuth_image::image as _image; use futures::{ channel::oneshot, @@ -194,7 +194,7 @@ macro_rules! _matcharm_generator { let mut res = None; $( $crate::cfg_image::$img_ident! { - if matches!(img, $crate::image::$enum_name_it::$img_ident(_)) { + if matches!(img, $crate::_image::$enum_name_it::$img_ident(_)) { res = Some($name::$img_ident($ctx).await); } } @@ -244,7 +244,7 @@ macro_rules! _impl_resolver { (collect, $name:ident) => { $crate::_impl_resolver_inner!($name, |ctx| async { - $crate::image::image_type_reflection!(all, impl_resolver; generate; {ctx, $name}) + $crate::_image::image_type_reflection!(all, impl_resolver; generate; {ctx, $name}) }); impl $crate::Singleton for $name { @@ -295,7 +295,7 @@ macro_rules! _impl_resolver_singleton { if let Some(a) = std::env::var(concat!("PATTERNSLEUTH_RES_", stringify!($name))).ok().and_then(|s| (s.strip_prefix("0x").map(|s| usize::from_str_radix(s, 16).ok()).unwrap_or_else(|| s.parse().ok()))) { return Ok($name(a)); } - $crate::image::image_type_reflection!(all, impl_resolver_singleton; generate; {ctx, $name}) + $crate::_image::image_type_reflection!(all, impl_resolver_singleton; generate; {ctx, $name}) }); impl $crate::Singleton for $name { diff --git a/patternsleuth_resolvers/src/unreal/gengine.rs b/patternsleuth_resolvers/src/unreal/gengine.rs index c4a2213..4e3b228 100644 --- a/patternsleuth_resolvers/src/unreal/gengine.rs +++ b/patternsleuth_resolvers/src/unreal/gengine.rs @@ -2,10 +2,10 @@ use std::fmt::Debug; use iced_x86::Register; use itertools::Itertools as _; +use patternsleuth_image::image::Image; use crate::{ disassemble::{Control, disassemble}, - image::Image, {Result, impl_resolver_singleton, try_ensure_one, unreal::util}, }; From 949831531e92c7b1986eedac0eac885e9fcbcf8a Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 26 Feb 2025 18:19:24 -0600 Subject: [PATCH 10/19] Remove unused section kind field --- patternsleuth_image/src/image/elf.rs | 24 +++++++-------- patternsleuth_image/src/image/mod.rs | 20 +++--------- patternsleuth_image/src/lib.rs | 46 +++++++++++----------------- patternsleuth_resolvers/src/lib.rs | 2 +- 4 files changed, 35 insertions(+), 57 deletions(-) diff --git a/patternsleuth_image/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs index 88ad6aa..6504780 100644 --- a/patternsleuth_image/src/image/elf.rs +++ b/patternsleuth_image/src/image/elf.rs @@ -252,17 +252,17 @@ impl ElfImage { ) -> Result, anyhow::Error> { let base_address = base_addr.unwrap_or(object.relative_address_base() as usize); let linked = base_addr.is_some(); - let calc_kind = |flag: u32| { - if flag & object::elf::PF_X == object::elf::PF_X { - SectionKind::Text - } else if flag & object::elf::PF_W == object::elf::PF_W { - SectionKind::Data - } else if flag & object::elf::PF_R == object::elf::PF_R { - SectionKind::ReadOnlyData - } else { - SectionKind::Unknown - } - }; + //let calc_kind = |flag: u32| { + // if flag & object::elf::PF_X == object::elf::PF_X { + // SectionKind::Text + // } else if flag & object::elf::PF_W == object::elf::PF_W { + // SectionKind::Data + // } else if flag & object::elf::PF_R == object::elf::PF_R { + // SectionKind::ReadOnlyData + // } else { + // SectionKind::Unknown + // } + //}; // the elf may not contains section table if it's in memory, use phdr instead. if let File::Elf64(object) = object { @@ -317,7 +317,7 @@ impl ElfImage { NamedMemorySection::new( section_name, base_address + segment.p_vaddr as usize, - calc_kind(segment.p_flags), + crate::SectionFlags::default(), &object.data()[offset_range], ) }) diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index 865baf6..c59b178 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -95,27 +95,15 @@ impl<'data> Image<'data> { .iter() .filter_map(|scan| { scan.scan - .section - .is_none_or(|s| s == section.kind()) - .then(|| { - scan.scan - .scan_type - .get_pattern() - .map(|pattern| (scan, pattern)) - }) - .flatten() + .scan_type + .get_pattern() + .map(|pattern| (scan, pattern)) }) .unzip(); let (xref_scans, xrefs): (Vec<_>, Vec<_>) = scan_queue .iter() - .filter_map(|scan| { - scan.scan - .section - .is_none_or(|s| s == section.kind()) - .then(|| scan.scan.scan_type.get_xref().map(|xref| (scan, xref))) - .flatten() - }) + .filter_map(|scan| scan.scan.scan_type.get_xref().map(|xref| (scan, xref))) .unzip(); let scan_results = scanner::scan_pattern(&patterns, base_address, data) diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index 040bb5b..ed6c66c 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -160,7 +160,6 @@ pub trait SectionedMemoryTrait<'data>: MemoryTrait<'data> + Send + Sync { } pub trait SectionTrait<'a>: MemoryBlockTrait<'a> + MemoryTrait<'a> + Send + Sync { fn name(&self) -> &str; - fn kind(&self) -> object::SectionKind; } #[derive(Debug, Clone, PartialEq)] @@ -337,9 +336,14 @@ impl<'data> MemoryTrait<'data> for Memory<'data> { } } +#[derive(Default, Debug, PartialEq)] +pub struct SectionFlags { + kind: Option, +} + pub struct NamedMemorySection<'data> { name: String, - kind: object::SectionKind, + flags: SectionFlags, address: usize, data: Cow<'data, [u8]>, } @@ -348,12 +352,12 @@ impl<'data> NamedMemorySection<'data> { fn new>>( name: String, address: usize, - kind: object::SectionKind, + flags: SectionFlags, data: D, ) -> Self { Self { name, - kind, + flags, address, data: data.into(), } @@ -363,9 +367,6 @@ impl NamedMemorySection<'_> { pub fn name(&self) -> &str { &self.name } - pub fn kind(&self) -> object::SectionKind { - self.kind - } pub fn len(&self) -> usize { self.data.len() } @@ -383,7 +384,7 @@ impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { } struct Memory<'data> { - sections: Vec>, + pub sections: Vec>, } impl<'data> SectionedMemoryTrait<'data> for Memory<'data> { fn sections(&self) -> Box> + '_> { @@ -398,9 +399,6 @@ impl<'data> SectionTrait<'data> for NamedMemorySection<'data> { fn name(&self) -> &str { &self.name } - fn kind(&self) -> object::SectionKind { - self.kind - } } impl<'data> Memory<'data> { @@ -412,7 +410,9 @@ impl<'data> Memory<'data> { Ok(NamedMemorySection::new( s.name()?.to_string(), s.address() as usize, - s.kind(), + SectionFlags { + kind: Some(s.kind()), + }, s.data()?, )) }) @@ -427,7 +427,9 @@ impl<'data> Memory<'data> { Ok(NamedMemorySection::new( s.name()?.to_string(), s.address() as usize, - s.kind(), + SectionFlags { + kind: Some(s.kind()), + }, d, )) }) @@ -444,7 +446,9 @@ impl<'data> Memory<'data> { Ok(NamedMemorySection::new( s.name()?.to_string(), s.address() as usize, - s.kind(), + SectionFlags { + kind: Some(s.kind()), + }, d, )) }) @@ -465,20 +469,6 @@ impl<'data> Memory<'data> { }) .ok_or(MemoryAccessError::MemoryOutOfBoundsError) } - pub fn find(&self, kind: object::SectionKind, filter: F) -> Option - where - F: Fn(usize, &[u8]) -> bool, - { - self.sections.iter().find_map(|section| { - if section.kind == kind { - section.data.windows(4).enumerate().find_map(|(i, slice)| { - filter(section.address + i, slice).then_some(section.address + i) - }) - } else { - None - } - }) - } } pub trait Addressable { diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index a0f376c..fdeb1a8 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -594,7 +594,7 @@ where let span = tracing::debug_span!( "section", section = section.name(), - kind = format!("{:?}", section.kind()), + //kind = format!("{:?}", section.kind()), results = tracing::field::Empty ) .entered(); From c93fe0c395081045e8c9aa99f0eb372ba7179788 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 26 Feb 2025 19:02:05 -0600 Subject: [PATCH 11/19] WIP minidump loader --- Cargo.lock | 205 +++++++++++++++++++++++---- patternsleuth_cli/src/main.rs | 2 +- patternsleuth_image/Cargo.toml | 1 + patternsleuth_image/src/image/mod.rs | 94 +++++++++++- 4 files changed, 270 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5e8b70..61fe8d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,7 +242,7 @@ dependencies = [ "objc-foundation", "objc_id", "parking_lot 0.12.1", - "thiserror", + "thiserror 1.0.56", "winapi", "x11rb", ] @@ -404,7 +404,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -439,7 +439,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -612,7 +612,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -637,7 +637,7 @@ dependencies = [ "log", "nix 0.25.1", "slotmap", - "thiserror", + "thiserror 1.0.56", "vec_map", ] @@ -756,7 +756,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -1060,6 +1060,15 @@ dependencies = [ "parking_lot_core 0.9.9", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "deranged" version = "0.3.10" @@ -1195,7 +1204,7 @@ dependencies = [ "percent-encoding", "raw-window-handle", "static_assertions", - "thiserror", + "thiserror 1.0.56", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1275,6 +1284,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enumflags2" version = "0.7.8" @@ -1293,7 +1311,7 @@ checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -1551,7 +1569,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -1976,7 +1994,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.56", "walkdir", "windows-sys 0.45.0", ] @@ -2174,6 +2192,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -2192,6 +2219,41 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minidump" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a30545aa9eded3d58064b370aa0efc43be2e5a3fdb14e46fe7d7f1043280090" +dependencies = [ + "debugid", + "encoding_rs", + "memmap2 0.9.5", + "minidump-common", + "num-traits", + "procfs-core", + "range-map", + "scroll 0.12.0", + "thiserror 2.0.11", + "time", + "tracing", + "uuid", +] + +[[package]] +name = "minidump-common" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dad3a7b1bd550c5f6f181b6b0fee6794f81aef6809e71467a6170ac2c0107b7" +dependencies = [ + "bitflags 2.4.1", + "debugid", + "num-derive", + "num-traits", + "range-map", + "scroll 0.12.0", + "smart-default", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -2244,7 +2306,7 @@ dependencies = [ "ndk-sys", "num_enum 0.5.11", "raw-window-handle", - "thiserror", + "thiserror 1.0.56", ] [[package]] @@ -2321,6 +2383,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2390,7 +2463,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -2634,6 +2707,7 @@ dependencies = [ "gimli", "itertools 0.12.0", "libc", + "minidump", "msvc-demangler", "object", "patternsleuth_scanner", @@ -2677,7 +2751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" dependencies = [ "fallible-iterator 0.2.0", - "scroll", + "scroll 0.11.0", "uuid", ] @@ -2704,7 +2778,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -2851,13 +2925,23 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags 2.4.1", + "hex", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2903,6 +2987,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "range-map" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f" +dependencies = [ + "num-traits", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -2970,7 +3063,7 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall 0.2.16", - "thiserror", + "thiserror 1.0.56", ] [[package]] @@ -3141,6 +3234,26 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "sctk-adwaita" version = "0.5.4" @@ -3149,7 +3262,7 @@ checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" dependencies = [ "ab_glyph", "log", - "memmap2", + "memmap2 0.5.10", "smithay-client-toolkit", "tiny-skia", ] @@ -3181,7 +3294,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -3203,7 +3316,7 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -3297,6 +3410,17 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smart-default" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "smithay-client-toolkit" version = "0.16.1" @@ -3308,7 +3432,7 @@ dependencies = [ "dlib", "lazy_static", "log", - "memmap2", + "memmap2 0.5.10", "nix 0.24.3", "pkg-config", "wayland-client", @@ -3379,9 +3503,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -3418,7 +3542,16 @@ version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.56", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -3429,7 +3562,18 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -3559,6 +3703,7 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3572,7 +3717,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -3657,7 +3802,7 @@ checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] @@ -3787,7 +3932,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -3821,7 +3966,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4450,7 +4595,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.98", ] [[package]] diff --git a/patternsleuth_cli/src/main.rs b/patternsleuth_cli/src/main.rs index 6ca69a0..d4c9c15 100644 --- a/patternsleuth_cli/src/main.rs +++ b/patternsleuth_cli/src/main.rs @@ -982,7 +982,7 @@ fn get_games(filter: impl AsRef<[String]>) -> Result> { return Ok(None); } - let Some(exe_path) = find_ext(entry.path(), &["exe", "elf"]) + let Some(exe_path) = find_ext(entry.path(), &["exe", "elf", "dmp"]) .transpose() .transpose()? else { diff --git a/patternsleuth_image/Cargo.toml b/patternsleuth_image/Cargo.toml index f851bf5..828fb93 100644 --- a/patternsleuth_image/Cargo.toml +++ b/patternsleuth_image/Cargo.toml @@ -15,6 +15,7 @@ msvc-demangler = { workspace = true, optional = true} itertools.workspace = true serde = { workspace = true, optional = true, features = ["derive"] } gimli = { version = "0.28.1", optional = true } +minidump = "0.24.1" [target.'cfg(target_os = "linux")'.dependencies] libc = { version = "0.2.152", optional = true } diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index c59b178..7a2e053 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -8,6 +8,7 @@ use crate::*; use anyhow::Error; #[cfg(feature = "image-elf")] use elf::ElfImage; +use itertools::Itertools; #[cfg(feature = "image-pe")] use pe::PEImage; @@ -49,7 +50,98 @@ impl<'data> Image<'data> { exe_path: Option

, cache_functions: bool, ) -> Result> { - let object = object::File::parse(data)?; + let object = match object::File::parse(data) { + Err(e) if e.to_string() == "Unknown file magic" => { + fn merge_adjacent_slices<'a, T>(a: &'a [T], b: &'a [T]) -> &'a [T] { + assert_eq!( + unsafe { a.as_ptr().add(a.len()) }, + b.as_ptr(), + "Slices are not adjacent in memory" + ); + + unsafe { std::slice::from_raw_parts(a.as_ptr(), a.len() + b.len()) } + } + + let dump = minidump::Minidump::read(data)?; + + let mem = dump.get_memory().unwrap(); + let modules = dump.get_stream::()?; + let main = modules.main_module().unwrap(); + + //let mut bytes: Option<&'data [u8]> = None; + let base_address = main.raw.base_of_image; + let size = main.raw.size_of_image; + let main_range = base_address..(base_address + size as u64); + + // verify sorted by address + for (a, b) in mem.by_addr().tuple_windows() { + assert!(a.base_address() < b.base_address()); + } + + let mut sections = vec![]; + let mut main_bytes = vec![0; size as usize]; + + let mut chunk: Option<(&[u8], u64)> = None; + + for mem in mem.by_addr() { + let bytes = unsafe { std::mem::transmute::<&[u8], &'data [u8]>(mem.bytes()) }; + if main_range.contains(&mem.base_address()) { + //println!("XX {:X} {:X}", mem.base_address(), mem.size()); + let start = mem.base_address() - base_address; + main_bytes[start as usize..(start + mem.size()) as usize] + .copy_from_slice(bytes); + } + + if let Some((slice, address)) = chunk { + // check if continuous with existing slice + if address + slice.len() as u64 == mem.base_address() { + // extend existing slice + chunk = Some((merge_adjacent_slices(slice, bytes), address)); + } else { + // add section + sections.push(NamedMemorySection::new( + "".to_string(), + address as usize, + SectionFlags::default(), + slice, + )); + // reset slice + chunk = Some((bytes, mem.base_address())); + } + } else { + // set slice + chunk = Some((bytes, mem.base_address())); + } + + //println!("{inc} {:X} {:X}", mem.base_address(), mem.size()); + } + // add any remaining section + if let Some((slice, address)) = chunk { + sections.push(NamedMemorySection::new( + "".to_string(), + address as usize, + SectionFlags::default(), + slice, + )); + } + + let object = object::File::parse(main_bytes.as_slice())?; + + match object { + object::File::Pe64(_) => { + return PEImageBuilder::new() + .object(object)? + .memory(Box::new(Memory { sections })) + .exe_path(exe_path.as_ref().map(AsRef::as_ref)) + .build(); + } + _ => unreachable!(), + } + } + Err(e) => return Err(e.into()), + Ok(o) => o, + }; + match object { #[cfg(feature = "image-elf")] object::File::Elf64(_) => { From 561d2a46b89a30501a028cbd8ccd8d5dbd5fea03 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Thu, 27 Feb 2025 00:54:25 -0600 Subject: [PATCH 12/19] Add memory read method --- patternsleuth_image/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index ed6c66c..45fe30e 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -157,6 +157,13 @@ pub trait SectionedMemoryTrait<'data>: MemoryTrait<'data> + Send + Sync { }) .ok_or(MemoryAccessError::MemoryOutOfBoundsError) } + fn read(&self, address: usize, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + buf.copy_from_slice( + self.get_section_containing(address)? + .range(address..address + buf.len())?, + ); + Ok(()) + } } pub trait SectionTrait<'a>: MemoryBlockTrait<'a> + MemoryTrait<'a> + Send + Sync { fn name(&self) -> &str; From 05eb6bc80e9f5aced4a624645526eee542d5d959 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Thu, 27 Feb 2025 10:04:56 -0600 Subject: [PATCH 13/19] Fix windows external image loader --- patternsleuth_image/src/process/external.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/patternsleuth_image/src/process/external.rs b/patternsleuth_image/src/process/external.rs index 8ecc06a..483bdd1 100644 --- a/patternsleuth_image/src/process/external.rs +++ b/patternsleuth_image/src/process/external.rs @@ -119,7 +119,7 @@ mod windows { use anyhow::{Result, bail}; use object::{Object, ObjectSection}; - use crate::image::pe::PEImage; + use crate::image::pe::PEImageBuilder; use crate::{Image, Memory}; use windows::Win32::Foundation::HMODULE; @@ -132,7 +132,7 @@ mod windows { }; pub fn read_image_from_pid<'data>(pid: i32) -> Result> { - let (memory, base) = unsafe { + let memory = unsafe { let process = OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, @@ -169,7 +169,7 @@ mod windows { None, )?; - (mem, info.lpBaseOfDll as usize) + mem }; let object = object::File::parse(memory.as_slice())?; @@ -186,6 +186,9 @@ mod windows { let memory = Memory::new_external_data(sections)?; - PEImage::read_inner_memory::(base, None, false, memory, object) + PEImageBuilder::new() + .object(object)? + .memory(Box::new(memory)) + .build() } } From 91c6a8ec832bc1ddf933919ad567b0ab5e7fded7 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Thu, 27 Feb 2025 16:45:15 -0600 Subject: [PATCH 14/19] Optimize memory section lookup --- patternsleuth_image/src/image/elf.rs | 2 +- patternsleuth_image/src/image/mod.rs | 2 +- patternsleuth_image/src/lib.rs | 67 +++++++++++++++++++++------- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/patternsleuth_image/src/image/elf.rs b/patternsleuth_image/src/image/elf.rs index 6504780..aa22daf 100644 --- a/patternsleuth_image/src/image/elf.rs +++ b/patternsleuth_image/src/image/elf.rs @@ -323,7 +323,7 @@ impl ElfImage { }) .collect::>(); - let memory = Memory { sections }; + let memory = Memory::new_sections(sections); Self::read_inner_memory(base_address, exe_path, linked, memory, object) } else { diff --git a/patternsleuth_image/src/image/mod.rs b/patternsleuth_image/src/image/mod.rs index 7a2e053..3b35974 100644 --- a/patternsleuth_image/src/image/mod.rs +++ b/patternsleuth_image/src/image/mod.rs @@ -131,7 +131,7 @@ impl<'data> Image<'data> { object::File::Pe64(_) => { return PEImageBuilder::new() .object(object)? - .memory(Box::new(Memory { sections })) + .memory(Box::new(Memory::new_sections(sections))) .exe_path(exe_path.as_ref().map(AsRef::as_ref)) .build(); } diff --git a/patternsleuth_image/src/lib.rs b/patternsleuth_image/src/lib.rs index 45fe30e..b8175a3 100644 --- a/patternsleuth_image/src/lib.rs +++ b/patternsleuth_image/src/lib.rs @@ -9,6 +9,7 @@ pub mod scanner { pub use patternsleuth_scanner::*; } +use itertools::Itertools; use scanner::{Pattern, Xref}; use std::{ borrow::Cow, @@ -18,7 +19,7 @@ use std::{ }; use anyhow::{Context, Result, bail}; -use object::{File, Object, ObjectSection}; +use object::{File, Object, ObjectSection, ReadRef}; use image::Image; @@ -391,7 +392,8 @@ impl<'data> MemoryBlockTrait<'data> for NamedMemorySection<'data> { } struct Memory<'data> { - pub sections: Vec>, + sections: Vec>, + section_addresses: Vec, } impl<'data> SectionedMemoryTrait<'data> for Memory<'data> { fn sections(&self) -> Box> + '_> { @@ -401,6 +403,14 @@ impl<'data> SectionedMemoryTrait<'data> for Memory<'data> { .map(|s| -> &dyn SectionTrait<'data> { s }), ) } + fn get_section_containing( + &self, + address: usize, + ) -> Result<&dyn SectionTrait<'data>, MemoryAccessError> { + // use specialized version which is much faster + self.get_section_containing(address) + .map(|s| -> &dyn SectionTrait<'data> { s }) + } } impl<'data> SectionTrait<'data> for NamedMemorySection<'data> { fn name(&self) -> &str { @@ -409,9 +419,23 @@ impl<'data> SectionTrait<'data> for NamedMemorySection<'data> { } impl<'data> Memory<'data> { + pub fn new_sections(mut sections: Vec>) -> Self { + sections.sort_by_key(|s| s.address); + // verify non-overlapping sections (dumps sometimes contain them so is disable ¯\_(ツ)_/¯) + //for (a, b) in sections.iter().tuple_windows() { + // assert!( + // a.address + a.data.len() < b.address, + // "sections have overlapping memory" + // ); + //} + Self { + section_addresses: sections.iter().map(|s| s.address).collect(), + sections, + } + } pub fn new(object: &File<'data>) -> Result { - Ok(Self { - sections: object + Ok(Self::new_sections( + object .sections() .map(|s| { Ok(NamedMemorySection::new( @@ -424,11 +448,11 @@ impl<'data> Memory<'data> { )) }) .collect::>>()?, - }) + )) } pub fn new_external_data(sections: Vec<(object::Section<'_, '_>, Vec)>) -> Result { - Ok(Self { - sections: sections + Ok(Self::new_sections( + sections .into_iter() .map(|(s, d)| { Ok(NamedMemorySection::new( @@ -441,13 +465,13 @@ impl<'data> Memory<'data> { )) }) .collect::>>()?, - }) + )) } pub fn new_internal_data( sections: Vec<(object::Section<'_, '_>, &'data [u8])>, ) -> Result { - Ok(Self { - sections: sections + Ok(Self::new_sections( + sections .into_iter() .map(|(s, d)| { Ok(NamedMemorySection::new( @@ -460,7 +484,7 @@ impl<'data> Memory<'data> { )) }) .collect::>>()?, - }) + )) } pub fn sections(&self) -> &[NamedMemorySection] { &self.sections @@ -469,12 +493,21 @@ impl<'data> Memory<'data> { &self, address: usize, ) -> Result<&NamedMemorySection<'data>, MemoryAccessError> { - self.sections - .iter() - .find(|section| { - address >= section.address && address < section.address + section.data.len() - }) - .ok_or(MemoryAccessError::MemoryOutOfBoundsError) + match self.section_addresses.binary_search(&address) { + Ok(idx) => return Ok(&self.sections[idx]), + // Not an exact match, so we need the insertion point - 1 + Err(idx) if idx > 0 => { + let prev_idx = idx - 1; + // Check if address is within the range + let section = &self.sections[prev_idx]; + if address < section.address + section.data.len() { + return Ok(section); + } + } + _ => {} + } + + Err(MemoryAccessError::MemoryOutOfBoundsError) } } From 760c9a89317183cf507aa2fcdb9410a6393a8ab0 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Fri, 28 Feb 2025 10:46:45 -0600 Subject: [PATCH 15/19] Add resolver name to error context --- patternsleuth_resolvers/src/lib.rs | 66 ++++++++++++++++----- patternsleuth_resolvers/src/unreal/fname.rs | 2 +- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/patternsleuth_resolvers/src/lib.rs b/patternsleuth_resolvers/src/lib.rs index fdeb1a8..4aa994c 100644 --- a/patternsleuth_resolvers/src/lib.rs +++ b/patternsleuth_resolvers/src/lib.rs @@ -47,15 +47,12 @@ pub fn try_ensure_one( } } match unique.len() { - 0 => Err(ResolveError::Msg("expected at least one value".into())), + 0 => Err(ResolveError::new_msg("expected at least one value")), 1 => Ok(unique.swap_remove(0)), - len => Err(ResolveError::Msg( - format!( - "found {}{len} unique values {unique:X?}", - if reached_max { ">=" } else { "" } - ) - .into(), - )), + len => Err(ResolveError::new_msg(format!( + "found {}{len} unique values {unique:X?}", + if reached_max { ">=" } else { "" } + ))), } } @@ -65,15 +62,37 @@ pub type Result = std::result::Result; feature = "serde-resolvers", derive(serde::Serialize, serde::Deserialize) )] -pub enum ResolveError { +pub struct ResolveError { + context: Vec, + r#type: ResolveErrorType, +} +impl ResolveError { + fn new_msg(msg: impl Into>) -> Self { + Self { + context: vec![], + r#type: ResolveErrorType::Msg(msg.into()), + } + } +} +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + feature = "serde-resolvers", + derive(serde::Serialize, serde::Deserialize) +)] +pub enum ResolveErrorType { Msg(Cow<'static, str>), MemoryAccessOutOfBounds(MemoryAccessError), } impl std::fmt::Display for ResolveError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - ResolveError::Msg(msg) => write!(f, "{msg}"), - ResolveError::MemoryAccessOutOfBounds(err) => err.fmt(f), + match &self.r#type { + ResolveErrorType::Msg(msg) => { + for ctx in self.context.iter().rev() { + write!(f, "{ctx}: ")?; + } + write!(f, "{msg}") + } + ResolveErrorType::MemoryAccessOutOfBounds(err) => err.fmt(f), } } } @@ -81,14 +100,17 @@ impl Error for ResolveError {} impl From for ResolveError { fn from(value: MemoryAccessError) -> Self { - Self::MemoryAccessOutOfBounds(value) + Self { + context: vec![], + r#type: ResolveErrorType::MemoryAccessOutOfBounds(value), + } } } #[macro_export] macro_rules! _bail_out { ($msg:expr) => { - return Err($crate::ResolveError::Msg($msg.into())); + return Err($crate::ResolveError::new_msg($msg)); }; } pub use _bail_out as bail_out; @@ -103,7 +125,7 @@ impl Context for Option { fn context(self, msg: &'static str) -> Result { match self { Some(value) => Ok(value), - None => Err(ResolveError::Msg(msg.into())), + None => Err(ResolveError::new_msg(msg)), } } } @@ -172,10 +194,12 @@ impl PartialEq for dyn Resolution { } pub struct DynResolverFactory { + pub name: &'static str, pub factory: for<'ctx> fn(&'ctx AsyncContext<'_, '_>) -> DynResolver<'ctx>, } pub struct ResolverFactory { + pub name: &'static str, pub factory: for<'ctx> fn(&'ctx AsyncContext<'_, '_>) -> Resolver<'ctx, T>, } @@ -329,6 +353,7 @@ macro_rules! _impl_resolver_inner { static GLOBAL: ::std::sync::OnceLock<&$crate::ResolverFactory<$name>> = ::std::sync::OnceLock::new(); GLOBAL.get_or_init(|| &$crate::ResolverFactory { + name: stringify!($name), factory: |$ctx: &$crate::AsyncContext| -> $crate::futures::future::BoxFuture<$crate::Result<$name>> { Box::pin(async $x) }, @@ -338,6 +363,7 @@ macro_rules! _impl_resolver_inner { static GLOBAL: ::std::sync::OnceLock<&$crate::DynResolverFactory> = ::std::sync::OnceLock::new(); GLOBAL.get_or_init(|| &$crate::DynResolverFactory { + name: stringify!($name), factory: |$ctx: &$crate::AsyncContext| -> $crate::futures::future::BoxFuture<$crate::Result<::std::sync::Arc>> { Box::pin(async { $ctx.resolve(Self::resolver()).await.map(|ok| -> ::std::sync::Arc { ok }) @@ -520,8 +546,16 @@ impl<'img, 'data> AsyncContext<'img, 'data> { } // compute the resolver value + let name = resolver.name; let resolver = (resolver.factory)(self); - let res = resolver.await.map(Arc::new); + let res = match resolver.await { + Err(mut e) => { + e.context.push(name.into()); + Err(e) + } + res => res, + }; + let res = res.map(Arc::new); let cache: Result> = match res.as_ref() { Ok(ok) => Ok(ok.clone()), diff --git a/patternsleuth_resolvers/src/unreal/fname.rs b/patternsleuth_resolvers/src/unreal/fname.rs index 07db845..2dd849d 100644 --- a/patternsleuth_resolvers/src/unreal/fname.rs +++ b/patternsleuth_resolvers/src/unreal/fname.rs @@ -102,7 +102,7 @@ impl_resolver_singleton!(ElfImage, FNameCtorWchar, |ctx| async { } } // how to scan code from X? - let result = result.ok_or(ResolveError::Msg("cannot find address".into()))?; + let result = result.ok_or(ResolveError::new_msg("cannot find address"))?; /* Post check if util::root_functions(ctx, &[result]).unwrap()[0] == result { From b756f62f8e576f7e07d9a10e39608646654c17b0 Mon Sep 17 00:00:00 2001 From: LongerWarrior <37636768+LongerWarrior@users.noreply.github.com> Date: Sat, 1 Mar 2025 00:57:48 +0200 Subject: [PATCH 16/19] Hardening FNamePool signatures --- patternsleuth_resolvers/src/unreal/fname.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/patternsleuth_resolvers/src/unreal/fname.rs b/patternsleuth_resolvers/src/unreal/fname.rs index 2dd849d..7f3248b 100644 --- a/patternsleuth_resolvers/src/unreal/fname.rs +++ b/patternsleuth_resolvers/src/unreal/fname.rs @@ -351,9 +351,8 @@ impl_resolver_singleton!(all, FNameToStringFString, |ctx| async { pub struct FNamePool(pub usize); impl_resolver_singleton!(all, FNamePool, |ctx| async { let patterns = [ - "74 ?? 4C 8D 05 | ?? ?? ?? ?? EB ?? 48 8D 0D", - "48 8d 0d | ?? ?? ?? ?? e8 ?? ?? ?? ?? 48 8b d0 c6 05 dc ?? ?? ?? ?? 48 8b 44 24 30 48 c1 e8 20 03 c0 48 03 44 da 10 48 83 c4 20 5b c3", - "48 8d 2d | ?? ?? ?? ?? ?? ?? ?? ?? 48 bf cd cc cc cc cc cc cc", + "74 09 4C 8D 05 | ?? ?? ?? ?? EB ?? 48 8D 0D ?? ?? ?? ?? E8", + "48 83 EC 20 C1 EA 03 48 8d 2d | ?? ?? ?? ?? ?? CA ?? ?? 48 bf cd cc cc cc cc cc cc", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; From f70331e47d1d9e86d6e52e5990feb7ab1a01cae1 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Wed, 19 Mar 2025 20:45:15 -0500 Subject: [PATCH 17/19] dll_hook: fixes --- Cargo.lock | 1 + examples/dll_hook/Cargo.toml | 1 + examples/dll_hook/src/hooks.rs | 1 + examples/dll_hook/src/lib.rs | 88 ++++++++++++---------------------- 4 files changed, 34 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61fe8d3..229dfb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1159,6 +1159,7 @@ dependencies = [ "itertools 0.12.0", "parking_lot 0.12.1", "patternsleuth_image", + "patternsleuth_resolvers", "regex", "retour", "simple-log", diff --git a/examples/dll_hook/Cargo.toml b/examples/dll_hook/Cargo.toml index 45f5942..e23ec6f 100644 --- a/examples/dll_hook/Cargo.toml +++ b/examples/dll_hook/Cargo.toml @@ -20,6 +20,7 @@ indexmap = "2.1.0" itertools.workspace = true parking_lot = "0.12.1" patternsleuth_image = { path = "../../patternsleuth_image", features = ["process-internal", "image-pe"] } +patternsleuth_resolvers = { version = "0.1.0", path = "../../patternsleuth_resolvers", features = ["image-pe"] } regex.workspace = true retour = { git = "https://github.com/Hpmason/retour-rs", features = ["static-detour"] } simple-log = "1.6.0" diff --git a/examples/dll_hook/src/hooks.rs b/examples/dll_hook/src/hooks.rs index 383de95..6dae416 100644 --- a/examples/dll_hook/src/hooks.rs +++ b/examples/dll_hook/src/hooks.rs @@ -76,6 +76,7 @@ pub unsafe fn initialize() -> Result<()> { //info!("tick time={:0.5}", delta_seconds); + #[allow(static_mut_refs)] GUOBJECT_LOCK.take(); HookUGameEngineTick.call(game_engine, delta_seconds, idle_mode); GUOBJECT_LOCK = Some(globals().guobject_array()); diff --git a/examples/dll_hook/src/lib.rs b/examples/dll_hook/src/lib.rs index 5497958..204844e 100644 --- a/examples/dll_hook/src/lib.rs +++ b/examples/dll_hook/src/lib.rs @@ -1,18 +1,16 @@ -#![feature(backtrace_frames)] - mod app; mod gui; mod hooks; mod object_cache; mod ue; +use std::mem::MaybeUninit; use std::path::PathBuf; use anyhow::{Context, Result, anyhow}; -use patternsleuth::resolvers::impl_try_collector; -use patternsleuth::resolvers::unreal::UObjectBaseUtilityGetPathName; -use patternsleuth::resolvers::unreal::blueprint_library::UFunctionBind; -use patternsleuth::resolvers::unreal::{ +use patternsleuth_resolvers::unreal::UObjectBaseUtilityGetPathName; +use patternsleuth_resolvers::unreal::blueprint_library::UFunctionBind; +use patternsleuth_resolvers::unreal::{ KismetSystemLibrary, fname::FNameToString, game_loop::{FEngineLoopInit, UGameEngineTick}, @@ -22,6 +20,7 @@ use patternsleuth::resolvers::unreal::{ }, kismet::{FFrameStep, FFrameStepExplicitProperty, FFrameStepViaExec}, }; +use patternsleuth_resolvers::{impl_try_collector, resolve}; use simple_log::{LogConfigBuilder, error, info}; use windows::Win32::{ Foundation::HMODULE, @@ -31,36 +30,7 @@ use windows::Win32::{ }, }; -// x3daudio1_7.dll -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn X3DAudioCalculate() {} -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn X3DAudioInitialize() {} - -// d3d9.dll -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn D3DPERF_EndEvent() {} -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn D3DPERF_BeginEvent() {} - -// d3d11.dll -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn D3D11CreateDevice() {} - -// dxgi.dll -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn CreateDXGIFactory() {} -#[no_mangle] -#[allow(non_snake_case, unused_variables)] -extern "system" fn CreateDXGIFactory1() {} - -#[no_mangle] +#[unsafe(no_mangle)] #[allow(non_snake_case, unused_variables)] extern "system" fn DllMain(dll_module: HMODULE, call_reason: u32, _: *mut ()) -> bool { unsafe { @@ -106,10 +76,8 @@ pub struct FFrameKismetExecutionMessage(usize); mod resolvers { use super::*; - use patternsleuth::{ - resolvers::{futures::future::join_all, *}, - scanner::Pattern, - }; + use patternsleuth_image::scanner::Pattern; + use patternsleuth_resolvers::{futures::future::join_all, *}; impl_resolver_singleton!(collect, FFrameKismetExecutionMessage); impl_resolver_singleton!(PEImage, FFrameKismetExecutionMessage, |ctx| async { @@ -140,7 +108,7 @@ impl_try_collector! { } } -static mut GLOBALS: Option = None; +static mut GLOBALS: MaybeUninit = MaybeUninit::uninit(); pub struct Globals { resolution: DllHookResolution, @@ -173,7 +141,10 @@ impl Globals { } pub fn globals() -> &'static Globals { - unsafe { GLOBALS.as_ref().unwrap() } + #[allow(static_mut_refs)] + unsafe { + GLOBALS.assume_init_ref() + } } #[macro_export] @@ -195,26 +166,29 @@ fn dump_backtrace() { } unsafe fn patch(bin_dir: PathBuf) -> Result<()> { - let exe = patternsleuth::process::internal::read_image()?; + unsafe { + let exe = patternsleuth_image::process::internal::read_image()?; - info!("starting scan"); - let resolution = exe.resolve(DllHookResolution::resolver())?; - info!("finished scan"); + info!("starting scan"); + let resolution = resolve(&exe, DllHookResolution::resolver())?; + info!("finished scan"); - info!("results: {:?}", resolution); + info!("results: {:?}", resolution); - let guobject_array: &'static ue::FUObjectArray = - &*(resolution.guobject_array.0 as *const ue::FUObjectArray); + let guobject_array: &'static ue::FUObjectArray = + &*(resolution.guobject_array.0 as *const ue::FUObjectArray); - GLOBALS = Some(Globals { - guobject_array: guobject_array.into(), - resolution, - main_thread_id: std::thread::current().id(), - }); + #[allow(static_mut_refs)] + GLOBALS.write(Globals { + guobject_array: guobject_array.into(), + resolution, + main_thread_id: std::thread::current().id(), + }); - hooks::initialize()?; + hooks::initialize()?; - info!("initialized"); + info!("initialized"); - app::run(bin_dir) + app::run(bin_dir) + } } From a16c25686ea9620e55e0eda576096dae26a28aec Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Sat, 22 Mar 2025 01:23:19 -0500 Subject: [PATCH 18/19] Improve EngineVersion resolver --- .../src/unreal/engine_version.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/patternsleuth_resolvers/src/unreal/engine_version.rs b/patternsleuth_resolvers/src/unreal/engine_version.rs index b14df09..c282c47 100644 --- a/patternsleuth_resolvers/src/unreal/engine_version.rs +++ b/patternsleuth_resolvers/src/unreal/engine_version.rs @@ -30,15 +30,19 @@ impl Debug for EngineVersion { impl_resolver!(all, EngineVersion, |ctx| async { let patterns = [ - "C7 03 | 04 00 ?? 00 66 89 4B 04 48 3B F8 74 ?? 48", + "C7 47 20 | 04 00 ?? 00 66 89 6F 24", + "C7 4? 20 | 04 00 ?? ?? 66 4? 89 ?? 24", + "C7 ?? 24 20 | 04 00 ?? ?? 48 8D 45 F0", "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 66 89 ?? ?? ?? ?? ?? C7 05", "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 66 89 ?? ?? ?? ?? ?? 89", "41 C7 ?? | 04 00 ?? 00 ?? ?? 00 00 00 66 41 89", "41 C7 ?? | 04 00 18 00 66 41 89 ?? 04", "41 C7 04 24 | 04 00 ?? 00 66 ?? 89 ?? 24", "41 C7 04 24 | 04 00 ?? 00 B9 ?? 00 00 00", + "41 C7 44 24 20 | 04 00 ?? 00 66 ?? 89 ?? 24", + "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 89 3D ?? ?? ?? ?? 85 FF", "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 89 05 ?? ?? ?? ?? E8", - "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 66 89 ?? ?? ?? ?? ?? 89 05", + "C7 05 ?? ?? ?? ?? | 04 00 ?? 00 66 89 ?? ?? ?? ?? ??", "C7 46 20 | 04 00 ?? 00 66 44 89 76 24 44 89 76 28 48 39 C7", "C7 03 | 04 00 ?? 00 66 44 89 63 04 C7 43 08 C1 5C 08 80 E8", "C7 47 20 | 04 00 ?? 00 66 89 6F 24 C7 47 28 ?? ?? ?? ?? 49", @@ -47,7 +51,14 @@ impl_resolver!(all, EngineVersion, |ctx| async { "C7 06 | 05 00 ?? ?? 48 8B 5C 24 20 4C 8D 76 10 33 ED", "11 76 30 c7 46 20 | 04 00 ?? 00", // maybe better go from BuildSettings::GetBranchName -> FGlobalEngineVersions::FGlobalEngineVersions - "0f 57 c0 0f 11 43 10 c7 03 | 05 ?? ?? ?? 66 c7 43 04 ?? ??", // <- last one is patch + "0F 57 C0 0F 11 43 10 C7 03 | 05 ?? ?? ?? 66 C7 43 04 ?? ??", // <- last one is patch + "48 89 2? 48 89 6? 08 C7 0? | 05 00 ?? ?? 66", + "49 89 2? 49 89 6? 08 C7 0? | 05 00 ?? ?? 66", + "C7 46 20 | 05 00 ?? ?? 66 89 ?? 24", + "C7 43 20 | 05 00 ?? ?? 48 3B F0", + "C7 46 20 | 05 00 ?? ?? 48 8D 44 24 20", + "C7 4? 20 | 05 00 ?? ?? 66 44 89 ?? 24", + "C7 ?? 24 20 | 05 00 ?? ?? 48 8D 45 F0", ]; let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; From 0f098f7d51b62f437f9b82ca6ff4e0f701343064 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Sat, 22 Mar 2025 20:34:03 -0500 Subject: [PATCH 19/19] Improve GUObjectArray resolver --- patternsleuth_resolvers/src/unreal/guobject_array.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patternsleuth_resolvers/src/unreal/guobject_array.rs b/patternsleuth_resolvers/src/unreal/guobject_array.rs index 4abfecf..5cb1d31 100644 --- a/patternsleuth_resolvers/src/unreal/guobject_array.rs +++ b/patternsleuth_resolvers/src/unreal/guobject_array.rs @@ -19,6 +19,8 @@ impl_resolver_singleton!(all, GUObjectArray, |ctx| async { "75 ?? 48 ?? ?? 48 8D 0D | ?? ?? ?? ?? E8 ?? ?? ?? ?? 45 33 C9 4C 89 74 24", "45 84 c0 48 c7 41 10 00 00 00 00 b8 ff ff ff ff 4c 8d 1d | ?? ?? ?? ?? 89 41 08 4c 8b d1 4c 89 19 0f 45 05 ?? ?? ?? ?? ff c0 89 41 08 3b 05", "81 ce 00 00 00 02 83 e0 fb 89 47 08 48 8d 0d | ?? ?? ?? ?? 48 89 fa 45 31 c0 e8 ?? ?? ?? ??", + "8B 05 ?? ?? ?? ?? 2B 05 ?? ?? ?? ?? 2B 05 | ?? ?? ?? ??", + "E8 ?? ?? ?? ?? 8B 05 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D | ?? ?? ?? ?? 29 C8 87 05", ]; // mov imm32 pattern for linux let patterns1 = [