windows_recipe_messagebox/windows-recipe-messagebox.rs
1//! # [`recipe!`] example
2//!
3//! This example demonstrates how to write a simple recipe.
4//!
5//! The recipe is injected into the `explorer.exe` process and shows
6//! a message box.
7//!
8//! # Possible log output
9//!
10//! ```text
11//! DEBUG domain_id=XenDomainId(102)
12//! DEBUG found MZ base_address=0xfffff80002861000
13//! INFO profile already exists profile_path="cache/windows/ntkrnlmp.pdb/3844dbb920174967be7aa4a2c20430fa2/profile.json"
14//! INFO Creating VMI session
15//! INFO found explorer.exe pid=1248 object=0xfffffa80030e9060
16//! DEBUG injector{vcpu=2 rip=0x0000000077c618ca}:memory_access: thread hijacked current_tid=1488
17//! DEBUG injector{vcpu=2 rip=0x0000000077c618ca}:memory_access: recipe step index=0
18//! DEBUG injector{vcpu=1 rip=0x0000000077c618ca}:memory_access: recipe finished result=0x0000000000000001
19//! ```
20
21mod common;
22
23use vmi::{
24 VcpuId, VmiDriver,
25 arch::amd64::Amd64,
26 os::{VmiOsProcess as _, windows::WindowsOs},
27 utils::injector::{InjectorHandler, Recipe, recipe},
28};
29
30struct MessageBox {
31 caption: String,
32 text: String,
33}
34
35impl MessageBox {
36 pub fn new(caption: impl AsRef<str>, text: impl AsRef<str>) -> Self {
37 Self {
38 caption: caption.as_ref().to_string(),
39 text: text.as_ref().to_string(),
40 }
41 }
42}
43
44#[rustfmt::skip]
45fn recipe_factory<Driver>(data: MessageBox) -> Recipe<Driver, WindowsOs<Driver>, MessageBox>
46where
47 Driver: VmiDriver<Architecture = Amd64>,
48{
49 recipe![
50 Recipe::<_, WindowsOs<Driver>, _>::new(data),
51 {
52 inject! {
53 user32!MessageBoxA(
54 0, // hWnd
55 data![text], // lpText
56 data![caption], // lpCaption
57 0 // uType
58 )
59 }
60 }
61 ]
62}
63
64fn main() -> Result<(), Box<dyn std::error::Error>> {
65 let (session, profile) = common::create_vmi_session()?;
66
67 let explorer_pid = {
68 // This block is used to drop the pause guard after the PID is found.
69 // If the `session.handle()` would be called with the VM paused, no
70 // events would be triggered.
71 let _pause_guard = session.pause_guard()?;
72
73 let registers = session.registers(VcpuId(0))?;
74 let vmi = session.with_registers(®isters);
75
76 let explorer = match common::find_process(&vmi, "explorer.exe")? {
77 Some(explorer) => explorer,
78 None => {
79 tracing::error!("explorer.exe not found");
80 return Ok(());
81 }
82 };
83
84 tracing::info!(
85 pid = %explorer.id()?,
86 object = %explorer.object()?,
87 "found explorer.exe"
88 );
89
90 explorer.id()?
91 };
92
93 session.handle(|session| {
94 InjectorHandler::new(
95 session,
96 &profile,
97 explorer_pid,
98 recipe_factory(MessageBox::new(
99 "Hello, World!",
100 "This is a message box from the VMI!",
101 )),
102 )
103 })?;
104
105 Ok(())
106}