specter/memory/info/
protection.rs1#[cfg(feature = "dev_release")]
4use crate::utils::logger;
5use mach2::{
6 kern_return::KERN_SUCCESS,
7 traps::mach_task_self,
8 vm::mach_vm_region,
9 vm_prot::{VM_PROT_EXECUTE, VM_PROT_READ, VM_PROT_WRITE},
10 vm_region::{VM_REGION_BASIC_INFO_64, vm_region_basic_info_64, vm_region_info_t},
11 vm_types::{mach_vm_address_t, mach_vm_size_t},
12};
13use thiserror::Error;
14
15#[derive(Error, Debug)]
16pub enum ProtectionError {
18 #[error("Failed to query region at {0:#x}")]
20 QueryFailed(usize),
21 #[error("Invalid address {0:#x}")]
23 InvalidAddress(usize),
24 #[error("Protection failed: {0}")]
26 ProtectionFailed(i32),
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct PageProtection {
32 flags: i32,
33}
34
35impl PageProtection {
36 pub fn from_raw(flags: i32) -> Self {
38 Self { flags }
39 }
40
41 pub fn raw(&self) -> i32 {
43 self.flags
44 }
45
46 pub fn is_readable(&self) -> bool {
48 (self.flags & VM_PROT_READ) != 0
49 }
50
51 pub fn is_writable(&self) -> bool {
53 (self.flags & VM_PROT_WRITE) != 0
54 }
55
56 pub fn is_executable(&self) -> bool {
58 (self.flags & VM_PROT_EXECUTE) != 0
59 }
60
61 pub fn read_only() -> Self {
63 Self {
64 flags: VM_PROT_READ,
65 }
66 }
67
68 pub fn read_write() -> Self {
70 Self {
71 flags: VM_PROT_READ | VM_PROT_WRITE,
72 }
73 }
74
75 pub fn read_execute() -> Self {
77 Self {
78 flags: VM_PROT_READ | VM_PROT_EXECUTE,
79 }
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct RegionInfo {
86 pub address: usize,
88 pub size: usize,
90 pub protection: PageProtection,
92}
93
94pub fn get_protection(addr: usize) -> Result<PageProtection, ProtectionError> {
102 let info = get_region_info(addr)?;
103 Ok(info.protection)
104}
105
106pub fn get_region_info(addr: usize) -> Result<RegionInfo, ProtectionError> {
114 let region = find_region(addr)?;
115
116 if region.address > addr {
117 return Err(ProtectionError::InvalidAddress(addr));
118 }
119
120 Ok(region)
121}
122
123pub fn find_region(addr: usize) -> Result<RegionInfo, ProtectionError> {
131 unsafe {
132 let task = mach_task_self();
133 let mut address = addr as mach_vm_address_t;
134 let mut region_size: mach_vm_size_t = 0;
135 let mut info = vm_region_basic_info_64::default();
136 let mut info_count = VM_REGION_BASIC_INFO_64;
137 let mut object_name = 0;
138
139 let kr = mach_vm_region(
140 task,
141 &mut address,
142 &mut region_size,
143 VM_REGION_BASIC_INFO_64,
144 &mut info as *mut _ as *mut i32,
145 &mut info_count as *mut _ as *mut u32,
146 &mut object_name,
147 );
148
149 if kr != KERN_SUCCESS {
150 return Err(ProtectionError::QueryFailed(addr));
151 }
152
153 Ok(RegionInfo {
154 address: address as usize,
155 size: region_size as usize,
156 protection: PageProtection::from_raw(info.protection),
157 })
158 }
159}
160
161pub fn is_readable(addr: usize) -> bool {
163 get_protection(addr)
164 .map(|p| p.is_readable())
165 .unwrap_or(false)
166}
167
168pub fn is_writable(addr: usize) -> bool {
170 get_protection(addr)
171 .map(|p| p.is_writable())
172 .unwrap_or(false)
173}
174
175pub fn is_executable(addr: usize) -> bool {
177 get_protection(addr)
178 .map(|p| p.is_executable())
179 .unwrap_or(false)
180}
181
182pub fn protect(
192 addr: usize,
193 size: usize,
194 protection: PageProtection,
195) -> Result<(), ProtectionError> {
196 unsafe {
197 let task = mach_task_self();
198 let kr = mach2::vm::mach_vm_protect(
199 task,
200 addr as mach_vm_address_t,
201 size as mach_vm_size_t,
202 0,
203 protection.raw(),
204 );
205
206 if kr != KERN_SUCCESS {
207 return Err(ProtectionError::ProtectionFailed(kr));
208 }
209 Ok(())
210 }
211}
212
213pub fn get_all_regions() -> Result<Vec<RegionInfo>, ProtectionError> {
218 let mut regions = Vec::new();
219 let task = unsafe { mach_task_self() };
220 let mut address: mach_vm_address_t = 0;
221
222 loop {
223 let mut size: mach_vm_size_t = 0;
224 let mut info = vm_region_basic_info_64::default();
225 let mut info_count = vm_region_basic_info_64::count();
226 let mut object_name: u32 = 0;
227
228 let kr = unsafe {
229 mach_vm_region(
230 task,
231 &mut address,
232 &mut size,
233 VM_REGION_BASIC_INFO_64,
234 &mut info as *mut _ as vm_region_info_t,
235 &mut info_count,
236 &mut object_name,
237 )
238 };
239
240 if kr != KERN_SUCCESS {
241 break;
242 }
243
244 if (info.protection & VM_PROT_READ) != 0 {
245 regions.push(RegionInfo {
246 address: address as usize,
247 size: size as usize,
248 protection: PageProtection::from_raw(info.protection),
249 });
250 }
251
252 address += size;
253 }
254
255 #[cfg(feature = "dev_release")]
256 logger::info(&format!("Found {} memory regions", regions.len()));
257 Ok(regions)
258}