memory_rs/internal/
injections.rs

1use std::ops::DerefMut;
2use std::slice::IterMut;
3
4use crate::internal::memory::{hook_function, write_aob, MemoryPattern};
5use crate::internal::memory_region::*;
6use anyhow::{Context, Result};
7
8/// Trait specifically designed to extend the Vec<T> struct in order
9/// to easily write something like `vec.inject()` when you have a vector
10/// of structs that implements Inject.
11pub trait Inject {
12    fn inject(&mut self);
13    fn remove_injection(&mut self);
14}
15
16/// Struct that contains its entry point and original bytes.
17/// The purpose of this struct is that when it goes out of scope,
18/// it automatically removes the modified bytes in order to do a clean
19/// remove of the DLL.
20#[derive(Debug)]
21pub struct Detour {
22    /// Pointer where the detour will be injected.
23    pub entry_point: usize,
24    /// Original bytes where the entry_point points.
25    f_orig: Vec<u8>,
26
27    /// New function where the detour will redirect.
28    new_function: usize,
29
30    /// Optional pointer that will be written the jump back if what you
31    /// inject isn't technically a function (i.e. doesn't return)
32    function_end: Option<&'static mut usize>,
33}
34
35impl Detour {
36    pub fn new(
37        entry_point: usize,
38        size: usize,
39        new_function: usize,
40        function_end: Option<&'static mut usize>,
41    ) -> Detour {
42        let mut f_orig = vec![];
43
44        unsafe {
45            let slice_ = std::slice::from_raw_parts(entry_point as *const u8, size);
46            f_orig.extend_from_slice(slice_);
47        }
48
49        Detour {
50            entry_point,
51            f_orig,
52            new_function,
53            function_end,
54        }
55    }
56
57    /// Creates a Detour from scan_aob. This function can fail
58    /// in the case when the scan_aob can't find it's target.
59    pub fn new_from_aob(
60        scan: MemoryPattern,
61        region: &MemoryRegion,
62        new_function: usize,
63        function_end: Option<&'static mut usize>,
64        size_injection: usize,
65        offset: Option<isize>,
66    ) -> Result<Detour> {
67        let mut entry_point = region.scan_aob(&scan)?.context("Couldn't find aob")?;
68
69        if let Some(v) = offset {
70            entry_point = ((entry_point as isize) + v) as usize;
71        }
72
73        Ok(Detour::new(
74            entry_point,
75            size_injection,
76            new_function,
77            function_end,
78        ))
79    }
80}
81
82impl Inject for Detour {
83    fn inject(&mut self) {
84        unsafe {
85            hook_function(
86                self.entry_point,
87                self.new_function,
88                self.function_end.as_deref_mut(),
89                self.f_orig.len(),
90            )
91            .unwrap();
92        }
93    }
94
95    fn remove_injection(&mut self) {
96        unsafe {
97            write_aob(self.entry_point, &self.f_orig).unwrap();
98        }
99    }
100}
101
102#[cfg(feature = "impl-drop")]
103impl Drop for Detour {
104    fn drop(&mut self) {
105        self.remove_injection();
106    }
107}
108
109/// `Injection` is a simple structure that contains an address where
110/// the instructions to be modified are, and the original bytes with
111/// the new ones. This struct is intended to be injected and removed
112/// easily.
113#[derive(Debug)]
114pub struct Injection {
115    /// Entry point relative to the executable
116    pub entry_point: usize,
117    /// Original bytes
118    pub f_orig: Vec<u8>,
119    /// Bytes to be injected
120    pub f_new: Vec<u8>,
121}
122
123impl Injection {
124    pub fn new(entry_point: usize, f_new: Vec<u8>) -> Injection {
125        let aob_size = f_new.len();
126        let slice = unsafe { std::slice::from_raw_parts(entry_point as *const u8, aob_size) };
127        let mut f_orig = Vec::new();
128        f_orig.extend_from_slice(slice);
129
130        Injection {
131            entry_point,
132            f_orig,
133            f_new,
134        }
135    }
136
137    /// Creates a new injection using the `generate_aob_pattern` macro.
138    /// # Example
139    /// ```
140    /// # use memory_rs::internal::{injections::*, memory::MemoryPattern};
141    /// # use memory_rs::internal::process_info::ProcessInfo;
142    /// # use memory_rs::generate_aob_pattern;
143    /// # #[allow(non_upper_case_globals)]
144    /// static arr: [u8; 5] = [0xEE, 0xAA, 0xFF, 0xBB, 0xFF];
145    /// # // avoid removal of arr at compilation
146    /// # println!("{:x?}", arr);
147    /// # let proc_inf = ProcessInfo::new(None).unwrap();
148    /// let mut injection = Injection::new_from_aob(&proc_inf.region, vec![0x90; 3],
149    ///     generate_aob_pattern![0xAA, _, 0xBB, 0xFF]).unwrap();
150    /// // With this we nop the bytes (i.e. we write 0x90) where
151    /// // generate_aob_pattern has a match.
152    /// injection.inject();
153    /// assert_eq!(&arr[1..4], &[0x90, 0x90, 0x90]);
154    ///
155    /// // If we remove the injection (or `injection` gets dropped) the original
156    /// // array should be restored.
157    /// injection.remove_injection();
158    /// assert_eq!(&arr[1..4], &[0xAA, 0xFF, 0xBB]);
159    ///
160    /// ```
161    pub fn new_from_aob(
162        region: &MemoryRegion,
163        f_new: Vec<u8>,
164        memory_pattern: MemoryPattern,
165    ) -> Result<Injection> {
166        let entry_point = region
167            .scan_aob(&memory_pattern)?
168            .context("Couldn't find aob")?;
169        Ok(Injection::new(entry_point, f_new))
170    }
171}
172
173impl Inject for Injection {
174    fn inject(&mut self) {
175        unsafe {
176            write_aob(self.entry_point, &(self.f_new)).unwrap();
177        }
178    }
179
180    fn remove_injection(&mut self) {
181        unsafe {
182            write_aob(self.entry_point, &(self.f_orig)).unwrap();
183        }
184    }
185}
186
187#[cfg(feature = "impl-drop")]
188impl Drop for Injection {
189    fn drop(&mut self) {
190        self.remove_injection();
191    }
192}
193
194/// StaticElement are all the variables that aren't changed that often,
195/// usually globals.
196pub struct StaticElement {
197    addr: usize,
198    original_value: Option<u32>,
199}
200
201impl StaticElement {
202    pub fn new(addr: usize) -> StaticElement {
203        let original_value = unsafe { Some(*(addr as *mut u32)) };
204
205        StaticElement {
206            addr,
207            original_value,
208        }
209    }
210}
211
212impl Inject for StaticElement {
213    fn inject(&mut self) {
214        unsafe {
215            let ptr = self.addr as *mut u32;
216            if self.original_value.is_none() {
217                self.original_value = Some(*ptr);
218            }
219            *ptr = 0;
220        }
221    }
222
223    fn remove_injection(&mut self) {
224        if self.original_value.is_none() {
225            return;
226        }
227        unsafe {
228            let ptr = self.addr as *mut u32;
229            *ptr = self.original_value.unwrap();
230        }
231
232        self.original_value = None;
233    }
234}
235
236#[cfg(feature = "impl-drop")]
237impl Drop for StaticElement {
238    fn drop(&mut self) {
239        self.remove_injection();
240    }
241}
242
243impl Inject for Box<dyn Inject> {
244    fn inject(&mut self) {
245        self.deref_mut().inject();
246    }
247
248    fn remove_injection(&mut self) {
249        self.deref_mut().remove_injection();
250    }
251}
252
253impl<T: Inject> Inject for IterMut<'_, T> {
254    fn inject(&mut self) {
255        self.for_each(|x| x.inject());
256    }
257
258    fn remove_injection(&mut self) {
259        self.for_each(|x| x.remove_injection());
260    }
261}