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}