process_memory/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3#![deny(unused_results)]
4#![deny(unreachable_pub)]
5#![deny(missing_debug_implementations)]
6#![deny(rust_2018_idioms)]
7#![deny(bad_style)]
8#![deny(unused)]
9#![deny(clippy::pedantic)]
10
11mod architecture;
12mod data_member;
13mod local_member;
14
15pub use architecture::Architecture;
16pub use data_member::DataMember;
17pub use local_member::LocalMember;
18
19#[cfg(target_os = "linux")]
20#[path = "linux.rs"]
21mod platform;
22#[cfg(target_os = "macos")]
23#[path = "macos.rs"]
24mod platform;
25#[cfg(windows)]
26#[path = "windows.rs"]
27mod platform;
28
29/// A trait that defines that it is possible to copy some memory from something represented by a
30/// type into a buffer.
31pub trait CopyAddress {
32    /// Copy an address into user-defined buffer.
33    ///
34    /// # Errors
35    /// `std::io::Error` if an error occurs copying the address.
36    fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()>;
37
38    /// Get the actual memory location from a set of offsets.
39    ///
40    /// If [`copy_address`] and [`get_pointer_width`] are already defined, then
41    /// we can provide a standard implementation that will work across all
42    /// operating systems.
43    ///
44    /// # Errors
45    /// `std::io::Error` if an error occurs copying the address.
46    ///
47    /// [`copy_address`]: #tymethod.copy_address
48    /// [`get_pointer_width`]: #tymethod.get_pointer_width
49    fn get_offset(&self, offsets: &[usize]) -> std::io::Result<usize> {
50        // Look ma! No unsafes!
51        let mut offset: usize = 0;
52        let noffsets: usize = offsets.len();
53        let mut copy = vec![0_u8; self.get_pointer_width() as usize];
54        for next_offset in offsets.iter().take(noffsets - 1) {
55            offset += next_offset;
56            self.copy_address(offset, &mut copy)?;
57            offset = self.get_pointer_width().pointer_from_ne_bytes(&copy);
58        }
59
60        offset += offsets[noffsets - 1];
61        Ok(offset)
62    }
63
64    /// Get the the pointer width of the underlying process.
65    /// This is required for [`get_offset`] to work.
66    ///
67    /// # Performance
68    /// Any implementation of this function should be marked with
69    /// `#[inline(always)]` as this function is *very* commonly called and
70    /// should be inlined.
71    ///
72    /// [`get_offset`]: #method.get_offset
73    fn get_pointer_width(&self) -> Architecture;
74}
75
76/// A trait that defines that it is possible to put a buffer into the memory of something
77/// represented by a type.
78pub trait PutAddress {
79    /// Put the data from a user-defined buffer at an address.
80    ///
81    /// # Errors
82    /// `std::io::Error` if an error occurs copying the address.
83    fn put_address(&self, addr: usize, buf: &[u8]) -> std::io::Result<()>;
84}
85
86/// A `Pid` is a "process id". Each different platform has a different method for uniquely
87/// identifying a process. You can see what the Rust standard library uses for your platform by
88/// looking at `std::process::id`.
89pub use platform::Pid;
90/// A `ProcessHandle` is a variable type that allows for access to functions that can manipulate
91/// other processes. On platforms other than Linux, this is typically a different type than
92/// [`Pid`], and thus it is a distinct type here.
93///
94/// [`Pid`]: type.Pid.html
95pub use platform::ProcessHandle;
96
97/// A trait that attempts to turn some type into a [`ProcessHandle`] so memory can be either copied
98/// or placed into it.
99///
100/// [`ProcessHandle`]: type.ProcessHandle.html
101pub trait TryIntoProcessHandle {
102    /// Attempt to turn a type into a [`ProcessHandle`]. Whilst Linux provides the same type for
103    /// [`Pid`]s and [`ProcessHandle`]s, Windows and macOS do not. As such, you need to ensure that
104    /// `try_into_process_handle` is called on all [`Pid`]s to ensure cross-platform capabilities.
105    ///
106    /// # Errors
107    /// Returns an error if the type cannot be turned into a [`ProcessHandle`]
108    ///
109    /// [`ProcessHandle`]: type.ProcessHandle.html
110    /// [`Pid`]: type.Pid.html
111    fn try_into_process_handle(&self) -> std::io::Result<ProcessHandle>;
112}
113
114impl TryIntoProcessHandle for ProcessHandle {
115    fn try_into_process_handle(&self) -> std::io::Result<platform::ProcessHandle> {
116        Ok(*self)
117    }
118}
119
120/// Additional functions on process handles
121pub trait ProcessHandleExt {
122    /// Returns `true` if the [`ProcessHandle`] is not null, and `false` otherwise.
123    fn check_handle(&self) -> bool;
124    /// Return the null equivalent of a [`ProcessHandle`].
125    #[must_use]
126    fn null_type() -> ProcessHandle;
127    /// Set this handle to use some architecture
128    #[must_use]
129    fn set_arch(self, arch: Architecture) -> Self;
130}
131
132/// A trait that refers to and allows writing to a region of memory in a running program.
133pub trait Memory<T> {
134    /// Set the offsets to the location in memory. This is used for things such as multi-level
135    /// pointers, such as a `Vec<Vec<T>>` or a `Vec<String>`.
136    ///
137    /// For those sorts of data structures, to access data you need to go via multiple pointers, so
138    /// that if an inner region reallocates its size, the variable that is being modified will be
139    /// correctly modified.
140    fn set_offset(&mut self, new_offsets: Vec<usize>);
141
142    /// Gets the actual total offset from the offsets given by [`Memory::set_offset`].
143    ///
144    /// This function is safe because it should never internally allow for a null pointer
145    /// deference, and instead should return a `std::io::Error` with a `std::io::ErrorKind` of
146    /// `Other`.
147    ///
148    /// # Errors
149    /// Returns an error if copying memory fails or if a null pointer dereference would
150    /// otherwise occur.
151    ///
152    /// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
153    fn get_offset(&self) -> std::io::Result<usize>;
154
155    /// Reads the value of the pointer from the offsets given by [`Memory::set_offset`].
156    ///
157    /// This function should never internally allow for a null pointer deference, and instead
158    /// should return a `std::io::Error` with a `std::io::ErrorKind` of `Other`.
159    ///
160    /// # Safety
161    /// This function is marked as unsafe as it may cause undefined behavior.
162    ///
163    /// The function will attempt to read a `T` from uncontrolled memory, and so may produce an
164    /// invalid value (e.g. a value of `2` for a `bool`, which is [undefined]. The caller _must_
165    /// ensure that the data being read is valid for a `T`, or should get an equivalent integer
166    /// representation and check the bit pattern themselves.
167    ///
168    /// # Errors
169    /// Returns an error if copying memory fails or if a null pointer dereference would
170    /// otherwise occur.
171    ///
172    /// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
173    /// [undefined]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
174    unsafe fn read(&self) -> std::io::Result<T>;
175
176    /// Writes `value` to the pointer from the offsets given by [`Memory::set_offset`].
177    ///
178    /// This function is safe because it should never internally allow for a null pointer
179    /// deference, and instead should return a `std::io::Error` with a `std::io::ErrorKind` of
180    /// `Other`.
181    ///
182    /// This function takes a reference instead of taking ownership so if the caller passes in a
183    /// `String` or a `Vec`, it does not have to be cloned.
184    ///
185    /// # Errors
186    /// Returns an error if copying memory fails or if a null pointer dereference would
187    /// otherwise occur.
188    ///
189    /// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
190    fn write(&self, value: &T) -> std::io::Result<()>;
191}
192
193/// Copy `length` bytes of memory at `addr` from `source`.
194///
195/// This is just a convenient way to call [`CopyAddress::copy_address`] without
196/// having to provide your own buffer.
197///
198/// # Errors
199/// Returns an error if copying memory fails
200pub fn copy_address<T>(addr: usize, length: usize, source: &T) -> std::io::Result<Vec<u8>>
201where
202    T: CopyAddress,
203{
204    let mut copy = vec![0; length];
205
206    source.copy_address(addr, &mut copy)?;
207    Ok(copy)
208}