process_memory_reader/
windows.rs

1use crate::{MemoryReadError, Process};
2use std::ffi::OsString;
3use std::mem::{size_of, size_of_val, MaybeUninit};
4use std::os::windows::ffi::OsStringExt;
5use std::ptr;
6use winapi::ctypes::c_void;
7use winapi::shared::minwindef::{DWORD, HMODULE, MAX_PATH, TRUE};
8use winapi::um::handleapi::CloseHandle;
9use winapi::um::memoryapi::ReadProcessMemory;
10use winapi::um::processthreadsapi::OpenProcess;
11use winapi::um::psapi::{EnumProcessModules, GetModuleBaseNameA};
12use winapi::um::tlhelp32::PROCESSENTRY32W;
13use winapi::um::tlhelp32::{
14    CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, TH32CS_SNAPPROCESS,
15};
16use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
17
18/// Opens process with specified id.
19///
20/// If the process is not found or could not be opened `None` will be returned.
21pub fn open_process(pid: u32) -> Option<WindowsProcess> {
22    let handle = unsafe { OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 0, pid) };
23
24    if handle.is_null() {
25        return None;
26    }
27
28    Some(WindowsProcess { pid, handle })
29}
30
31/// Finds all processes with matching name.
32pub fn find_by_name(name: &str) -> Vec<WindowsProcess> {
33    let handle = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
34    let mut processes = Vec::new();
35
36    if handle.is_null() {
37        return processes;
38    }
39
40    let mut maybe_entry = MaybeUninit::<PROCESSENTRY32W>::uninit();
41
42    unsafe {
43        ptr::write(
44            &mut (*maybe_entry.as_mut_ptr()).dwSize,
45            size_of::<PROCESSENTRY32W>() as u32,
46        );
47    }
48
49    if unsafe { Process32FirstW(handle, maybe_entry.as_mut_ptr()) } == TRUE {
50        while unsafe { Process32NextW(handle, maybe_entry.as_mut_ptr()) } == TRUE {
51            let entry = unsafe { maybe_entry.assume_init() };
52
53            let process_name_full = &entry.szExeFile;
54            let process_name_length = process_name_full.iter().take_while(|&&c| c != 0).count();
55            let process_name = &OsString::from_wide(&process_name_full[..process_name_length]);
56
57            if process_name != name {
58                continue;
59            }
60
61            open_process(entry.th32ProcessID).map(|process| processes.push(process));
62        }
63    }
64
65    unsafe {
66        CloseHandle(handle);
67    }
68
69    processes
70}
71
72#[derive(Debug)]
73pub struct WindowsProcess {
74    pid: u32,
75    handle: *mut c_void,
76}
77
78impl Process for WindowsProcess {
79    fn base_address(&self, module_name: &str) -> Option<usize> {
80        let mut maybe_hmod = MaybeUninit::<HMODULE>::uninit();
81        let mut maybe_cb_needed = MaybeUninit::<DWORD>::uninit();
82
83        let result = unsafe {
84            EnumProcessModules(
85                self.handle,
86                maybe_hmod.as_mut_ptr(),
87                size_of_val(&maybe_hmod) as u32,
88                maybe_cb_needed.as_mut_ptr(),
89            )
90        };
91
92        if result != TRUE {
93            return None;
94        }
95
96        let mut base_name_vec: Vec<u8> = Vec::with_capacity(MAX_PATH);
97
98        unsafe {
99            let base_name_length = GetModuleBaseNameA(
100                self.handle,
101                maybe_hmod.assume_init(),
102                base_name_vec.as_mut_ptr() as *mut _,
103                base_name_vec.capacity() as u32,
104            );
105
106            base_name_vec.set_len(base_name_length as usize)
107        }
108
109        let base_name = String::from_utf8_lossy(&base_name_vec);
110
111        if base_name.to_lowercase() == module_name.to_lowercase() {
112            unsafe { Some(maybe_hmod.assume_init() as usize) }
113        } else {
114            None
115        }
116    }
117
118    fn read_bytes(&self, address: usize, buffer: &mut [u8]) -> Result<(), MemoryReadError> {
119        let mut maybe_read = MaybeUninit::<usize>::uninit();
120
121        let result = unsafe {
122            ReadProcessMemory(
123                self.handle,
124                address as *const _,
125                buffer.as_mut_ptr() as *mut _,
126                buffer.len(),
127                maybe_read.as_mut_ptr(),
128            )
129        };
130
131        if result != TRUE {
132            return Err(MemoryReadError::InaccessibleMemoryAddress { address });
133        }
134
135        let read = unsafe { maybe_read.assume_init() };
136
137        if read != buffer.len() {
138            return Err(MemoryReadError::LessBytesRead {
139                expected: buffer.len(),
140                actual: read,
141            });
142        }
143
144        Ok(())
145    }
146}
147
148impl Drop for WindowsProcess {
149    fn drop(&mut self) {
150        unsafe {
151            CloseHandle(self.handle);
152        }
153    }
154}