-
Notifications
You must be signed in to change notification settings - Fork 10
/
data_member.rs
160 lines (152 loc) · 6.23 KB
/
data_member.rs
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::{CopyAddress, Memory, ProcessHandle, PutAddress};
/// # Tools for working with memory of other programs
/// This module provides functions for modifying the memory of a program from outside of the
/// address space of that program.
///
/// Examples:
/// ```rust
/// # use process_memory::{Memory, DataMember, Pid, TryIntoProcessHandle};
/// // We have a variable with some value
/// let x = 4u32;
/// println!("Original x-value: {}", x);
///
/// // We need to make sure that we get a handle to a process, in this case, ourselves
/// let handle = (std::process::id() as Pid).try_into_process_handle().unwrap();
/// // We make a `DataMember` that has an offset referring to its location in memory
/// let member = DataMember::new_offset(handle, vec![&x as *const _ as usize]);
/// // The memory refered to is now the same
/// println!("Memory location: &x: {}, member: {}", &x as *const _ as usize,
/// member.get_offset().unwrap());
/// assert_eq!(&x as *const _ as usize, member.get_offset().unwrap());
/// // The value of the member is the same as the variable
/// println!("Member value: {}", unsafe { member.read().unwrap() });
/// assert_eq!(x, unsafe { member.read().unwrap() });
/// // We can write to and modify the value of the variable using the member
/// member.write(&6u32).unwrap();
/// println!("New x-value: {}", x);
/// assert_eq!(x, 6u32);
/// ```
#[derive(Clone, Debug)]
pub struct DataMember<T> {
offsets: Vec<usize>,
process: ProcessHandle,
_phantom: std::marker::PhantomData<*mut T>,
}
impl<T: Sized + Copy> DataMember<T> {
/// Create a new `DataMember` from a [`ProcessHandle`]. You must remember to call
/// [`try_into_process_handle`] on a [`Pid`], because the types may have the same backing type,
/// resulting in errors when called with the wrong value.
///
/// By default, there will be no offsets, leading to an error when attempting to call
/// [`Memory::read`], so you will likely need to call [`Memory::set_offset`] before attempting
/// any reads.
///
/// [`try_into_process_handle`]: trait.TryIntoProcessHandle.html#tymethod.try_into_process_handle
/// [`ProcessHandle`]: type.ProcessHandle.html
/// [`Pid`]: type.Pid.html
/// [`Memory::read`]: trait.Memory.html#tymethod.read
/// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
#[must_use]
pub fn new(handle: ProcessHandle) -> Self {
Self {
offsets: Vec::new(),
process: handle,
_phantom: std::marker::PhantomData,
}
}
/// Create a new `DataMember` from a [`ProcessHandle`] and some number of offsets. You must
/// remember to call [`try_into_process_handle`] on a [`Pid`] as sometimes the `Pid` can have
/// the same backing type as a [`ProcessHandle`], resulting in an error.
///
/// [`try_into_process_handle`]: trait.TryIntoProcessHandle.html#tymethod.try_into_process_handle
/// [`ProcessHandle`]: type.ProcessHandle.html
/// [`Pid`]: type.Pid.html
#[must_use]
pub fn new_offset(handle: ProcessHandle, offsets: Vec<usize>) -> Self {
Self {
offsets,
process: handle,
_phantom: std::marker::PhantomData,
}
}
}
impl<T: Sized + Copy> Memory<T> for DataMember<T> {
fn set_offset(&mut self, new_offsets: Vec<usize>) {
self.offsets = new_offsets;
}
fn get_offset(&self) -> std::io::Result<usize> {
self.process.get_offset(&self.offsets)
}
unsafe fn read(&self) -> std::io::Result<T> {
let offset = self.process.get_offset(&self.offsets)?;
// This can't be [0_u8;size_of::<T>()] because no const generics.
// It will be freed at the end of the function because no references are held to it.
let mut buffer = vec![0_u8; std::mem::size_of::<T>()];
self.process.copy_address(offset, &mut buffer)?;
Ok(buffer.as_ptr().cast::<T>().read_unaligned())
}
fn write(&self, value: &T) -> std::io::Result<()> {
use std::slice;
let offset = self.process.get_offset(&self.offsets)?;
let buffer: &[u8] = unsafe {
slice::from_raw_parts((value as *const T).cast::<u8>(), std::mem::size_of::<T>())
};
self.process.put_address(offset, buffer)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::TryIntoProcessHandle;
#[test]
fn modify_remote_i32() {
let test = 4_i32;
#[allow(clippy::cast_possible_wrap)]
let handle = (std::process::id() as crate::Pid)
.try_into_process_handle()
.unwrap();
println!("Process Handle: {:?}", handle);
let mut member = DataMember::<i32>::new(handle);
member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
unsafe {
// safety: the memory being pointed to is known to be a valid i32 as we control it
assert_eq!(test, member.read().unwrap());
}
member.write(&5_i32).unwrap();
assert_eq!(test, 5_i32);
}
#[test]
fn modify_remote_i64() {
let test = 3_i64;
#[allow(clippy::cast_possible_wrap)]
let handle = (std::process::id() as crate::Pid)
.try_into_process_handle()
.unwrap();
println!("Process Handle: {:?}", handle);
let mut member = DataMember::<i64>::new(handle);
member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
unsafe {
// safety: the memory being pointed to is known to be a valid i64 as we control it
assert_eq!(test, member.read().unwrap());
}
member.write(&-1_i64).unwrap();
assert_eq!(test, -1);
}
#[test]
fn modify_remote_usize() {
let test = 0_usize;
#[allow(clippy::cast_possible_wrap)]
let handle = (std::process::id() as crate::Pid)
.try_into_process_handle()
.unwrap();
println!("Process Handle: {:?}", handle);
let mut member = DataMember::<usize>::new(handle);
member.set_offset(vec![std::ptr::addr_of!(test) as usize]);
unsafe {
// safety: the memory being pointed to is known to be a valid usize as we control it
assert_eq!(test, member.read().unwrap());
}
member.write(&0xffff).unwrap();
assert_eq!(test, 0xffff);
}
}