1use crate::error::{Result, WraithError};
4
5#[derive(Debug, Clone)]
7pub struct MemoryRegion {
8 pub base_address: usize,
9 pub allocation_base: usize,
10 pub allocation_protect: u32,
11 pub region_size: usize,
12 pub state: MemoryState,
13 pub protect: u32,
14 pub memory_type: MemoryType,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum MemoryState {
20 Commit,
21 Reserve,
22 Free,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum MemoryType {
28 Image, Mapped, Private, Unknown,
32}
33
34impl MemoryRegion {
35 pub fn is_executable(&self) -> bool {
37 (self.protect & PAGE_EXECUTE) != 0
38 || (self.protect & PAGE_EXECUTE_READ) != 0
39 || (self.protect & PAGE_EXECUTE_READWRITE) != 0
40 || (self.protect & PAGE_EXECUTE_WRITECOPY) != 0
41 }
42
43 pub fn is_readable(&self) -> bool {
45 (self.protect & PAGE_READONLY) != 0
46 || (self.protect & PAGE_READWRITE) != 0
47 || (self.protect & PAGE_WRITECOPY) != 0
48 || (self.protect & PAGE_EXECUTE_READ) != 0
49 || (self.protect & PAGE_EXECUTE_READWRITE) != 0
50 || (self.protect & PAGE_EXECUTE_WRITECOPY) != 0
51 }
52
53 pub fn is_writable(&self) -> bool {
55 (self.protect & PAGE_READWRITE) != 0
56 || (self.protect & PAGE_WRITECOPY) != 0
57 || (self.protect & PAGE_EXECUTE_READWRITE) != 0
58 || (self.protect & PAGE_EXECUTE_WRITECOPY) != 0
59 }
60
61 pub fn is_committed(&self) -> bool {
63 self.state == MemoryState::Commit
64 }
65
66 pub fn is_image(&self) -> bool {
68 self.memory_type == MemoryType::Image
69 }
70
71 pub fn protection_string(&self) -> &'static str {
73 match self.protect {
74 PAGE_NOACCESS => "---",
75 PAGE_READONLY => "R--",
76 PAGE_READWRITE => "RW-",
77 PAGE_WRITECOPY => "RC-",
78 PAGE_EXECUTE => "--X",
79 PAGE_EXECUTE_READ => "R-X",
80 PAGE_EXECUTE_READWRITE => "RWX",
81 PAGE_EXECUTE_WRITECOPY => "RCX",
82 _ => "???",
83 }
84 }
85}
86
87pub struct MemoryRegionIterator {
89 current_address: usize,
90 max_address: usize,
91}
92
93impl MemoryRegionIterator {
94 pub fn new() -> Self {
96 Self {
97 current_address: 0,
98 max_address: Self::max_user_address(),
99 }
100 }
101
102 pub fn from_address(address: usize) -> Self {
104 Self {
105 current_address: address,
106 max_address: Self::max_user_address(),
107 }
108 }
109
110 fn max_user_address() -> usize {
111 #[cfg(target_arch = "x86_64")]
112 {
113 0x7FFFFFFFFFFF }
115 #[cfg(target_arch = "x86")]
116 {
117 0x7FFFFFFF }
119 }
120}
121
122impl Default for MemoryRegionIterator {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl Iterator for MemoryRegionIterator {
129 type Item = MemoryRegion;
130
131 fn next(&mut self) -> Option<Self::Item> {
132 if self.current_address >= self.max_address {
133 return None;
134 }
135
136 let mut mbi = MemoryBasicInformation::default();
137 let result = unsafe {
139 VirtualQuery(
140 self.current_address as *const _,
141 &mut mbi,
142 core::mem::size_of::<MemoryBasicInformation>(),
143 )
144 };
145
146 if result == 0 {
147 return None;
148 }
149
150 self.current_address = mbi.base_address + mbi.region_size;
152
153 let state = match mbi.state {
154 MEM_COMMIT => MemoryState::Commit,
155 MEM_RESERVE => MemoryState::Reserve,
156 MEM_FREE => MemoryState::Free,
157 _ => MemoryState::Free,
158 };
159
160 let memory_type = match mbi.memory_type {
161 MEM_IMAGE => MemoryType::Image,
162 MEM_MAPPED => MemoryType::Mapped,
163 MEM_PRIVATE => MemoryType::Private,
164 _ => MemoryType::Unknown,
165 };
166
167 Some(MemoryRegion {
168 base_address: mbi.base_address,
169 allocation_base: mbi.allocation_base,
170 allocation_protect: mbi.allocation_protect,
171 region_size: mbi.region_size,
172 state,
173 protect: mbi.protect,
174 memory_type,
175 })
176 }
177}
178
179pub fn find_executable_regions() -> Vec<MemoryRegion> {
181 MemoryRegionIterator::new()
182 .filter(|r| r.is_committed() && r.is_executable())
183 .collect()
184}
185
186pub fn find_image_regions() -> Vec<MemoryRegion> {
188 MemoryRegionIterator::new()
189 .filter(|r| r.is_committed() && r.is_image())
190 .collect()
191}
192
193pub fn find_private_regions() -> Vec<MemoryRegion> {
195 MemoryRegionIterator::new()
196 .filter(|r| r.is_committed() && r.memory_type == MemoryType::Private)
197 .collect()
198}
199
200pub fn query_region(address: usize) -> Result<MemoryRegion> {
202 let mut mbi = MemoryBasicInformation::default();
203 let result = unsafe {
205 VirtualQuery(
206 address as *const _,
207 &mut mbi,
208 core::mem::size_of::<MemoryBasicInformation>(),
209 )
210 };
211
212 if result == 0 {
213 return Err(WraithError::ReadFailed {
214 address: address as u64,
215 size: 0,
216 });
217 }
218
219 let state = match mbi.state {
220 MEM_COMMIT => MemoryState::Commit,
221 MEM_RESERVE => MemoryState::Reserve,
222 _ => MemoryState::Free,
223 };
224
225 let memory_type = match mbi.memory_type {
226 MEM_IMAGE => MemoryType::Image,
227 MEM_MAPPED => MemoryType::Mapped,
228 MEM_PRIVATE => MemoryType::Private,
229 _ => MemoryType::Unknown,
230 };
231
232 Ok(MemoryRegion {
233 base_address: mbi.base_address,
234 allocation_base: mbi.allocation_base,
235 allocation_protect: mbi.allocation_protect,
236 region_size: mbi.region_size,
237 state,
238 protect: mbi.protect,
239 memory_type,
240 })
241}
242
243#[repr(C)]
245#[derive(Default)]
246struct MemoryBasicInformation {
247 base_address: usize,
248 allocation_base: usize,
249 allocation_protect: u32,
250 #[cfg(target_arch = "x86_64")]
251 partition_id: u16,
252 region_size: usize,
253 state: u32,
254 protect: u32,
255 memory_type: u32,
256}
257
258const MEM_COMMIT: u32 = 0x1000;
260const MEM_RESERVE: u32 = 0x2000;
261const MEM_FREE: u32 = 0x10000;
262
263const MEM_IMAGE: u32 = 0x1000000;
265const MEM_MAPPED: u32 = 0x40000;
266const MEM_PRIVATE: u32 = 0x20000;
267
268const PAGE_NOACCESS: u32 = 0x01;
270const PAGE_READONLY: u32 = 0x02;
271const PAGE_READWRITE: u32 = 0x04;
272const PAGE_WRITECOPY: u32 = 0x08;
273const PAGE_EXECUTE: u32 = 0x10;
274const PAGE_EXECUTE_READ: u32 = 0x20;
275const PAGE_EXECUTE_READWRITE: u32 = 0x40;
276const PAGE_EXECUTE_WRITECOPY: u32 = 0x80;
277
278#[link(name = "kernel32")]
279extern "system" {
280 fn VirtualQuery(
281 address: *const core::ffi::c_void,
282 buffer: *mut MemoryBasicInformation,
283 length: usize,
284 ) -> usize;
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_memory_iterator() {
293 let regions: Vec<_> = MemoryRegionIterator::new().take(10).collect();
294 assert!(!regions.is_empty());
295 }
296
297 #[test]
298 fn test_find_executable() {
299 let exec_regions = find_executable_regions();
300 assert!(!exec_regions.is_empty());
302 }
303
304 #[test]
305 fn test_query_region() {
306 let addr = test_query_region as usize;
308 let region = query_region(addr).expect("should query region");
309 assert!(region.is_executable());
310 assert!(region.is_committed());
311 }
312
313 #[test]
314 fn test_protection_string() {
315 let region = query_region(test_protection_string as usize).expect("should query");
316 let prot_str = region.protection_string();
317 assert!(prot_str.contains('X'));
319 }
320}