1#![expect(non_snake_case)]
2
3use std::{
4 fmt::Debug,
5 hash::{Hash, Hasher},
6};
7
8use vmi::{
9 Registers as _, Va, VmiContext, VmiError, VmiEventResponse, VmiVa as _,
10 arch::amd64::Amd64,
11 driver::VmiFullDriver,
12 os::{
13 ProcessObject, ThreadObject, VmiOsProcess as _, VmiOsThread as _,
14 windows::{WindowsFileObject, WindowsOs, WindowsOsExt as _},
15 },
16 trace::Hex,
17};
18
19use super::{MonitorState, Process, Thread};
20use crate::file_transfer::FileTransfer;
21
22type MonitorCallback<Driver> = fn(
24 &VmiContext<WindowsOs<Driver>>,
25 &mut MonitorState<Driver>,
26) -> Result<VmiEventResponse<Amd64>, VmiError>;
27
28pub struct MonitorHook<Driver>
32where
33 Driver: VmiFullDriver<Architecture = Amd64>,
34{
35 pub name: &'static str,
37
38 pub callback: MonitorCallback<Driver>,
40}
41
42impl<Driver> Debug for MonitorHook<Driver>
43where
44 Driver: VmiFullDriver<Architecture = Amd64>,
45{
46 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47 f.write_str(self.name)
48 }
49}
50
51impl<Driver> Clone for MonitorHook<Driver>
52where
53 Driver: VmiFullDriver<Architecture = Amd64>,
54{
55 fn clone(&self) -> Self {
56 *self
57 }
58}
59
60impl<Driver> Copy for MonitorHook<Driver> where Driver: VmiFullDriver<Architecture = Amd64> {}
61
62impl<Driver> PartialEq for MonitorHook<Driver>
63where
64 Driver: VmiFullDriver<Architecture = Amd64>,
65{
66 fn eq(&self, other: &Self) -> bool {
67 self.name.eq(other.name)
68 }
69}
70
71impl<Driver> Eq for MonitorHook<Driver> where Driver: VmiFullDriver<Architecture = Amd64> {}
72
73impl<Driver> Hash for MonitorHook<Driver>
74where
75 Driver: VmiFullDriver<Architecture = Amd64>,
76{
77 fn hash<H: Hasher>(&self, state: &mut H) {
78 self.name.hash(state);
79 }
80}
81
82#[tracing::instrument(skip_all)]
87pub fn PspInsertProcess<Driver>(
88 vmi: &VmiContext<WindowsOs<Driver>>,
89 state: &mut MonitorState<Driver>,
90) -> Result<VmiEventResponse<Amd64>, VmiError>
91where
92 Driver: VmiFullDriver<Architecture = Amd64>,
93{
94 let NewProcess = ProcessObject(Va(vmi.os().function_argument(0)?));
106 let Parent = ProcessObject(Va(vmi.os().function_argument(1)?));
107
108 tracing::trace!(%NewProcess, %Parent);
109
110 let new_process = vmi.os().process(NewProcess)?;
111 let parent = vmi.os().process(Parent)?;
112
113 let process = Process::new(new_process.id()?, parent.id()?, new_process.name()?);
114
115 tracing::debug!(
116 name = process.name,
117 pid = %process.pid,
118 ppid = %process.ppid,
119 process = %NewProcess,
120 parent = %Parent,
121 );
122
123 if state.target_process.is_none()
125 && let Some(creator_thread) = state.creator_thread
126 && vmi.os().current_thread()?.object()? == creator_thread
127 {
128 state.target_process = Some(NewProcess);
129 state.creator_thread = None;
130
131 tracing::info!(
132 name = process.name,
133 pid = %process.pid,
134 process = %NewProcess,
135 "deployed process started"
136 );
137 }
138
139 state.tracker.insert_process(NewProcess, process);
140
141 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
142}
143
144#[tracing::instrument(skip_all)]
149pub fn MmCleanProcessAddressSpace<Driver>(
150 vmi: &VmiContext<WindowsOs<Driver>>,
151 state: &mut MonitorState<Driver>,
152) -> Result<VmiEventResponse<Amd64>, VmiError>
153where
154 Driver: VmiFullDriver<Architecture = Amd64>,
155{
156 let Process = ProcessObject(Va(vmi.os().function_argument(0)?));
164
165 tracing::trace!(%Process);
166
167 let process = match state.tracker.retire_process(Process) {
168 Some(process) => process,
169 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
170 };
171
172 tracing::debug!(
173 name = process.name,
174 pid = %process.pid,
175 process = %Process,
176 );
177
178 if state.target_process == Some(Process) {
179 state.output = Some(Ok(Some(process.pid)));
180
181 tracing::info!(
182 name = process.name,
183 pid = %process.pid,
184 process = %Process,
185 "deployed process terminated"
186 );
187 }
188
189 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
190}
191
192#[tracing::instrument(skip_all)]
197pub fn PspInsertThread<Driver>(
198 vmi: &VmiContext<WindowsOs<Driver>>,
199 state: &mut MonitorState<Driver>,
200) -> Result<VmiEventResponse<Amd64>, VmiError>
201where
202 Driver: VmiFullDriver<Architecture = Amd64>,
203{
204 let Thread = ThreadObject(Va(vmi.os().function_argument(0)?));
214 let Process = ProcessObject(Va(vmi.os().function_argument(1)?));
215
216 tracing::trace!(%Thread, %Process);
217
218 let os_thread = vmi.os().thread(Thread)?;
219 let os_process = vmi.os().process(Process)?;
220
221 let tid = os_thread.id()?;
222 let pid = os_process.id()?;
223
224 let thread_object = Thread;
225 let process_object = Process;
226
227 state.tracker.try_insert_thread(
228 process_object,
229 thread_object,
230 || {
231 Ok::<_, VmiError>(Process::new(
232 pid,
233 os_process.parent_id()?,
234 os_process.name()?,
235 ))
236 },
237 Thread::new(tid),
238 )?;
239
240 tracing::debug!(
241 %pid,
242 %tid,
243 process = %process_object,
244 thread = %thread_object,
245 );
246
247 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
248}
249
250#[tracing::instrument(skip_all)]
255pub fn KeTerminateThread<Driver>(
256 vmi: &VmiContext<WindowsOs<Driver>>,
257 state: &mut MonitorState<Driver>,
258) -> Result<VmiEventResponse<Amd64>, VmiError>
259where
260 Driver: VmiFullDriver<Architecture = Amd64>,
261{
262 let Thread = ThreadObject(Va(vmi.os().function_argument(0)?));
270
271 if state.creator_thread == Some(Thread) {
275 state.creator_thread = None;
276 }
277
278 tracing::trace!(%Thread);
279
280 let os_thread = vmi.os().thread(Thread)?;
281 let os_process = os_thread.process()?;
282
283 let tid = os_thread.id()?;
284 let pid = os_process.id()?;
285
286 let thread_object = Thread;
287 let process_object = os_process.object()?;
288
289 let thread = match state.tracker.retire_thread(process_object, thread_object) {
290 Some(thread) => thread,
291 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
292 };
293
294 tracing::debug!(
295 %pid,
296 %tid,
297 process = %process_object,
298 thread = %thread_object,
299 transfer_active = thread.file_transfer.is_some(),
300 );
301
302 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
303}
304
305#[tracing::instrument(skip_all)]
310pub fn NtWriteFile<Driver>(
311 vmi: &VmiContext<WindowsOs<Driver>>,
312 state: &mut MonitorState<Driver>,
313) -> Result<VmiEventResponse<Amd64>, VmiError>
314where
315 Driver: VmiFullDriver<Architecture = Amd64>,
316{
317 let current_process = vmi.os().current_process()?;
334 let process_object = current_process.object()?;
335
336 if state.target_process != Some(process_object) {
337 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
338 }
339
340 let FileHandle = vmi.os().function_argument(0)?;
341
342 tracing::trace!(FileHandle = %Hex(FileHandle));
343
344 if vmi.os().is_kernel_handle(FileHandle)? {
345 tracing::debug!(handle = %Hex(FileHandle), "kernel handle, skipping");
346 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
347 }
348
349 let process = match state.tracker.get_process_mut(process_object) {
350 Some(process) => process,
351 None => {
352 tracing::warn!("target process not tracked");
353 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
354 }
355 };
356
357 let file_object = match current_process.lookup_object::<WindowsFileObject<_>>(FileHandle)? {
358 Some(file_object) => file_object,
359 None => {
360 tracing::warn!(handle = %Hex(FileHandle), "cannot resolve file handle");
361 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
362 }
363 };
364
365 let path = file_object.full_path()?;
366 let transfer = FileTransfer::new(FileHandle, file_object.va(), path.clone());
367
368 if process.record_file_transfer(transfer) {
369 tracing::info!(handle = %Hex(FileHandle), path, "recorded file transfer");
370 }
371
372 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
373}
374
375#[tracing::instrument(skip_all)]
381pub fn NtClose<Driver>(
382 vmi: &VmiContext<WindowsOs<Driver>>,
383 state: &mut MonitorState<Driver>,
384) -> Result<VmiEventResponse<Amd64>, VmiError>
385where
386 Driver: VmiFullDriver<Architecture = Amd64>,
387{
388 let current_thread = vmi.os().current_thread()?;
397 let thread_object = current_thread.object()?;
398
399 if let Some(thread) = state.tracker.get_thread(thread_object)
400 && thread.file_transfer.is_some()
401 {
402 return advance_file_transfer(vmi, state, thread_object);
403 }
404
405 let current_process = current_thread.current_process()?;
407 let process_object = current_process.object()?;
408
409 if state.target_process != Some(process_object) {
410 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
411 }
412
413 if !state.tracker.contains_thread(thread_object) {
414 tracing::warn!(%thread_object, "close on untracked target thread");
415 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
416 }
417
418 let Handle = vmi.os().function_argument(0)?;
419
420 tracing::trace!(Handle = %Hex(Handle));
421
422 if vmi.os().is_kernel_handle(Handle)? {
423 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
424 }
425
426 let mut transfer = match state
428 .tracker
429 .get_process_mut(process_object)
430 .and_then(|process| process.take_file_transfer(Handle))
431 {
432 Some(transfer) => transfer,
433 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
434 };
435
436 if let Some(file_object) = current_process.lookup_object::<WindowsFileObject<_>>(Handle)?
437 && file_object.va() != transfer.file_object()
438 {
439 tracing::warn!(
440 handle = %Hex(Handle),
441 path = transfer.path(),
442 "discarding stale file-transfer handle"
443 );
444 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
445 }
446
447 transfer.start();
448
449 if let Some(thread) = state.tracker.get_thread_mut(thread_object) {
450 thread.file_transfer = Some(transfer);
451 }
452
453 advance_file_transfer(vmi, state, thread_object)
454}
455
456fn advance_file_transfer<Driver>(
458 vmi: &VmiContext<WindowsOs<Driver>>,
459 state: &mut MonitorState<Driver>,
460 thread_object: ThreadObject,
461) -> Result<VmiEventResponse<Amd64>, VmiError>
462where
463 Driver: VmiFullDriver<Architecture = Amd64>,
464{
465 let thread = match state.tracker.get_thread_mut(thread_object) {
466 Some(thread) => thread,
467 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
468 };
469
470 let file_transfer = match thread.file_transfer.as_mut() {
471 Some(file_transfer) => file_transfer,
472 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
473 };
474
475 let registers = match file_transfer.execute(vmi)? {
476 Some(registers) => registers,
477 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
478 };
479
480 if !file_transfer.done() {
481 return Ok(VmiEventResponse::default().with_registers(registers.gp_registers()));
482 }
483
484 let file_transfer = match thread.file_transfer.take() {
485 Some(file_transfer) => file_transfer,
486 None => return Ok(VmiEventResponse::fast_singlestep(vmi.default_view())),
487 };
488
489 tracing::info!(
490 path = file_transfer.path(),
491 "file-transfer injection completed"
492 );
493
494 Ok(VmiEventResponse::fast_singlestep(vmi.default_view())
495 .with_registers(registers.gp_registers()))
496}