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