specter/memory/manipulation/rw.rs
1//! Memory Read/Write Utilities
2
3use std::ptr;
4
5#[cfg(feature = "dev_release")]
6use crate::utils::logger;
7use thiserror::Error;
8
9#[derive(Error, Debug)]
10/// Errors that can occur during memory read/write operations
11pub enum RwError {
12 /// The target address is null
13 #[error("Null pointer")]
14 NullPointer,
15 /// Failed to find the target image base
16 #[error("Image not found: {0}")]
17 ImageBaseNotFound(#[from] crate::memory::info::image::ImageError),
18 /// Failed to change memory protection
19 #[error("Protection failed: {0}")]
20 ProtectionFailed(i32),
21 /// Thread manipulation error
22 #[error("Thread error: {0}")]
23 ThreadError(#[from] crate::memory::platform::thread::ThreadError),
24}
25
26/// Reads a value of type T from the specified address
27///
28/// # Type Parameters
29/// * `T` - The type of value to read (must implement `Copy`)
30///
31/// # Arguments
32/// * `address` - The absolute address to read from
33///
34/// # Returns
35/// * `Result<T, RwError>` - The read value or an error
36pub unsafe fn read<T: Copy>(address: usize) -> Result<T, RwError> {
37 unsafe {
38 if address == 0 {
39 return Err(RwError::NullPointer);
40 }
41 Ok(ptr::read(address as *const T))
42 }
43}
44
45/// Reads a value of type T from a relative virtual address (RVA)
46///
47/// # Type Parameters
48/// * `T` - The type of value to read (must implement `Copy`)
49///
50/// # Arguments
51/// * `rva` - The relative virtual address to read from
52///
53/// # Returns
54/// * `Result<T, RwError>` - The read value or an error
55pub unsafe fn read_at_rva<T: Copy>(rva: usize) -> Result<T, RwError> {
56 unsafe {
57 let image_name = crate::config::get_target_image_name().ok_or_else(|| {
58 crate::memory::info::image::ImageError::NotFound("call mem_init first".to_string())
59 })?;
60 let base = crate::memory::info::image::get_image_base(&image_name)?;
61 read::<T>(base + rva)
62 }
63}
64
65/// Reads a pointer chain starting from a base address
66///
67/// # Arguments
68/// * `base` - The base address
69/// * `offsets` - A list of offsets to follow
70///
71/// # Returns
72/// * `Result<usize, RwError>` - The final address or an error
73pub unsafe fn read_pointer_chain(base: usize, offsets: &[usize]) -> Result<usize, RwError> {
74 unsafe {
75 if base == 0 {
76 return Err(RwError::NullPointer);
77 }
78
79 let mut current = base;
80 for (i, &offset) in offsets.iter().enumerate() {
81 if i < offsets.len() - 1 {
82 let ptr = (current + offset) as *const usize;
83 if ptr.is_null() {
84 return Err(RwError::NullPointer);
85 }
86 current = ptr::read(ptr);
87 if current == 0 {
88 return Err(RwError::NullPointer);
89 }
90 } else {
91 current += offset;
92 }
93 }
94 Ok(current)
95 }
96}
97
98/// Writes a value of type T to the specified address
99///
100/// # Type Parameters
101/// * `T` - The type of value to write (must implement `Copy`)
102///
103/// # Arguments
104/// * `address` - The absolute address to write to
105/// * `value` - The value to write
106///
107/// # Returns
108/// * `Result<(), RwError>` - A result indicating success or failure
109pub unsafe fn write<T: Copy>(address: usize, value: T) -> Result<(), RwError> {
110 unsafe {
111 if address == 0 {
112 return Err(RwError::NullPointer);
113 }
114 ptr::write(address as *mut T, value);
115 Ok(())
116 }
117}
118
119/// Writes a value to code/executable memory via stealth write and invalidates icache.
120///
121/// # Type Parameters
122/// * `T` - The type of value to write (must implement `Copy`)
123///
124/// # Arguments
125/// * `address` - The absolute address to write to
126/// * `value` - The value to write
127///
128/// # Returns
129/// * `Result<(), RwError>` - A result indicating success or failure
130pub unsafe fn write_code<T: Copy>(address: usize, value: T) -> Result<(), RwError> {
131 unsafe {
132 if address == 0 {
133 return Err(RwError::NullPointer);
134 }
135 let size = std::mem::size_of::<T>();
136 let data = std::slice::from_raw_parts(&value as *const T as *const u8, size);
137 write_bytes(address, data)
138 }
139}
140
141/// Writes a value of type T to a relative virtual address (RVA)
142///
143/// # Type Parameters
144/// * `T` - The type of value to write (must implement `Copy`)
145///
146/// # Arguments
147/// * `rva` - The relative virtual address to write to
148/// * `value` - The value to write
149///
150/// # Returns
151/// * `Result<(), RwError>` - A result indicating success or failure
152pub unsafe fn write_at_rva<T: Copy>(rva: usize, value: T) -> Result<(), RwError> {
153 unsafe {
154 let image_name = crate::config::get_target_image_name().ok_or_else(|| {
155 crate::memory::info::image::ImageError::NotFound("call mem_init first".to_string())
156 })?;
157 let base = crate::memory::info::image::get_image_base(&image_name)?;
158 write::<T>(base + rva, value)
159 }
160}
161
162/// Writes a slice of bytes to the specified address via stealth write,
163/// handling icache invalidation and thread safety.
164///
165/// Uses mach_vm_remap to avoid detectable vm_protect calls on code pages.
166///
167/// # Arguments
168/// * `address` - The absolute address to write to
169/// * `data` - The bytes to write
170///
171/// # Returns
172/// * `Result<(), RwError>` - A result indicating success or failure
173pub unsafe fn write_bytes(address: usize, data: &[u8]) -> Result<(), RwError> {
174 unsafe {
175 let suspended = crate::memory::platform::thread::suspend_other_threads()?;
176
177 let result = super::patch::stealth_write(address, data).map_err(|e| match e {
178 super::patch::PatchError::ProtectionFailed(kr) => RwError::ProtectionFailed(kr),
179 _ => RwError::ProtectionFailed(0),
180 });
181
182 crate::memory::platform::thread::resume_threads(&suspended);
183
184 if result.is_ok() {
185 #[cfg(feature = "dev_release")]
186 logger::debug("Bytes written");
187 }
188
189 result
190 }
191}