Skip to main content

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}