verity_memory/ops/
read.rs

1use std::panic::{catch_unwind, AssertUnwindSafe};
2
3use winapi::{shared::minwindef::LPVOID, um::{memoryapi::VirtualProtect, winnt::PAGE_EXECUTE_READWRITE}};
4
5use crate::{errors::ReadMemoryError, utils};
6
7/// Reads a value from the specified memory address with the specified type.
8/// 
9/// # Safety
10/// This function is `unsafe` because it dereferences a raw pointer, which could lead to undefined behavior if the pointer is invalid.
11/// 
12/// # Type Parameters
13/// - `T`: The type of value to read. It must implement the `Copy` trait.
14/// 
15/// # Parameters
16/// - `address`: A raw pointer to the memory location from which to read.
17/// 
18/// # Returns
19/// - `Ok(T)`: The value read from the specified memory address if successful.
20/// - `Err(ReadMemoryError)`: Returns an error if the pointer is null, misaligned, or the read operation fails.
21/// 
22/// # Errors
23/// - `ReadMemoryError::NullPointer`: If the provided pointer is null.
24/// - `ReadMemoryError::InvalidAlignment`: If the provided pointer is not correctly aligned for the type `T`.
25/// - `ReadMemoryError::FailedToChangeProtection`: If changing the memory protection fails.
26/// - `ReadMemoryError::FailedToRestoreProtection`: If restoring the memory protection fails.
27/// - `ReadMemoryError::InvalidAccess`: If there is an error during the read operation.
28/// 
29/// # Example
30/// ```
31/// use verity_memory::ops::read;
32/// let address: *const i32 = 0x12345678 as *const i32;
33/// let result = unsafe { read::read_memory(address) };
34/// match result {
35///     Ok(value) => println!("Value read: {}", value),
36///     Err(e) => println!("Failed to read memory: {:?}", e),
37/// }
38/// ```
39pub unsafe fn read_memory<T: Copy>(address: *const T) -> Result<T, ReadMemoryError> {
40    if address.is_null() {
41        return Err(ReadMemoryError::NullPointer);
42    }
43
44    if !utils::check_alignment(address) {
45        return Err(ReadMemoryError::InvalidAlignment);
46    }
47
48    let mut old_protect = 0;
49    let size = std::mem::size_of::<T>();
50
51    let res = VirtualProtect(
52        address as LPVOID,
53        size,
54        PAGE_EXECUTE_READWRITE,
55        &mut old_protect,
56    );
57
58    if res == 0 {
59        return Err(ReadMemoryError::FailedToChangeProtection);
60    }
61
62    let result = catch_unwind(AssertUnwindSafe(|| *address))
63        .map_err(|_| ReadMemoryError::InvalidAccess);
64
65    let res_restore = VirtualProtect(address as LPVOID, size, old_protect, &mut old_protect);
66    if res_restore == 0 {
67        return Err(ReadMemoryError::FailedToRestoreProtection);
68    }
69
70    result
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_read_memory_valid() {
79        let valid_data = 42;
80        let ptr: *const i32 = &valid_data;
81
82        let result = unsafe { read_memory(ptr) };
83        assert_eq!(result, Ok(42));
84    }
85
86    #[test]
87    fn test_read_memory_null_pointer() {
88        let null_ptr: *const i32 = std::ptr::null();
89
90        let result = unsafe { read_memory(null_ptr) };
91        assert_eq!(result, Err(ReadMemoryError::NullPointer));
92    }
93
94    #[test]
95    fn test_read_memory_invalid_alignment() {
96        let invalid_data = 42i32;
97        let ptr: *const i32 = &invalid_data;
98
99        let unaligned_ptr = (ptr as usize + 1) as *const i32;
100
101        let result = unsafe { read_memory(unaligned_ptr) };
102        assert_eq!(result, Err(ReadMemoryError::InvalidAlignment));
103    }
104}