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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![deny(unused_results)]
#![deny(unreachable_pub)]
#![deny(missing_debug_implementations)]
#![deny(rust_2018_idioms)]
#![deny(bad_style)]
#![deny(unused)]
#![deny(clippy::pedantic)]
mod architecture;
mod data_member;
mod local_member;
pub use architecture::Architecture;
pub use data_member::DataMember;
pub use local_member::LocalMember;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod platform;
#[cfg(target_os = "macos")]
#[path = "macos.rs"]
mod platform;
#[cfg(windows)]
#[path = "windows.rs"]
mod platform;
/// A trait that defines that it is possible to copy some memory from something represented by a
/// type into a buffer.
pub trait CopyAddress {
/// Copy an address into user-defined buffer.
///
/// # Errors
/// `std::io::Error` if an error occurs copying the address.
fn copy_address(&self, addr: usize, buf: &mut [u8]) -> std::io::Result<()>;
/// Get the actual memory location from a set of offsets.
///
/// If [`copy_address`] and [`get_pointer_width`] are already defined, then
/// we can provide a standard implementation that will work across all
/// operating systems.
///
/// # Errors
/// `std::io::Error` if an error occurs copying the address.
///
/// [`copy_address`]: #tymethod.copy_address
/// [`get_pointer_width`]: #tymethod.get_pointer_width
fn get_offset(&self, offsets: &[usize]) -> std::io::Result<usize> {
// Look ma! No unsafes!
let mut offset: usize = 0;
let noffsets: usize = offsets.len();
let mut copy = vec![0_u8; self.get_pointer_width() as usize];
for next_offset in offsets.iter().take(noffsets - 1) {
offset += next_offset;
self.copy_address(offset, &mut copy)?;
offset = self.get_pointer_width().pointer_from_ne_bytes(©);
}
offset += offsets[noffsets - 1];
Ok(offset)
}
/// Get the the pointer width of the underlying process.
/// This is required for [`get_offset`] to work.
///
/// # Performance
/// Any implementation of this function should be marked with
/// `#[inline(always)]` as this function is *very* commonly called and
/// should be inlined.
///
/// [`get_offset`]: #method.get_offset
fn get_pointer_width(&self) -> Architecture;
}
/// A trait that defines that it is possible to put a buffer into the memory of something
/// represented by a type.
pub trait PutAddress {
/// Put the data from a user-defined buffer at an address.
///
/// # Errors
/// `std::io::Error` if an error occurs copying the address.
fn put_address(&self, addr: usize, buf: &[u8]) -> std::io::Result<()>;
}
/// A `Pid` is a "process id". Each different platform has a different method for uniquely
/// identifying a process. You can see what the Rust standard library uses for your platform by
/// looking at `std::process::id`.
pub use platform::Pid;
/// A `ProcessHandle` is a variable type that allows for access to functions that can manipulate
/// other processes. On platforms other than Linux, this is typically a different type than
/// [`Pid`], and thus it is a distinct type here.
///
/// [`Pid`]: type.Pid.html
pub use platform::ProcessHandle;
/// A trait that attempts to turn some type into a [`ProcessHandle`] so memory can be either copied
/// or placed into it.
///
/// [`ProcessHandle`]: type.ProcessHandle.html
pub trait TryIntoProcessHandle {
/// Attempt to turn a type into a [`ProcessHandle`]. Whilst Linux provides the same type for
/// [`Pid`]s and [`ProcessHandle`]s, Windows and macOS do not. As such, you need to ensure that
/// `try_into_process_handle` is called on all [`Pid`]s to ensure cross-platform capabilities.
///
/// # Errors
/// Returns an error if the type cannot be turned into a [`ProcessHandle`]
///
/// [`ProcessHandle`]: type.ProcessHandle.html
/// [`Pid`]: type.Pid.html
fn try_into_process_handle(&self) -> std::io::Result<ProcessHandle>;
}
impl TryIntoProcessHandle for ProcessHandle {
fn try_into_process_handle(&self) -> std::io::Result<platform::ProcessHandle> {
Ok(*self)
}
}
/// Additional functions on process handles
pub trait ProcessHandleExt {
/// Returns `true` if the [`ProcessHandle`] is not null, and `false` otherwise.
fn check_handle(&self) -> bool;
/// Return the null equivalent of a [`ProcessHandle`].
#[must_use]
fn null_type() -> ProcessHandle;
/// Set this handle to use some architecture
#[must_use]
fn set_arch(self, arch: Architecture) -> Self;
}
/// A trait that refers to and allows writing to a region of memory in a running program.
pub trait Memory<T> {
/// Set the offsets to the location in memory. This is used for things such as multi-level
/// pointers, such as a `Vec<Vec<T>>` or a `Vec<String>`.
///
/// For those sorts of data structures, to access data you need to go via multiple pointers, so
/// that if an inner region reallocates its size, the variable that is being modified will be
/// correctly modified.
fn set_offset(&mut self, new_offsets: Vec<usize>);
/// Gets the actual total offset from the offsets given by [`Memory::set_offset`].
///
/// This function is safe because it should never internally allow for a null pointer
/// deference, and instead should return a `std::io::Error` with a `std::io::ErrorKind` of
/// `Other`.
///
/// # Errors
/// Returns an error if copying memory fails or if a null pointer dereference would
/// otherwise occur.
///
/// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
fn get_offset(&self) -> std::io::Result<usize>;
/// Reads the value of the pointer from the offsets given by [`Memory::set_offset`].
///
/// This function should never internally allow for a null pointer deference, and instead
/// should return a `std::io::Error` with a `std::io::ErrorKind` of `Other`.
///
/// # Safety
/// This function is marked as unsafe as it may cause undefined behavior.
///
/// The function will attempt to read a `T` from uncontrolled memory, and so may produce an
/// invalid value (e.g. a value of `2` for a `bool`, which is [undefined]. The caller _must_
/// ensure that the data being read is valid for a `T`, or should get an equivalent integer
/// representation and check the bit pattern themselves.
///
/// # Errors
/// Returns an error if copying memory fails or if a null pointer dereference would
/// otherwise occur.
///
/// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
/// [undefined]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
unsafe fn read(&self) -> std::io::Result<T>;
/// Writes `value` to the pointer from the offsets given by [`Memory::set_offset`].
///
/// This function is safe because it should never internally allow for a null pointer
/// deference, and instead should return a `std::io::Error` with a `std::io::ErrorKind` of
/// `Other`.
///
/// This function takes a reference instead of taking ownership so if the caller passes in a
/// `String` or a `Vec`, it does not have to be cloned.
///
/// # Errors
/// Returns an error if copying memory fails or if a null pointer dereference would
/// otherwise occur.
///
/// [`Memory::set_offset`]: trait.Memory.html#tymethod.set_offset
fn write(&self, value: &T) -> std::io::Result<()>;
}
/// Copy `length` bytes of memory at `addr` from `source`.
///
/// This is just a convenient way to call [`CopyAddress::copy_address`] without
/// having to provide your own buffer.
///
/// # Errors
/// Returns an error if copying memory fails
pub fn copy_address<T>(addr: usize, length: usize, source: &T) -> std::io::Result<Vec<u8>>
where
T: CopyAddress,
{
let mut copy = vec![0; length];
source.copy_address(addr, &mut copy)?;
Ok(copy)
}