1use vmi_core::{
2 Architecture, Pa, Va, VmiError, VmiState, VmiVa,
3 driver::VmiRead,
4 os::{ProcessId, ProcessObject, ThreadObject, VmiOsImageArchitecture, VmiOsProcess},
5};
6
7use super::{
8 super::{WindowsHandleTable, WindowsPeb, WindowsRegion, WindowsSession, WindowsWow64Kind},
9 FromWindowsObject, WindowsObject, WindowsObjectTypeKind, WindowsThread, WindowsToken,
10};
11use crate::{
12 ArchAdapter, ListEntryIterator, OffsetsExt, TreeNodeIterator, WindowsOs, WindowsOsExt as _,
13 offset,
14 offsets::{v1, v2},
15};
16
17pub struct WindowsProcess<'a, Driver>
27where
28 Driver: VmiRead,
29 Driver::Architecture: ArchAdapter<Driver>,
30{
31 vmi: VmiState<'a, WindowsOs<Driver>>,
33
34 va: Va,
36}
37
38impl<'a, Driver> From<WindowsProcess<'a, Driver>> for WindowsObject<'a, Driver>
39where
40 Driver: VmiRead,
41 Driver::Architecture: ArchAdapter<Driver>,
42{
43 fn from(value: WindowsProcess<'a, Driver>) -> Self {
44 Self::new(value.vmi, value.va)
45 }
46}
47
48impl<'a, Driver> FromWindowsObject<'a, Driver> for WindowsProcess<'a, Driver>
49where
50 Driver: VmiRead,
51 Driver::Architecture: ArchAdapter<Driver>,
52{
53 fn from_object(object: WindowsObject<'a, Driver>) -> Result<Option<Self>, VmiError> {
54 match object.type_kind()? {
55 Some(WindowsObjectTypeKind::Process) => {
56 Ok(Some(Self::new(object.vmi, ProcessObject(object.va))))
57 }
58 _ => Ok(None),
59 }
60 }
61}
62
63impl<Driver> VmiVa for WindowsProcess<'_, Driver>
64where
65 Driver: VmiRead,
66 Driver::Architecture: ArchAdapter<Driver>,
67{
68 fn va(&self) -> Va {
69 self.va
70 }
71}
72
73impl<'a, Driver> WindowsProcess<'a, Driver>
74where
75 Driver: VmiRead,
76 Driver::Architecture: ArchAdapter<Driver>,
77{
78 pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, process: ProcessObject) -> Self {
80 Self { vmi, va: process.0 }
81 }
82
83 pub fn create_time(&self) -> Result<u64, VmiError> {
89 let EPROCESS = offset!(self.vmi, _EPROCESS);
90
91 self.vmi.read_u64(self.va + EPROCESS.CreateTime.offset())
92 }
93
94 pub fn exit_time(&self) -> Result<u64, VmiError> {
100 let EPROCESS = offset!(self.vmi, _EPROCESS);
101
102 self.vmi.read_u64(self.va + EPROCESS.ExitTime.offset())
103 }
104
105 pub fn is_wow64(&self) -> Result<bool, VmiError> {
111 let EPROCESS = offset!(self.vmi, _EPROCESS);
112
113 let wow64process = self
114 .vmi
115 .read_va_native(self.va + EPROCESS.WoW64Process.offset())?;
116
117 Ok(!wow64process.is_null())
118 }
119
120 pub fn peb(&self) -> Result<Option<WindowsPeb<'a, Driver>>, VmiError> {
129 let EPROCESS = offset!(self.vmi, _EPROCESS);
130
131 let wow64 = self
132 .vmi
133 .read_va_native(self.va + EPROCESS.WoW64Process.offset())?;
134
135 if wow64.is_null() {
136 return self.native_peb();
137 }
138
139 let va = match &self.vmi.underlying_os().offsets.ext() {
140 Some(OffsetsExt::V1(_)) => wow64,
141 Some(OffsetsExt::V2(v2)) => self
142 .vmi
143 .read_va_native(wow64 + v2._EWOW64PROCESS.Peb.offset())?,
144 None => panic!("OffsetsExt not set"),
145 };
146
147 if va.is_null() {
148 return Ok(None);
149 }
150
151 let root = self.translation_root()?;
152
153 Ok(Some(WindowsPeb::with_kind(
154 self.vmi,
155 va,
156 root,
157 WindowsWow64Kind::X86,
158 )))
159 }
160
161 pub fn native_peb(&self) -> Result<Option<WindowsPeb<'a, Driver>>, VmiError> {
167 let EPROCESS = offset!(self.vmi, _EPROCESS);
168
169 let va = self.vmi.read_va_native(self.va + EPROCESS.Peb.offset())?;
170
171 if va.is_null() {
172 return Ok(None);
173 }
174
175 let root = self.translation_root()?;
176
177 Ok(Some(WindowsPeb::with_kind(
178 self.vmi,
179 va,
180 root,
181 WindowsWow64Kind::Native,
182 )))
183 }
184
185 pub fn session(&self) -> Result<Option<WindowsSession<'a, Driver>>, VmiError> {
187 let EPROCESS = offset!(self.vmi, _EPROCESS);
188
189 let session = self
190 .vmi
191 .read_va_native(self.va + EPROCESS.Session.offset())?;
192
193 if session.is_null() {
194 return Ok(None);
195 }
196
197 Ok(Some(WindowsSession::new(self.vmi, session)))
198 }
199
200 pub fn token(&self) -> Result<WindowsToken<'a, Driver>, VmiError> {
206 let EPROCESS = offset!(self.vmi, _EPROCESS);
207
208 let token = self
209 .vmi
210 .os()
211 .read_fast_ref(self.va + EPROCESS.Token.offset())?;
212
213 Ok(WindowsToken::new(self.vmi, token))
214 }
215
216 pub fn handle_table(&self) -> Result<Option<WindowsHandleTable<'a, Driver>>, VmiError> {
222 let EPROCESS = offset!(self.vmi, _EPROCESS);
223
224 let handle_table = self
225 .vmi
226 .read_va_native(self.va + EPROCESS.ObjectTable.offset())?;
227
228 if handle_table.is_null() {
229 return Ok(None);
230 }
231
232 Ok(Some(WindowsHandleTable::new(self.vmi, handle_table)))
233 }
234
235 pub fn lookup_object<T>(&self, handle: u64) -> Result<Option<T>, VmiError>
277 where
278 T: FromWindowsObject<'a, Driver>,
279 {
280 if let Some(handle_table) = self.handle_table()?
281 && let Some(entry) = handle_table.lookup(handle)?
282 && let Some(object) = entry.object()?
283 {
284 return T::from_object(object);
285 }
286
287 Ok(None)
288 }
289
290 pub fn vad_root(&self) -> Result<Option<WindowsRegion<'a, Driver>>, VmiError> {
297 let node = match &self.vmi.underlying_os().offsets.ext() {
298 Some(OffsetsExt::V1(offsets)) => self.vad_root_v1(offsets)?,
299 Some(OffsetsExt::V2(offsets)) => self.vad_root_v2(offsets)?,
300 None => panic!("OffsetsExt not set"),
301 };
302
303 if node.is_null() {
304 return Ok(None);
305 }
306
307 Ok(Some(WindowsRegion::new(self.vmi, node)))
308 }
309
310 fn vad_root_v1(&self, offsets_ext: &v1::Offsets) -> Result<Va, VmiError> {
311 let EPROCESS = offset!(self.vmi, _EPROCESS);
312 let MM_AVL_TABLE = &offsets_ext._MM_AVL_TABLE;
313
314 let vad_root = self.va + EPROCESS.VadRoot.offset() + MM_AVL_TABLE.BalancedRoot.offset();
317
318 Ok(vad_root)
319 }
320
321 fn vad_root_v2(&self, offsets_ext: &v2::Offsets) -> Result<Va, VmiError> {
322 let EPROCESS = offset!(self.vmi, _EPROCESS);
323 let RTL_AVL_TREE = &offsets_ext._RTL_AVL_TREE;
324
325 let vad_root = self
328 .vmi
329 .read_va_native(self.va + EPROCESS.VadRoot.offset() + RTL_AVL_TREE.Root.offset())?;
330
331 Ok(vad_root)
332 }
333
334 pub fn vad_hint(&self) -> Result<Option<WindowsRegion<'a, Driver>>, VmiError> {
344 let node = match &self.vmi.underlying_os().offsets.ext() {
345 Some(OffsetsExt::V1(offsets)) => self.vad_hint_v1(offsets)?,
346 Some(OffsetsExt::V2(offsets)) => self.vad_hint_v2(offsets)?,
347 None => panic!("OffsetsExt not set"),
348 };
349
350 if node.is_null() {
351 return Ok(None);
352 }
353
354 Ok(Some(WindowsRegion::new(self.vmi, node)))
355 }
356
357 fn vad_hint_v1(&self, offsets_ext: &v1::Offsets) -> Result<Va, VmiError> {
358 let EPROCESS = offset!(self.vmi, _EPROCESS);
359 let MM_AVL_TABLE = &offsets_ext._MM_AVL_TABLE;
360
361 self.vmi
362 .read_va_native(self.va + EPROCESS.VadRoot.offset() + MM_AVL_TABLE.NodeHint.offset())
363 }
364
365 fn vad_hint_v2(&self, _offsets_ext: &v2::Offsets) -> Result<Va, VmiError> {
366 let EPROCESS = offset!(self.vmi, _EPROCESS);
367
368 let VadHint = EPROCESS
369 .VadHint
370 .expect("VadHint is not present in common offsets");
371
372 self.vmi.read_va_native(self.va + VadHint.offset())
373 }
374}
375
376impl<'a, Driver> VmiOsProcess<'a, Driver> for WindowsProcess<'a, Driver>
377where
378 Driver: VmiRead,
379 Driver::Architecture: ArchAdapter<Driver>,
380{
381 type Os = WindowsOs<Driver>;
382
383 fn id(&self) -> Result<ProcessId, VmiError> {
389 let EPROCESS = offset!(self.vmi, _EPROCESS);
390
391 let result = self
392 .vmi
393 .read_u32(self.va + EPROCESS.UniqueProcessId.offset())?;
394
395 Ok(ProcessId(result))
396 }
397
398 fn object(&self) -> Result<ProcessObject, VmiError> {
400 Ok(ProcessObject(self.va))
401 }
402
403 fn name(&self) -> Result<String, VmiError> {
409 let EPROCESS = offset!(self.vmi, _EPROCESS);
410
411 self.vmi
412 .read_string(self.va + EPROCESS.ImageFileName.offset())
413 }
414
415 fn parent_id(&self) -> Result<ProcessId, VmiError> {
421 let EPROCESS = offset!(self.vmi, _EPROCESS);
422
423 let result = self
424 .vmi
425 .read_u32(self.va + EPROCESS.InheritedFromUniqueProcessId.offset())?;
426
427 Ok(ProcessId(result))
428 }
429
430 fn architecture(&self) -> Result<VmiOsImageArchitecture, VmiError> {
438 let EPROCESS = offset!(self.vmi, _EPROCESS);
439
440 let wow64process = self
441 .vmi
442 .read_va_native(self.va + EPROCESS.WoW64Process.offset())?;
443
444 if wow64process.is_null() {
445 Ok(VmiOsImageArchitecture::Amd64)
446 }
447 else {
448 Ok(VmiOsImageArchitecture::X86)
449 }
450 }
451
452 fn translation_root(&self) -> Result<Pa, VmiError> {
458 let KPROCESS = offset!(self.vmi, _KPROCESS);
459
460 let dtb = self
467 .vmi
468 .read_address_native(self.va + KPROCESS.DirectoryTableBase.offset())?;
469
470 Ok(Driver::Architecture::dtb_to_root(dtb))
471 }
472
473 fn user_translation_root(&self) -> Result<Pa, VmiError> {
482 let KPROCESS = offset!(self.vmi, _KPROCESS);
483 let UserDirectoryTableBase = match &KPROCESS.UserDirectoryTableBase {
484 Some(UserDirectoryTableBase) => UserDirectoryTableBase,
485 None => return self.translation_root(),
486 };
487
488 let dtb = self
489 .vmi
490 .read_address_native(self.va + UserDirectoryTableBase.offset())?;
491
492 if dtb < Driver::Architecture::PAGE_SIZE {
493 return self.translation_root();
494 }
495
496 Ok(Driver::Architecture::dtb_to_root(dtb))
497 }
498
499 fn image_base(&self) -> Result<Va, VmiError> {
505 let EPROCESS = offset!(self.vmi, _EPROCESS);
506
507 self.vmi
508 .read_va_native(self.va + EPROCESS.SectionBaseAddress.offset())
509 }
510
511 fn regions(
517 &self,
518 ) -> Result<
519 impl Iterator<Item = Result<WindowsRegion<'a, Driver>, VmiError>> + use<'a, Driver>,
520 VmiError,
521 > {
522 let vmi = self.vmi;
523 let iterator = match self.vad_root()? {
524 Some(vad_root) => TreeNodeIterator::new(vmi, vad_root.va()),
525 None => TreeNodeIterator::empty(vmi),
526 };
527
528 Ok(iterator.map(move |result| result.map(|vad| WindowsRegion::new(vmi, vad))))
529 }
530
531 fn lookup_region(&self, address: Va) -> Result<Option<WindowsRegion<'a, Driver>>, VmiError> {
542 let vad = match self.vad_hint()? {
543 Some(vad) => vad,
544 None => return Ok(None),
545 };
546
547 let vpn = address.0 >> 12;
548
549 if vpn >= vad.starting_vpn()? && vpn <= vad.ending_vpn()? {
550 return Ok(Some(vad));
551 }
552
553 let mut next = self.vad_root()?;
554 while let Some(vad) = next {
555 if vpn < vad.starting_vpn()? {
556 next = vad.left_child()?;
557 }
558 else if vpn > vad.ending_vpn()? {
559 next = vad.right_child()?;
560 }
561 else {
562 return Ok(Some(vad));
563 }
564 }
565
566 Ok(None)
567 }
568
569 fn threads(
580 &self,
581 ) -> Result<
582 impl Iterator<Item = Result<<Self::Os as vmi_core::VmiOs>::Thread<'a>, VmiError>>
583 + use<'a, Driver>,
584 VmiError,
585 > {
586 let EPROCESS = offset!(self.vmi, _EPROCESS);
587 let ETHREAD = offset!(self.vmi, _ETHREAD);
588
589 let vmi = self.vmi;
590 Ok(ListEntryIterator::new(
591 vmi,
592 self.va + EPROCESS.ThreadListHead.offset(),
593 ETHREAD.ThreadListEntry.offset(),
594 )
595 .map(move |result| result.map(|entry| WindowsThread::new(vmi, ThreadObject(entry)))))
596 }
597
598 fn is_valid_address(&self, address: Va) -> Result<Option<bool>, VmiError> {
603 if Driver::Architecture::is_page_present_or_transition(self.vmi, address)? {
625 return Ok(Some(true));
626 }
627
628 let vad = match self.lookup_region(address)? {
629 Some(vad) => vad,
630 None => return Ok(Some(false)),
631 };
632
633 const MM_ZERO_ACCESS: u8 = 0; const MM_DECOMMIT: u8 = 0x10; const MM_NOACCESS: u8 = 0x18; const VadImageMap: u8 = 2;
638
639 if matches!(
640 vad.vad_protection()?,
641 MM_ZERO_ACCESS | MM_DECOMMIT | MM_NOACCESS
642 ) {
643 return Ok(Some(false));
644 }
645
646 Ok(Some(
647 (vad.private_memory()? && vad.mem_commit()?) ||
649
650 (!vad.private_memory()? && vad.vad_type()? == VadImageMap),
660 ))
661 }
662}