memflow_native/windows/
process.rs

1use memflow::cglue;
2use memflow::os::process::*;
3use memflow::prelude::v1::*;
4use memflow::types::gap_remover::GapRemover;
5
6use super::{conv_err, ProcessVirtualMemory};
7
8use windows::Wdk::System::Threading::{NtQueryInformationProcess, ProcessBasicInformation};
9use windows::Win32::Foundation::{HINSTANCE, HMODULE, STILL_ACTIVE};
10
11use windows::Win32::System::Memory::{
12    VirtualQueryEx, MEMORY_BASIC_INFORMATION, MEM_FREE, MEM_RESERVE, PAGE_EXECUTE,
13    PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_READONLY,
14    PAGE_READWRITE, PAGE_WRITECOPY,
15};
16
17use windows::Win32::System::ProcessStatus::{
18    K32EnumProcessModulesEx, K32GetModuleFileNameExA, K32GetModuleInformation, LIST_MODULES_32BIT,
19    LIST_MODULES_64BIT,
20};
21
22use windows::Win32::System::Threading::PROCESS_BASIC_INFORMATION;
23
24use core::mem::{size_of, size_of_val};
25use core::ptr;
26
27#[derive(Clone)]
28pub struct WindowsProcess {
29    virt_mem: ProcessVirtualMemory,
30    info: ProcessInfo,
31    cached_modules: Vec<HMODULE>,
32}
33unsafe impl Send for WindowsProcess {}
34unsafe impl Sync for WindowsProcess {}
35
36impl WindowsProcess {
37    pub fn try_new(info: ProcessInfo) -> Result<Self> {
38        Ok(Self {
39            virt_mem: ProcessVirtualMemory::try_new(&info)?,
40            info,
41            cached_modules: vec![],
42        })
43    }
44}
45
46cglue_impl_group!(WindowsProcess, ProcessInstance, {});
47cglue_impl_group!(WindowsProcess, IntoProcessInstance, {});
48
49impl Process for WindowsProcess {
50    /// Retrieves the state of the process
51    fn state(&mut self) -> ProcessState {
52        let mut info = PROCESS_BASIC_INFORMATION::default();
53
54        if unsafe {
55            NtQueryInformationProcess(
56                **self.virt_mem.handle,
57                ProcessBasicInformation,
58                &mut info as *mut _ as _,
59                size_of_val(&info) as _,
60                ptr::null_mut(),
61            )
62        }
63        .ok()
64        .is_err()
65        {
66            return ProcessState::Unknown;
67        }
68
69        if info.ExitStatus == STILL_ACTIVE {
70            ProcessState::Alive
71        } else {
72            ProcessState::Dead(info.ExitStatus.0)
73        }
74    }
75
76    /// Changes the dtb this process uses for memory translations.
77    /// This function serves no purpose in memflow-native.
78    fn set_dtb(&mut self, _dtb1: Address, _dtb2: Address) -> Result<()> {
79        Ok(())
80    }
81
82    /// Walks the process' module list and calls the provided callback for each module structure
83    /// address
84    ///
85    /// # Arguments
86    /// * `target_arch` - sets which architecture to retrieve the modules for (if emulated). Choose
87    /// between `Some(ProcessInfo::sys_arch())`, and `Some(ProcessInfo::proc_arch())`. `None` for all.
88    /// * `callback` - where to pass each matching module to. This is an opaque callback.
89    fn module_address_list_callback(
90        &mut self,
91        target_arch: Option<&ArchitectureIdent>,
92        mut callback: ModuleAddressCallback,
93    ) -> Result<()> {
94        let filter_flags = match target_arch {
95            Some(ident) => match ident.into_obj().bits() {
96                32 => [Some(LIST_MODULES_32BIT), None],
97                64 => [Some(LIST_MODULES_64BIT), None],
98                _ => [Some(LIST_MODULES_32BIT), Some(LIST_MODULES_64BIT)],
99            },
100            None => [Some(LIST_MODULES_32BIT), Some(LIST_MODULES_64BIT)],
101        };
102
103        for f in IntoIterator::into_iter(filter_flags).flatten() {
104            self.cached_modules.clear();
105            self.cached_modules.resize(1024, HMODULE::default());
106
107            let mut needed = 0;
108
109            loop {
110                unsafe {
111                    K32EnumProcessModulesEx(
112                        **self.virt_mem.handle,
113                        self.cached_modules.as_mut_ptr(),
114                        (self.cached_modules.len() * size_of::<HMODULE>()) as _,
115                        &mut needed,
116                        f.0,
117                    )
118                    .ok()
119                    .map_err(conv_err)?
120                }
121
122                needed /= size_of::<HINSTANCE>() as u32;
123
124                if needed as usize <= self.cached_modules.len() {
125                    break;
126                }
127
128                self.cached_modules
129                    .resize(self.cached_modules.len() * 2, HMODULE::default());
130            }
131
132            self.cached_modules.resize(needed as _, HMODULE::default());
133
134            // TODO: ARM STUFF
135            let arch = match f {
136                LIST_MODULES_32BIT => ArchitectureIdent::X86(32, false),
137                LIST_MODULES_64BIT => ArchitectureIdent::X86(64, false),
138                _ => ArchitectureIdent::Unknown(0),
139            };
140
141            callback.extend(self.cached_modules.iter().map(|&m| ModuleAddressInfo {
142                address: Address::from(m.0 as umem),
143                arch,
144            }));
145        }
146
147        Ok(())
148    }
149
150    /// Retrieves a module by its structure address and architecture
151    ///
152    /// # Arguments
153    /// * `address` - address where module's information resides in
154    /// * `architecture` - architecture of the module. Should be either `ProcessInfo::proc_arch`, or `ProcessInfo::sys_arch`.
155    fn module_by_address(
156        &mut self,
157        address: Address,
158        arch: ArchitectureIdent,
159    ) -> Result<ModuleInfo> {
160        let mut path = [0u8; 128];
161
162        if unsafe {
163            K32GetModuleFileNameExA(
164                **self.virt_mem.handle,
165                HINSTANCE(address.to_umem() as _),
166                &mut path,
167            )
168        } == 0
169        {
170            return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Unknown));
171        }
172
173        let mut info = Default::default();
174
175        unsafe {
176            K32GetModuleInformation(
177                **self.virt_mem.handle,
178                HINSTANCE(address.to_umem() as _),
179                &mut info,
180                size_of_val(&info) as _,
181            )
182        }
183        .ok()
184        .map_err(conv_err)?;
185
186        let path = String::from_utf8_lossy(&path);
187        let path = &*path;
188        let path = path.split_once('\0').map(|(i, _)| i).unwrap_or(path);
189        let name = path.rsplit_once('\\').map(|(_, i)| i).unwrap_or(path);
190
191        Ok(ModuleInfo {
192            address,
193            parent_process: self.info.address,
194            arch,
195            base: address,
196            size: info.SizeOfImage as _,
197            name: name.into(),
198            path: path.into(),
199        })
200    }
201
202    /// Retrieves address of the primary module structure of the process
203    ///
204    /// This will generally be for the initial executable that was run
205    fn primary_module_address(&mut self) -> Result<Address> {
206        let mut info = PROCESS_BASIC_INFORMATION::default();
207
208        unsafe {
209            NtQueryInformationProcess(
210                **self.virt_mem.handle,
211                ProcessBasicInformation,
212                &mut info as *mut _ as _,
213                size_of_val(&info) as _,
214                ptr::null_mut(),
215            )
216        }
217        .ok()
218        .map_err(conv_err)?;
219
220        // 0x10 is the offset of the `ImageBaseAddress` field in the `PEB64` structure
221        self.read_addr64(Address::from(info.PebBaseAddress as umem + 0x10))
222            .data_part()
223    }
224
225    fn module_import_list_callback(
226        &mut self,
227        info: &ModuleInfo,
228        callback: ImportCallback,
229    ) -> Result<()> {
230        memflow::os::util::module_import_list_callback(&mut self.virt_mem, info, callback)
231    }
232
233    fn module_export_list_callback(
234        &mut self,
235        info: &ModuleInfo,
236        callback: ExportCallback,
237    ) -> Result<()> {
238        memflow::os::util::module_export_list_callback(&mut self.virt_mem, info, callback)
239    }
240
241    fn module_section_list_callback(
242        &mut self,
243        info: &ModuleInfo,
244        callback: SectionCallback,
245    ) -> Result<()> {
246        memflow::os::util::module_section_list_callback(&mut self.virt_mem, info, callback)
247    }
248
249    /// Retrieves the process info
250    fn info(&self) -> &ProcessInfo {
251        &self.info
252    }
253
254    fn mapped_mem_range(
255        &mut self,
256        gap_size: imem,
257        start: Address,
258        end: Address,
259        out: MemoryRangeCallback,
260    ) {
261        let mut gap_remover = GapRemover::new(out, gap_size, start, end);
262
263        let mut region = Default::default();
264
265        let mut cur_addr = start;
266
267        while unsafe {
268            VirtualQueryEx(
269                **self.virt_mem.handle,
270                Some(cur_addr.to_umem() as *mut _),
271                &mut region,
272                size_of::<MEMORY_BASIC_INFORMATION>(),
273            )
274        } > 0
275            && cur_addr < end
276        {
277            cur_addr = Address::from(
278                (region.BaseAddress as umem).saturating_add(region.RegionSize as umem),
279            );
280
281            if region.State == MEM_FREE || region.State == MEM_RESERVE || region.RegionSize == 0 {
282                continue;
283            }
284
285            let page_type = PageType::empty();
286
287            let page_type = match region.Protect {
288                PAGE_EXECUTE | PAGE_EXECUTE_READ => page_type.noexec(false),
289                PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY => {
290                    page_type.noexec(false).write(true)
291                }
292                PAGE_READWRITE | PAGE_WRITECOPY => page_type.write(true),
293                PAGE_READONLY => page_type.write(false),
294                _ => page_type,
295            };
296
297            let range = CTup3(
298                Address::from(region.BaseAddress as umem),
299                region.RegionSize as _,
300                page_type,
301            );
302
303            gap_remover.push_range(range);
304        }
305    }
306}
307
308impl MemoryView for WindowsProcess {
309    fn read_raw_iter(&mut self, data: ReadRawMemOps) -> Result<()> {
310        self.virt_mem.read_raw_iter(data)
311    }
312
313    fn write_raw_iter(&mut self, data: WriteRawMemOps) -> Result<()> {
314        self.virt_mem.write_raw_iter(data)
315    }
316
317    fn metadata(&self) -> MemoryViewMetadata {
318        self.virt_mem.metadata()
319    }
320}