process_memory/
data_member.rs

1use crate::{CopyAddress, Memory, ProcessHandle, PutAddress};
2
3/// # Tools for working with memory of other programs
4/// This module provides functions for modifying the memory of a program from outside of the
5/// address space of that program.
6///
7/// Examples:
8/// ```rust
9/// # use process_memory::{Memory, DataMember, Pid, TryIntoProcessHandle};
10/// // We have a variable with some value
11/// let x = 4u32;
12/// println!("Original x-value: {}", x);
13///
14/// // We need to make sure that we get a handle to a process, in this case, ourselves
15/// let handle = (std::process::id() as Pid).try_into_process_handle().unwrap();
16/// // We make a `DataMember` that has an offset referring to its location in memory
17/// let member = DataMember::new_offset(handle, vec![&x as *const _ as usize]);
18/// // The memory refered to is now the same
19/// println!("Memory location: &x: {}, member: {}", &x as *const _ as usize,
20///     member.get_offset().unwrap());
21/// assert_eq!(&x as *const _ as usize, member.get_offset().unwrap());
22/// // The value of the member is the same as the variable
23/// println!("Member value: {}", unsafe { member.read().unwrap() });
24/// assert_eq!(x, unsafe { member.read().unwrap() });
25/// // We can write to and modify the value of the variable using the member
26/// member.write(&6u32).unwrap();
27/// println!("New x-value: {}", x);
28/// assert_eq!(x, 6u32);
29/// ```
30#[derive(Clone, Debug)]
31pub struct DataMember<T> {
32    offsets: Vec<usize>,
33    process: ProcessHandle,
34    _phantom: std::marker::PhantomData<*mut T>,
35}
36
37impl<T: Sized + Copy> DataMember<T> {
38    /// Create a new `DataMember` from a [`ProcessHandle`]. You must remember to call
39    /// [`try_into_process_handle`] on a [`Pid`], because the types may have the same backing type,
40    /// resulting in errors when called with the wrong value.
41    ///
42    /// By default, there will be no offsets, leading to an error when attempting to call
43    /// [`Memory::read`], so you will likely need to call [`Memory::set_offset`] before attempting
44    /// any reads.
45    ///
46    /// [`try_into_process_handle`]: trait.TryIntoProcessHandle.html#tymethod.try_into_process_handle
47    /// [`ProcessHandle`]: type.ProcessHandle.html
48    /// [`Pid`]: type.Pid.html
49    /// [`Memory::read`]: trait.Memory.html#tymethod.read
50    /// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
51    #[must_use]
52    pub fn new(handle: ProcessHandle) -> Self {
53        Self {
54            offsets: Vec::new(),
55            process: handle,
56            _phantom: std::marker::PhantomData,
57        }
58    }
59
60    /// Create a new `DataMember` from a [`ProcessHandle`] and some number of offsets. You must
61    /// remember to call [`try_into_process_handle`] on a [`Pid`] as sometimes the `Pid` can have
62    /// the same backing type as a [`ProcessHandle`], resulting in an error.
63    ///
64    /// [`try_into_process_handle`]: trait.TryIntoProcessHandle.html#tymethod.try_into_process_handle
65    /// [`ProcessHandle`]: type.ProcessHandle.html
66    /// [`Pid`]: type.Pid.html
67    #[must_use]
68    pub fn new_offset(handle: ProcessHandle, offsets: Vec<usize>) -> Self {
69        Self {
70            offsets,
71            process: handle,
72            _phantom: std::marker::PhantomData,
73        }
74    }
75}
76
77impl<T: Sized + Copy> Memory<T> for DataMember<T> {
78    fn set_offset(&mut self, new_offsets: Vec<usize>) {
79        self.offsets = new_offsets;
80    }
81
82    fn get_offset(&self) -> std::io::Result<usize> {
83        self.process.get_offset(&self.offsets)
84    }
85
86    unsafe fn read(&self) -> std::io::Result<T> {
87        let offset = self.process.get_offset(&self.offsets)?;
88        // This can't be [0_u8;size_of::<T>()] because no const generics.
89        // It will be freed at the end of the function because no references are held to it.
90        let mut buffer = vec![0_u8; std::mem::size_of::<T>()];
91        self.process.copy_address(offset, &mut buffer)?;
92        Ok(buffer.as_ptr().cast::<T>().read_unaligned())
93    }
94
95    fn write(&self, value: &T) -> std::io::Result<()> {
96        use std::slice;
97        let offset = self.process.get_offset(&self.offsets)?;
98        let buffer: &[u8] = unsafe {
99            slice::from_raw_parts((value as *const T).cast::<u8>(), std::mem::size_of::<T>())
100        };
101        self.process.put_address(offset, buffer)
102    }
103}
104
105#[cfg(test)]
106mod test {
107    use super::*;
108    use crate::TryIntoProcessHandle;
109    #[test]
110    fn modify_remote_i32() {
111        let test = 4_i32;
112        #[allow(clippy::cast_possible_wrap)]
113        let handle = (std::process::id() as crate::Pid)
114            .try_into_process_handle()
115            .unwrap();
116        println!("Process Handle: {:?}", handle);
117        let mut member = DataMember::<i32>::new(handle);
118        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
119        unsafe {
120            // safety: the memory being pointed to is known to be a valid i32 as we control it
121            assert_eq!(test, member.read().unwrap());
122        }
123        member.write(&5_i32).unwrap();
124        assert_eq!(test, 5_i32);
125    }
126    #[test]
127    fn modify_remote_i64() {
128        let test = 3_i64;
129        #[allow(clippy::cast_possible_wrap)]
130        let handle = (std::process::id() as crate::Pid)
131            .try_into_process_handle()
132            .unwrap();
133        println!("Process Handle: {:?}", handle);
134        let mut member = DataMember::<i64>::new(handle);
135        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
136        unsafe {
137            // safety: the memory being pointed to is known to be a valid i64 as we control it
138            assert_eq!(test, member.read().unwrap());
139        }
140        member.write(&-1_i64).unwrap();
141        assert_eq!(test, -1);
142    }
143    #[test]
144    fn modify_remote_usize() {
145        let test = 0_usize;
146        #[allow(clippy::cast_possible_wrap)]
147        let handle = (std::process::id() as crate::Pid)
148            .try_into_process_handle()
149            .unwrap();
150        println!("Process Handle: {:?}", handle);
151        let mut member = DataMember::<usize>::new(handle);
152        member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
153        unsafe {
154            // safety: the memory being pointed to is known to be a valid usize as we control it
155            assert_eq!(test, member.read().unwrap());
156        }
157        member.write(&0xffff).unwrap();
158        assert_eq!(test, 0xffff);
159    }
160}