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