memory_rs/internal/
memory_region.rs

1use crate::error::*;
2use crate::internal::memory;
3use anyhow::Result;
4
5#[derive(Debug)]
6pub struct MemoryRegion {
7    pub start_address: usize,
8    pub size: usize,
9    is_safe: bool,
10}
11
12impl MemoryRegion {
13    pub fn new(start_address: usize, size: usize, is_safe: bool) -> Result<Self> {
14        let memory_region = Self {
15            start_address,
16            size,
17            is_safe,
18        };
19
20        // Do at least one check if the memory is safe
21        memory::check_valid_region(start_address, size)?;
22
23        Ok(memory_region)
24    }
25
26    fn check_valid_region(&self) -> Result<()> {
27        if !self.is_safe {
28            memory::check_valid_region(self.start_address, self.size)?;
29        }
30
31        Ok(())
32    }
33
34    pub fn scan_aob(&self, pat: &memory::MemoryPattern) -> Result<Option<usize>> {
35        self.check_valid_region()?;
36
37        let data = unsafe { std::slice::from_raw_parts(self.start_address as *mut u8, self.size) };
38        let index = data.windows(pat.size).position(pat.pattern);
39
40        match index {
41            Some(addr) => Ok(Some(self.start_address + addr)),
42            None => Ok(None),
43        }
44    }
45
46    pub fn scan_aob_all_matches(&self, pat: &memory::MemoryPattern) -> Result<Vec<usize>> {
47        self.check_valid_region()?;
48        let data = unsafe { std::slice::from_raw_parts(self.start_address as *mut u8, self.size) };
49        let mut iter = data.windows(pat.size);
50        let mut matches = Vec::new();
51
52        loop {
53            let val = iter.position(pat.pattern);
54            if val.is_none() {
55                break;
56            }
57
58            let val = val.unwrap();
59            match matches.last() {
60                Some(&last_val) => matches.push(val + last_val + 0x1),
61                None => matches.push(self.start_address + val),
62            };
63        }
64
65        Ok(matches)
66    }
67
68    /// Scan all aob matches aligned at `align`. If None is provided, it will align to 4 by default
69    pub fn scan_aob_all_matches_aligned(
70        &self,
71        pat: &memory::MemoryPattern,
72        align: Option<usize>,
73    ) -> Result<Vec<usize>> {
74        self.check_valid_region()?;
75        let data = unsafe { std::slice::from_raw_parts(self.start_address as *mut u8, self.size) };
76        let align = align.unwrap_or(4);
77        let padding = (align - (pat.size % align)) % align;
78        let chunk_size = pat.size + padding;
79        let mut iter = data.chunks_exact(chunk_size);
80        let mut matches = Vec::new();
81
82        loop {
83            let val = iter.position(|x| pat.scan(&x[..pat.size]));
84            if val.is_none() {
85                break;
86            }
87
88            let val = val.unwrap();
89            match matches.last() {
90                Some(&last_val) => matches.push((val + 0x1) * chunk_size + last_val),
91                None => matches.push(self.start_address + val),
92            };
93        }
94
95        Ok(matches)
96    }
97
98    pub fn scan_aligned_value<T>(&self, value: T) -> Result<Vec<usize>>
99    where
100        T: Copy + PartialEq,
101    {
102        self.check_valid_region()?;
103        let size_type = std::mem::size_of::<T>();
104        let mut matches = Vec::new();
105
106        if self.size / size_type == 0 {
107            return Err(Error::new(
108                ErrorType::Internal,
109                "The space to scan is smaller than the type size".into(),
110            )
111            .into());
112        }
113
114        let data = unsafe {
115            std::slice::from_raw_parts(self.start_address as *mut T, self.size / size_type)
116        };
117        let mut iter = data.iter();
118
119        let match_function = |&x| x == value;
120
121        while let Some(val) = iter.position(match_function) {
122            match matches.last() {
123                Some(&last_val) => matches.push((val + 0x1) * size_type + last_val),
124                None => matches.push((val * size_type) + self.start_address),
125            };
126        }
127
128        Ok(matches)
129    }
130}