region\os/
windows.rs

1use crate::{Error, Protection, Region, Result};
2use std::cmp::{max, min};
3use std::ffi::c_void;
4use std::io;
5use std::mem::{size_of, MaybeUninit};
6use std::sync::Once;
7use windows_sys::Win32::System::Memory::{
8  VirtualAlloc, VirtualFree, VirtualLock, VirtualProtect, VirtualQuery, VirtualUnlock,
9  MEMORY_BASIC_INFORMATION, MEM_COMMIT, MEM_PRIVATE, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE,
10  PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_GUARD, PAGE_NOACCESS,
11  PAGE_NOCACHE, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOMBINE, PAGE_WRITECOPY,
12};
13use windows_sys::Win32::System::SystemInformation::{GetNativeSystemInfo, SYSTEM_INFO};
14
15pub struct QueryIter {
16  region_address: usize,
17  upper_bound: usize,
18}
19
20impl QueryIter {
21  pub fn new(origin: *const (), size: usize) -> Result<QueryIter> {
22    let system = system_info();
23
24    Ok(QueryIter {
25      region_address: max(origin as usize, system.lpMinimumApplicationAddress as usize),
26      upper_bound: min(
27        (origin as usize).saturating_add(size),
28        system.lpMaximumApplicationAddress as usize,
29      ),
30    })
31  }
32
33  pub fn upper_bound(&self) -> usize {
34    self.upper_bound
35  }
36}
37
38impl Iterator for QueryIter {
39  type Item = Result<Region>;
40
41  fn next(&mut self) -> Option<Self::Item> {
42    let mut info: MEMORY_BASIC_INFORMATION = unsafe { std::mem::zeroed() };
43
44    while self.region_address < self.upper_bound {
45      let bytes = unsafe {
46        VirtualQuery(
47          self.region_address as *mut c_void,
48          &mut info,
49          size_of::<MEMORY_BASIC_INFORMATION>(),
50        )
51      };
52
53      if bytes == 0 {
54        return Some(Err(Error::SystemCall(io::Error::last_os_error())));
55      }
56
57      self.region_address = (info.BaseAddress as usize).saturating_add(info.RegionSize);
58
59      // Only mapped memory regions are of interest
60      if info.State == MEM_RESERVE || info.State == MEM_COMMIT {
61        let mut region = Region {
62          base: info.BaseAddress as *const _,
63          reserved: info.State != MEM_COMMIT,
64          guarded: (info.Protect & PAGE_GUARD) != 0,
65          shared: (info.Type & MEM_PRIVATE) == 0,
66          size: info.RegionSize as usize,
67          ..Default::default()
68        };
69
70        if region.is_committed() {
71          region.protection = Protection::from_native(info.Protect);
72        }
73
74        return Some(Ok(region));
75      }
76    }
77
78    None
79  }
80}
81
82pub fn page_size() -> usize {
83  system_info().dwPageSize as usize
84}
85
86pub unsafe fn alloc(base: *const (), size: usize, protection: Protection) -> Result<*const ()> {
87  let allocation = VirtualAlloc(
88    base as *mut c_void,
89    size,
90    MEM_COMMIT | MEM_RESERVE,
91    protection.to_native(),
92  );
93
94  if allocation.is_null() {
95    return Err(Error::SystemCall(io::Error::last_os_error()));
96  }
97
98  Ok(allocation as *const ())
99}
100
101pub unsafe fn free(base: *const (), _size: usize) -> Result<()> {
102  match VirtualFree(base as *mut c_void, 0, MEM_RELEASE) {
103    0 => Err(Error::SystemCall(io::Error::last_os_error())),
104    _ => Ok(()),
105  }
106}
107
108pub unsafe fn protect(base: *const (), size: usize, protection: Protection) -> Result<()> {
109  let result = VirtualProtect(base as *mut c_void, size, protection.to_native(), &mut 0);
110
111  if result == 0 {
112    Err(Error::SystemCall(io::Error::last_os_error()))
113  } else {
114    Ok(())
115  }
116}
117
118pub fn lock(base: *const (), size: usize) -> Result<()> {
119  let result = unsafe { VirtualLock(base as *mut c_void, size) };
120
121  if result == 0 {
122    Err(Error::SystemCall(io::Error::last_os_error()))
123  } else {
124    Ok(())
125  }
126}
127
128pub fn unlock(base: *const (), size: usize) -> Result<()> {
129  let result = unsafe { VirtualUnlock(base as *mut c_void, size) };
130
131  if result == 0 {
132    Err(Error::SystemCall(io::Error::last_os_error()))
133  } else {
134    Ok(())
135  }
136}
137
138fn system_info() -> &'static SYSTEM_INFO {
139  static INIT: Once = Once::new();
140  static mut INFO: MaybeUninit<SYSTEM_INFO> = MaybeUninit::uninit();
141
142  unsafe {
143    INIT.call_once(|| GetNativeSystemInfo(INFO.as_mut_ptr()));
144    &*INFO.as_ptr()
145  }
146}
147
148impl Protection {
149  fn from_native(protection: u32) -> Self {
150    // Ignore unsupported flags (TODO: Preserve this information?)
151    let ignored = PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE;
152
153    match protection & !ignored {
154      PAGE_EXECUTE => Protection::EXECUTE,
155      PAGE_EXECUTE_READ => Protection::READ_EXECUTE,
156      PAGE_EXECUTE_READWRITE => Protection::READ_WRITE_EXECUTE,
157      PAGE_EXECUTE_WRITECOPY => Protection::READ_WRITE_EXECUTE,
158      PAGE_NOACCESS => Protection::NONE,
159      PAGE_READONLY => Protection::READ,
160      PAGE_READWRITE => Protection::READ_WRITE,
161      PAGE_WRITECOPY => Protection::READ_WRITE,
162      _ => unreachable!("Protection: 0x{:X}", protection),
163    }
164  }
165
166  pub(crate) fn to_native(self) -> u32 {
167    match self {
168      Protection::NONE => PAGE_NOACCESS,
169      Protection::READ => PAGE_READONLY,
170      Protection::EXECUTE => PAGE_EXECUTE,
171      Protection::READ_EXECUTE => PAGE_EXECUTE_READ,
172      Protection::READ_WRITE => PAGE_READWRITE,
173      Protection::READ_WRITE_EXECUTE => PAGE_EXECUTE_READWRITE,
174      Protection::WRITE_EXECUTE => PAGE_EXECUTE_READWRITE,
175      _ => unreachable!("Protection: {:?}", self),
176    }
177  }
178}