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);
    }
}