1use std;
2use std::ffi::{OsStr, OsString};
3use std::io;
4use std::os::windows::ffi::{OsStrExt, OsStringExt};
5use std::path::{Path, PathBuf};
6use std::ptr::null_mut;
7use winapi::shared::minwindef::{DWORD, FALSE};
8use winapi::um::dbghelp::{
9 SymCleanup, SymFromNameW, SymInitializeW, SymLoadModuleExW, SymUnloadModule64, SYMBOL_INFOW,
10};
11use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
12use winapi::um::memoryapi::VirtualQueryEx;
13use winapi::um::processthreadsapi::OpenProcess;
14use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
15use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32};
16use winapi::um::tlhelp32::{Module32FirstW, Module32NextW, MODULEENTRY32W};
17use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
18use winapi::um::winnt::{MEMORY_BASIC_INFORMATION, MEM_COMMIT, MEM_IMAGE};
19use winapi::um::winnt::{PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE};
20use winapi::um::winnt::{PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY};
21
22use MapRangeImpl;
23
24pub type Pid = u32;
25
26#[derive(Debug, Clone, PartialEq)]
27pub struct MapRange {
28 base_addr: usize,
29 base_size: usize,
30 pathname: Option<PathBuf>,
31 read: bool,
32 write: bool,
33 exec: bool,
34}
35
36impl MapRangeImpl for MapRange {
37 fn size(&self) -> usize {
38 self.base_size
39 }
40 fn start(&self) -> usize {
41 self.base_addr
42 }
43 fn filename(&self) -> Option<&Path> {
44 self.pathname.as_deref()
45 }
46 fn is_exec(&self) -> bool {
47 self.exec
48 }
49 fn is_write(&self) -> bool {
50 self.write
51 }
52 fn is_read(&self) -> bool {
53 self.read
54 }
55}
56
57pub fn get_process_maps(pid: Pid) -> io::Result<Vec<MapRange>> {
58 let mut modules = get_process_modules(pid)?;
59 modules.sort_by_key(|m| m.base_addr);
60 let page_ranges = get_process_page_ranges(pid)?;
61 let mut maps = Vec::<MapRange>::with_capacity(page_ranges.len());
62 for page_range in page_ranges {
63 maps.push(MapRange {
64 base_addr: page_range.base_addr,
65 base_size: page_range.base_size,
66 pathname: find_pathname(&modules, &page_range),
67 read: page_range.read,
68 write: page_range.write,
69 exec: page_range.exec,
70 });
71 }
72 Ok(maps)
73}
74
75fn find_pathname(modules: &Vec<Module>, page_range: &PageRange) -> Option<PathBuf> {
78 if !page_range.image {
79 return None;
80 }
81
82 let module: &Module = match modules.binary_search_by_key(&page_range.base_addr, |m| m.base_addr)
84 {
85 Ok(i) => &modules[i],
86 Err(0) => return None,
87 Err(i) => &modules[i - 1],
88 };
89
90 if module.contains(page_range) {
91 Some(module.pathname.clone())
92 } else {
93 None
94 }
95}
96
97struct Module {
99 base_addr: usize,
100 base_size: usize,
101 pathname: PathBuf,
102}
103
104impl Module {
105 fn contains(&self, page_range: &PageRange) -> bool {
106 self.base_addr <= page_range.base_addr
107 && page_range.base_addr + page_range.base_size <= self.base_addr + self.base_size
108 }
109}
110
111fn get_process_modules(pid: Pid) -> io::Result<Vec<Module>> {
113 unsafe {
114 let handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
115 if handle == INVALID_HANDLE_VALUE {
116 return Err(io::Error::last_os_error());
117 }
118
119 let mut module = MODULEENTRY32W {
120 ..Default::default()
121 };
122 module.dwSize = std::mem::size_of_val(&module) as u32;
123
124 let mut success = Module32FirstW(handle, &mut module);
125 if success == 0 {
126 CloseHandle(handle);
127 return Err(io::Error::last_os_error());
128 }
129
130 let mut vec = Vec::new();
131 while success != 0 {
132 vec.push(Module {
133 base_addr: module.modBaseAddr as usize,
134 base_size: module.modBaseSize as usize,
135 pathname: PathBuf::from(wstr_to_string(&module.szExePath)),
136 });
137
138 success = Module32NextW(handle, &mut module);
139 }
140 CloseHandle(handle);
141 Ok(vec)
142 }
143}
144
145struct PageRange {
147 base_addr: usize,
148 base_size: usize,
149 image: bool,
150 read: bool,
151 write: bool,
152 exec: bool,
153}
154
155fn get_process_page_ranges(pid: Pid) -> io::Result<Vec<PageRange>> {
157 unsafe {
158 let mut sysinfo = SYSTEM_INFO {
159 ..Default::default()
160 };
161 GetSystemInfo(&mut sysinfo);
162
163 let mut vec = Vec::new();
164
165 let process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
166 if process == INVALID_HANDLE_VALUE {
167 return Err(io::Error::last_os_error());
168 }
169
170 let mut meminfo = MEMORY_BASIC_INFORMATION {
171 ..Default::default()
172 };
173 let buffer_size = std::mem::size_of_val(&meminfo);
174
175 let mut address = sysinfo.lpMinimumApplicationAddress;
176 while address < sysinfo.lpMaximumApplicationAddress {
177 let bytes_returned = VirtualQueryEx(process, address, &mut meminfo, buffer_size);
178 if bytes_returned != buffer_size {
179 CloseHandle(process);
180 return Err(io::Error::last_os_error());
181 }
182
183 if meminfo.State & MEM_COMMIT != 0 {
184 vec.push(PageRange {
186 base_addr: meminfo.BaseAddress as usize,
187 base_size: meminfo.RegionSize as usize,
188 image: meminfo.Type & MEM_IMAGE != 0,
189 read: meminfo.Protect
190 & (PAGE_EXECUTE_READ
191 | PAGE_EXECUTE_READWRITE
192 | PAGE_EXECUTE_WRITECOPY
193 | PAGE_READONLY
194 | PAGE_READWRITE
195 | PAGE_WRITECOPY)
196 != 0,
197 write: meminfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE) != 0,
198 exec: meminfo.Protect
199 & (PAGE_EXECUTE
200 | PAGE_EXECUTE_READ
201 | PAGE_EXECUTE_READWRITE
202 | PAGE_EXECUTE_WRITECOPY)
203 != 0,
204 });
205 }
206
207 address = (meminfo.BaseAddress as *mut u8)
208 .add(meminfo.RegionSize)
209 .cast(); }
211
212 CloseHandle(process);
213 Ok(vec)
214 }
215}
216
217pub struct SymbolLoader {
222 pub process: HANDLE,
223}
224
225pub struct SymbolModule<'a> {
226 pub parent: &'a SymbolLoader,
227 pub filename: &'a Path,
228 pub base: u64,
229}
230
231impl SymbolLoader {
232 pub fn new(pid: Pid) -> io::Result<Self> {
233 unsafe {
234 let process = OpenProcess(PROCESS_VM_READ, FALSE, pid as DWORD);
235 if process == INVALID_HANDLE_VALUE {
236 return Err(io::Error::last_os_error());
237 }
238 if SymInitializeW(process, null_mut(), FALSE) == 0 {
239 return Err(io::Error::last_os_error());
240 }
241 Ok(Self { process })
242 }
243 }
244
245 pub fn address_from_name(&self, name: &str) -> io::Result<(u64, u64)> {
246 let size = std::mem::size_of::<SYMBOL_INFOW>() + 256;
248 let buffer = vec![0; size];
249 let info: *mut SYMBOL_INFOW = buffer.as_ptr() as *mut SYMBOL_INFOW;
250 unsafe {
251 (*info).SizeOfStruct = size as u32;
252 if SymFromNameW(self.process, string_to_wstr(name).as_ptr(), info) == 0 {
253 return Err(std::io::Error::last_os_error());
254 }
255 Ok(((*info).ModBase, (*info).Address))
256 }
257 }
258
259 pub fn load_module<'a>(&'a self, filename: &'a Path) -> io::Result<SymbolModule<'a>> {
261 unsafe {
262 let base = SymLoadModuleExW(
263 self.process,
264 null_mut(),
265 path_to_wstr(filename).as_ptr(),
266 null_mut(),
267 0,
268 0,
269 null_mut(),
270 0,
271 );
272 if base == 0 {
273 return Err(std::io::Error::last_os_error());
274 }
275 Ok(SymbolModule {
276 parent: self,
277 filename,
278 base,
279 })
280 }
281 }
282}
283
284impl Drop for SymbolLoader {
285 fn drop(&mut self) {
286 unsafe {
287 SymCleanup(self.process);
288 CloseHandle(self.process);
289 }
290 }
291}
292
293impl<'a> Drop for SymbolModule<'a> {
294 fn drop(&mut self) {
295 unsafe {
296 SymUnloadModule64(self.parent.process, self.base);
297 }
298 }
299}
300
301fn wstr_to_string(full: &[u16]) -> OsString {
302 let len = full.iter().position(|&x| x == 0).unwrap_or(full.len());
303 OsString::from_wide(&full[..len])
304}
305
306fn string_to_wstr(val: &str) -> Vec<u16> {
307 OsStr::new(val)
308 .encode_wide()
309 .chain(std::iter::once(0))
310 .collect()
311}
312
313fn path_to_wstr(val: &Path) -> Vec<u16> {
314 OsStr::new(val)
315 .encode_wide()
316 .chain(std::iter::once(0))
317 .collect()
318}