process_memory/
local_member.rs

1use crate::Memory;
2
3/// This struct provides functions for modifying the memory of a program from within the address
4/// space of that program. This may be helpful for debug functions, or for an injected DLL.
5///
6/// # Examples:
7/// ```rust
8/// # use process_memory::{Memory, LocalMember};
9/// // We have a variable with some value
10/// let x = 4u32;
11///
12/// // We make a `LocalMember` that has an offset referring to its location in memory
13/// let member = LocalMember::new_offset(vec![&x as *const _ as usize]);
14/// // The memory refered to is now the same
15/// assert_eq!(&x as *const _ as usize, member.get_offset().unwrap());
16/// // The value of the member is the same as the variable
17/// assert_eq!(x, unsafe { member.read().unwrap() });
18/// // We can write to and modify the value of the variable using the member
19/// member.write(&6u32).unwrap();
20/// assert_eq!(x, 6u32);
21/// ```
22///
23/// # Safety
24///
25/// These functions are technically ***not safe***. Do not attempt to read or write to any local
26/// memory that you do not know is correct. If you're trying to explore your entire address space
27/// or are testing to see if a pointer is allocated to you, use [`DataMember`] with your own PID.
28///
29/// Unfortunately it's not possible to implement some traits safely (e.g. [`Memory`] on
30/// [`DataMember`] but implement it on other structures unsafely in Rust.
31///
32/// The implemented functions try to stop you from shooting yourself in the foot by checking none
33/// of the pointers end up at the null pointer, but this does not guarantee that you won't be able
34/// to mess something up really badly in your program.
35///
36/// [`DataMember`]: struct.DataMember.html
37#[derive(Clone, Debug, Default)]
38pub struct LocalMember<T> {
39    offsets: Vec<usize>,
40    _phantom: std::marker::PhantomData<*mut T>,
41}
42
43impl<T: Sized + Copy> LocalMember<T> {
44    /// Creates a new `LocalMember` with no offsets. Any calls to
45    /// [`Memory::read`] will attempt to read from a null pointer reference.
46    ///
47    /// To set offsets, use [`Memory::set_offset`]offset), or create the `LocalMember` using
48    /// [`new_offset`].
49    ///
50    /// [`Memory::read`]: trait.Memory.html#tymethod.read
51    /// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
52    /// [`new_offset`]: struct.LocalMember.html#method.new_offset
53    #[must_use]
54    pub fn new() -> Self {
55        Self {
56            offsets: Vec::new(),
57            _phantom: std::marker::PhantomData,
58        }
59    }
60
61    /// Create a new `LocalMember` with a given set of offsets.
62    #[must_use]
63    pub fn new_offset(offsets: Vec<usize>) -> Self {
64        Self {
65            offsets,
66            _phantom: std::marker::PhantomData,
67        }
68    }
69}
70
71impl<T: Sized + Copy> Memory<T> for LocalMember<T> {
72    fn set_offset(&mut self, new_offsets: Vec<usize>) {
73        self.offsets = new_offsets;
74    }
75
76    fn get_offset(&self) -> std::io::Result<usize> {
77        let mut offset = 0_usize;
78        for i in 0..self.offsets.len() - 1 {
79            offset = offset.wrapping_add(self.offsets[i]);
80            if offset == 0 {
81                return Err(std::io::Error::new(
82                    std::io::ErrorKind::Other,
83                    "Would be a null dereference!",
84                ));
85            }
86            // We can't guarantee alignment, so we must use `read_unaligned()`
87            // to ensure that its ok to read from, as `read()` requires that
88            // our source pointer is properly aligned.
89            unsafe {
90                offset = (offset as *const usize).read_unaligned();
91            }
92        }
93        Ok(offset.wrapping_add(self.offsets[self.offsets.len() - 1]))
94    }
95
96    /// This will only return a error if one of the offsets gives a null pointer. or give a
97    /// non-aligned read
98    unsafe fn read(&self) -> std::io::Result<T> {
99        let offset = self.get_offset()? as *const T;
100        // Read the value of the pointer. We can't guarantee alignment, so this
101        // is `read_unaligned()` instead of `read()`
102        let x: T = offset.read_unaligned();
103        Ok(x)
104    }
105
106    /// This will only return a error if one of the offsets gives a null pointer.
107    fn write(&self, value: &T) -> std::io::Result<()> {
108        use std::ptr::copy_nonoverlapping;
109
110        let offset = self.get_offset()? as *mut T;
111        unsafe {
112            copy_nonoverlapping(value, offset, 1_usize);
113        }
114        Ok(())
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use super::*;
121    #[test]
122    fn modify_local_i32() {
123        let test = 4_i32;
124        let mut member = LocalMember::<i32>::new();
125        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
126        unsafe {
127            // safety: the memory being pointed to is known to be a valid i32 as we control it
128            assert_eq!(test, member.read().unwrap());
129        }
130        member.write(&5_i32).unwrap();
131        assert_eq!(test, 5_i32);
132    }
133    #[test]
134    fn modify_local_i64() {
135        let test = 3_i64;
136        let mut member = LocalMember::<i64>::new();
137        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
138        unsafe {
139            // safety: the memory being pointed to is known to be a valid i64 as we control it
140            assert_eq!(test, member.read().unwrap());
141        }
142        member.write(&-1_i64).unwrap();
143        assert_eq!(test, -1);
144    }
145    #[test]
146    fn modify_local_usize() {
147        let test = 0_usize;
148        let mut member = LocalMember::<usize>::new();
149        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
150        unsafe {
151            // safety: the memory being pointed to is known to be a valid usize as we control it
152            assert_eq!(test, member.read().unwrap());
153        }
154        member.write(&0xffff).unwrap();
155        assert_eq!(test, 0xffff);
156    }
157}