Skip to main content

VmiSession

Struct VmiSession 

Source
pub struct VmiSession<'a, Os>
where Os: VmiOs,
{ /* private fields */ }
Expand description

A VMI session.

The session combines a VmiCore with an OS-specific VmiOs implementation to provide unified access to both low-level VMI operations and higher-level OS abstractions.

Implementations§

Source§

impl<'a, Os> VmiSession<'a, Os>
where Os: VmiOs,

Source

pub fn new( core: &'a VmiCore<<Os as VmiOs>::Driver>, os: &'a Os, ) -> VmiSession<'a, Os>

Available on crate features utils and injector only.

Creates a new VMI session.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 569)
524fn main() -> Result<(), Box<dyn std::error::Error>> {
525    tracing_subscriber::fmt()
526        .with_max_level(tracing::Level::DEBUG)
527        .init();
528
529    let domain_id = 'x: {
530        for name in &["win7", "win10", "win11", "ubuntu22"] {
531            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
532                break 'x domain_id;
533            }
534        }
535
536        panic!("Domain not found");
537    };
538
539    tracing::debug!(?domain_id);
540
541    // Setup VMI.
542    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
543    let core = VmiCore::new(driver)?;
544
545    // Try to find the kernel information.
546    // This is necessary in order to load the profile.
547    let kernel_info = {
548        let _pause_guard = core.pause_guard()?;
549        let regs = core.registers(0.into())?;
550
551        WindowsOs::find_kernel(&core, &regs)?.expect("kernel information")
552    };
553
554    // Load the profile.
555    // The profile contains offsets to kernel functions and data structures.
556    let isr = IsrCache::new("cache")?;
557    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
558    let profile = entry.profile()?;
559
560    // Create the VMI session.
561    tracing::info!("Creating VMI session");
562    let terminate_flag = Arc::new(AtomicBool::new(false));
563    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
564    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
565    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
566    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
567
568    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
569    let session = VmiSession::new(&core, &os);
570
571    session.handle(|session| Monitor::new(session, &profile, terminate_flag))?;
572
573    Ok(())
574}
More examples
Hide additional examples
examples/windows-dump.rs (line 428)
393fn main() -> Result<(), Box<dyn std::error::Error>> {
394    tracing_subscriber::fmt()
395        .with_max_level(tracing::Level::DEBUG)
396        .with_ansi(false)
397        .init();
398
399    // First argument is the path to the dump file.
400    let args = std::env::args().collect::<Vec<_>>();
401    if args.len() != 2 {
402        eprintln!("Usage: {} <dump-file>", args[0]);
403        std::process::exit(1);
404    }
405
406    let dump_file = &args[1];
407
408    // Setup VMI.
409    let driver = Driver::new(dump_file)?;
410    let core = VmiCore::new(driver)?;
411
412    let registers = core.registers(VcpuId(0))?;
413
414    // Try to find the kernel information.
415    // This is necessary in order to load the profile.
416    let kernel_info = WindowsOs::find_kernel(&core, &registers)?.expect("kernel information");
417    tracing::info!(?kernel_info, "Kernel information");
418
419    // Load the profile.
420    // The profile contains offsets to kernel functions and data structures.
421    let isr = IsrCache::new("cache")?;
422    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
423    let profile = entry.profile()?;
424
425    // Create the VMI session.
426    tracing::info!("Creating VMI session");
427    let os = WindowsOs::<Driver>::with_kernel_base(&profile, kernel_info.base_address)?;
428    let session = VmiSession::new(&core, &os);
429
430    let vmi = session.with_registers(&registers);
431    let root_directory = vmi.os().object_root_directory()?;
432
433    println!("Kernel Modules:");
434    println!("=================================================");
435    enumerate_kernel_modules(&vmi)?;
436
437    println!("Object Tree (root directory: {}):", root_directory.va());
438    println!("=================================================");
439    enumerate_directory_object(&root_directory, 0)?;
440
441    println!("Processes:");
442    println!("=================================================");
443    enumerate_processes(&vmi)?;
444
445    Ok(())
446}
examples/common/mod.rs (line 73)
15pub fn create_vmi_session() -> Result<Session, Box<dyn std::error::Error>> {
16    tracing_subscriber::fmt()
17        .with_max_level(tracing::Level::DEBUG)
18        .with_target(false)
19        .init();
20
21    let domain_id = 'x: {
22        for name in &["win7", "win10", "win11", "ubuntu22"] {
23            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
24                break 'x domain_id;
25            }
26        }
27
28        panic!("Domain not found");
29    };
30
31    tracing::debug!(?domain_id);
32
33    // Setup VMI.
34    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
35    let core = VmiCore::new(driver)?;
36
37    // Try to find the kernel information.
38    // This is necessary in order to load the profile.
39    let kernel_info = {
40        // Pause the vCPU to get consistent state.
41        let _pause_guard = core.pause_guard()?;
42
43        // Get the register state for the first vCPU.
44        let registers = core.registers(VcpuId(0))?;
45
46        // On AMD64 architecture, the kernel is usually found using the
47        // `MSR_LSTAR` register, which contains the address of the system call
48        // handler. This register is set by the operating system during boot
49        // and is left unchanged (unless some rootkits are involved).
50        //
51        // Therefore, we can take an arbitrary registers at any point in time
52        // (as long as the OS has booted and the page tables are set up) and
53        // use them to find the kernel.
54        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
55    };
56
57    // Load the profile.
58    // The profile contains offsets to kernel functions and data structures.
59    let isr = IsrCache::new("cache")?;
60    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
61    let entry = Box::leak(Box::new(entry));
62    let profile = entry.profile()?;
63
64    // Create the VMI session.
65    tracing::info!("Creating VMI session");
66    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
67
68    // Please don't do this in production code.
69    // This is only done for the sake of the example.
70    let core = Box::leak(Box::new(core));
71    let os = Box::leak(Box::new(os));
72
73    Ok((VmiSession::new(core, os), profile))
74}
examples/basic-process-list.rs (line 57)
13fn main() -> Result<(), Box<dyn std::error::Error>> {
14    let domain_id = 'x: {
15        for name in &["win7", "win10", "win11", "ubuntu22"] {
16            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
17                break 'x domain_id;
18            }
19        }
20
21        panic!("Domain not found");
22    };
23
24    // Setup VMI.
25    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
26    let core = VmiCore::new(driver)?;
27
28    // Try to find the kernel information.
29    // This is necessary in order to load the profile.
30    let kernel_info = {
31        // Pause the VM to get consistent state.
32        let _pause_guard = core.pause_guard()?;
33
34        // Get the register state for the first vCPU.
35        let registers = core.registers(VcpuId(0))?;
36
37        // On AMD64 architecture, the kernel is usually found using the
38        // `MSR_LSTAR` register, which contains the address of the system call
39        // handler. This register is set by the operating system during boot
40        // and is left unchanged (unless some rootkits are involved).
41        //
42        // Therefore, we can take an arbitrary registers at any point in time
43        // (as long as the OS has booted and the page tables are set up) and
44        // use them to find the kernel.
45        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
46    };
47
48    // Load the profile.
49    // The profile contains offsets to kernel functions and data structures.
50    let isr = IsrCache::new("cache")?;
51    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
52    let profile = entry.profile()?;
53
54    // Create the VMI session.
55    tracing::info!("Creating VMI session");
56    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
57    let session = VmiSession::new(&core, &os);
58
59    // Pause the VM again to get consistent state.
60    let _pause_guard = session.pause_guard()?;
61
62    // Create a new `VmiState` with the current register.
63    let registers = session.registers(VcpuId(0))?;
64    let vmi = session.with_registers(&registers);
65
66    // Get the list of processes and print them.
67    for process in vmi.os().processes()? {
68        let process = process?;
69
70        println!(
71            "{} [{}] {} (root @ {})",
72            process.object()?,
73            process.id()?,
74            process.name()?,
75            process.translation_root()?
76        );
77    }
78
79    Ok(())
80}
Source

pub fn with_registers( &'a self, registers: &'a <<Os as VmiOs>::Architecture as Architecture>::Registers, ) -> VmiState<'a, Os>

Available on crate features utils and injector only.

Creates a new VMI state with the specified registers.

Examples found in repository?
examples/windows-recipe-messagebox.rs (line 75)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let (session, _profile) = common::create_vmi_session()?;
67
68    let explorer_pid = {
69        // This block is used to drop the pause guard after the PID is found.
70        // If the `session.handle()` would be called with the VM paused, no
71        // events would be triggered.
72        let _pause_guard = session.pause_guard()?;
73
74        let registers = session.registers(VcpuId(0))?;
75        let vmi = session.with_registers(&registers);
76
77        let explorer = match common::find_process(&vmi, "explorer.exe")? {
78            Some(explorer) => explorer,
79            None => {
80                tracing::error!("explorer.exe not found");
81                return Ok(());
82            }
83        };
84
85        tracing::info!(
86            pid = %explorer.id()?,
87            object = %explorer.object()?,
88            "found explorer.exe"
89        );
90
91        explorer.id()?
92    };
93
94    session.handle(|session| {
95        UserInjectorHandler::new(
96            session,
97            recipe_factory(MessageBox::new(
98                "Hello, World!",
99                "This is a message box from the VMI!",
100            )),
101        )?
102        .with_pid(explorer_pid)
103    })?;
104
105    Ok(())
106}
More examples
Hide additional examples
examples/windows-recipe-writefile.rs (line 213)
203fn main() -> Result<(), Box<dyn std::error::Error>> {
204    let (session, _profile) = common::create_vmi_session()?;
205
206    let explorer_pid = {
207        // This block is used to drop the pause guard after the PID is found.
208        // If the `session.handle()` would be called with the VM paused, no
209        // events would be triggered.
210        let _pause_guard = session.pause_guard()?;
211
212        let registers = session.registers(VcpuId(0))?;
213        let vmi = session.with_registers(&registers);
214
215        let explorer = match common::find_process(&vmi, "explorer.exe")? {
216            Some(explorer) => explorer,
217            None => {
218                tracing::error!("explorer.exe not found");
219                return Ok(());
220            }
221        };
222
223        tracing::info!(
224            pid = %explorer.id()?,
225            object = %explorer.object()?,
226            "found explorer.exe"
227        );
228
229        explorer.id()?
230    };
231
232    session.handle(|session| {
233        UserInjectorHandler::new(
234            session,
235            recipe_factory(GuestFile::new(
236                "C:\\Users\\John\\Desktop\\test.txt",
237                "Hello, World!".as_bytes(),
238            )),
239        )?
240        .with_pid(explorer_pid)
241    })?;
242
243    Ok(())
244}
examples/windows-recipe-writefile-advanced.rs (line 311)
301fn main() -> Result<(), Box<dyn std::error::Error>> {
302    let (session, _profile) = common::create_vmi_session()?;
303
304    let explorer_pid = {
305        // This block is used to drop the pause guard after the PID is found.
306        // If the `session.handle()` would be called with the VM paused, no
307        // events would be triggered.
308        let _pause_guard = session.pause_guard()?;
309
310        let registers = session.registers(VcpuId(0))?;
311        let vmi = session.with_registers(&registers);
312
313        let explorer = match common::find_process(&vmi, "explorer.exe")? {
314            Some(explorer) => explorer,
315            None => {
316                tracing::error!("explorer.exe not found");
317                return Ok(());
318            }
319        };
320
321        tracing::info!(
322            pid = %explorer.id()?,
323            object = %explorer.object()?,
324            "found explorer.exe"
325        );
326
327        explorer.id()?
328    };
329
330    let mut content = Vec::new();
331    for c in 'A'..='Z' {
332        content.extend((0..2049).map(|_| c as u8).collect::<Vec<_>>());
333    }
334
335    session.handle(|session| {
336        UserInjectorHandler::new(
337            session,
338            recipe_factory(GuestFile::new(
339                "C:\\Users\\John\\Desktop\\test.txt",
340                content,
341            )),
342        )?
343        .with_pid(explorer_pid)
344    })?;
345
346    Ok(())
347}
examples/windows-dump.rs (line 430)
393fn main() -> Result<(), Box<dyn std::error::Error>> {
394    tracing_subscriber::fmt()
395        .with_max_level(tracing::Level::DEBUG)
396        .with_ansi(false)
397        .init();
398
399    // First argument is the path to the dump file.
400    let args = std::env::args().collect::<Vec<_>>();
401    if args.len() != 2 {
402        eprintln!("Usage: {} <dump-file>", args[0]);
403        std::process::exit(1);
404    }
405
406    let dump_file = &args[1];
407
408    // Setup VMI.
409    let driver = Driver::new(dump_file)?;
410    let core = VmiCore::new(driver)?;
411
412    let registers = core.registers(VcpuId(0))?;
413
414    // Try to find the kernel information.
415    // This is necessary in order to load the profile.
416    let kernel_info = WindowsOs::find_kernel(&core, &registers)?.expect("kernel information");
417    tracing::info!(?kernel_info, "Kernel information");
418
419    // Load the profile.
420    // The profile contains offsets to kernel functions and data structures.
421    let isr = IsrCache::new("cache")?;
422    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
423    let profile = entry.profile()?;
424
425    // Create the VMI session.
426    tracing::info!("Creating VMI session");
427    let os = WindowsOs::<Driver>::with_kernel_base(&profile, kernel_info.base_address)?;
428    let session = VmiSession::new(&core, &os);
429
430    let vmi = session.with_registers(&registers);
431    let root_directory = vmi.os().object_root_directory()?;
432
433    println!("Kernel Modules:");
434    println!("=================================================");
435    enumerate_kernel_modules(&vmi)?;
436
437    println!("Object Tree (root directory: {}):", root_directory.va());
438    println!("=================================================");
439    enumerate_directory_object(&root_directory, 0)?;
440
441    println!("Processes:");
442    println!("=================================================");
443    enumerate_processes(&vmi)?;
444
445    Ok(())
446}
examples/basic-process-list.rs (line 64)
13fn main() -> Result<(), Box<dyn std::error::Error>> {
14    let domain_id = 'x: {
15        for name in &["win7", "win10", "win11", "ubuntu22"] {
16            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
17                break 'x domain_id;
18            }
19        }
20
21        panic!("Domain not found");
22    };
23
24    // Setup VMI.
25    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
26    let core = VmiCore::new(driver)?;
27
28    // Try to find the kernel information.
29    // This is necessary in order to load the profile.
30    let kernel_info = {
31        // Pause the VM to get consistent state.
32        let _pause_guard = core.pause_guard()?;
33
34        // Get the register state for the first vCPU.
35        let registers = core.registers(VcpuId(0))?;
36
37        // On AMD64 architecture, the kernel is usually found using the
38        // `MSR_LSTAR` register, which contains the address of the system call
39        // handler. This register is set by the operating system during boot
40        // and is left unchanged (unless some rootkits are involved).
41        //
42        // Therefore, we can take an arbitrary registers at any point in time
43        // (as long as the OS has booted and the page tables are set up) and
44        // use them to find the kernel.
45        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
46    };
47
48    // Load the profile.
49    // The profile contains offsets to kernel functions and data structures.
50    let isr = IsrCache::new("cache")?;
51    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
52    let profile = entry.profile()?;
53
54    // Create the VMI session.
55    tracing::info!("Creating VMI session");
56    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
57    let session = VmiSession::new(&core, &os);
58
59    // Pause the VM again to get consistent state.
60    let _pause_guard = session.pause_guard()?;
61
62    // Create a new `VmiState` with the current register.
63    let registers = session.registers(VcpuId(0))?;
64    let vmi = session.with_registers(&registers);
65
66    // Get the list of processes and print them.
67    for process in vmi.os().processes()? {
68        let process = process?;
69
70        println!(
71            "{} [{}] {} (root @ {})",
72            process.object()?,
73            process.id()?,
74            process.name()?,
75            process.translation_root()?
76        );
77    }
78
79    Ok(())
80}
examples/windows-breakpoint-manager.rs (line 88)
66    pub fn new(
67        session: &VmiSession<WindowsOs<Driver>>,
68        profile: &Profile,
69        terminate_flag: Arc<AtomicBool>,
70    ) -> Result<Self, VmiError> {
71        // Capture the current state of the vCPU and get the base address of
72        // the kernel.
73        //
74        // This base address is essential to correctly offset monitored
75        // functions.
76        //
77        // NOTE: `kernel_image_base` tries to find the kernel in the memory
78        //       with the help of the CPU registers. On AMD64 architecture,
79        //       the kernel image base is usually found using the `MSR_LSTAR`
80        //       register, which contains the address of the system call
81        //       handler. This register is set by the operating system during
82        //       boot and is left unchanged (unless some rootkits are involved).
83        //
84        //       Therefore, we can take an arbitrary registers at any point
85        //       in time (as long as the OS has booted and the page tables are
86        //       set up) and use them to find the kernel image base.
87        let registers = session.registers(VcpuId(0))?;
88        let vmi = session.with_registers(&registers);
89
90        let kernel_image_base = vmi.os().kernel_image_base()?;
91        tracing::info!(%kernel_image_base);
92
93        // Get the system process.
94        //
95        // The system process is the first process created by the kernel.
96        // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
97        // To monitor page table entries, we need to locate the translation root
98        // of this process.
99        let system_process = vmi.os().system_process()?;
100        tracing::info!(system_process = %system_process.object()?);
101
102        // Get the translation root of the system process.
103        // This is effectively "the CR3 of the kernel".
104        //
105        // The translation root is the root of the page table hierarchy (also
106        // known as the Directory Table Base or PML4).
107        let root = system_process.translation_root()?;
108        tracing::info!(%root);
109
110        // Load the symbols from the profile.
111        let symbols = Symbols::new(profile)?;
112
113        // Enable monitoring of the INT3 and singlestep events.
114        //
115        // INT3 is used to monitor the execution of specific functions.
116        // Singlestep is used to monitor the modifications of page table
117        // entries.
118        vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
119        vmi.monitor_enable(EventMonitor::Singlestep)?;
120
121        // Create a new view for the monitor.
122        // This view is used for monitoring function calls and memory accesses.
123        let view = vmi.create_view(MemoryAccess::RWX)?;
124        vmi.switch_to_view(view)?;
125
126        // Create a new breakpoint controller.
127        //
128        // The breakpoint controller is used to insert breakpoints for specific
129        // functions.
130        //
131        // From the guest's perspective, these breakpoints are "hidden", since
132        // the breakpoint controller will unset the read/write access to the
133        // physical memory page where the breakpoint is inserted, while keeping
134        // the execute access.
135        //
136        // This way, the guest will be able to execute the code, but attempts to
137        // read or write the memory will trigger the `memory_access` callback.
138        //
139        // When a vCPU tries to execute the breakpoint instruction:
140        // - an `interrupt` callback will be triggered
141        // - the breakpoint will be handled (e.g., log the function call)
142        // - a fast-singlestep[1] will be performed over the INT3 instruction
143        //
144        // When a vCPU tries to read from this page (e.g., a PatchGuard check):
145        // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
146        //   access type)
147        // - fast-singlestep[1] will be performed over the instruction that tried to
148        //   read the memory
149        //
150        // This way, the instruction will read the original memory content.
151        //
152        // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
153        //     to a different view, execute a single instruction, and then
154        //     switch back to the original view. In this case, the view is
155        //     switched to the `default_view` (which is unmodified).
156        let mut bpm = BreakpointManager::new();
157
158        // Create a new page table monitor.
159        //
160        // The page table monitor is used to monitor the page table entries of
161        // the hooked functions.
162        //
163        // More specifically, it is used to monitor the pages that the breakpoint
164        // was inserted into. This is necessary to handle the case when the
165        // page containing the breakpoint is paged out (and then paged in
166        // again).
167        //
168        // `PageTableMonitor` works by unsetting the write access to the page
169        // tables of the hooked functions. When the page is paged out, the
170        // `PRESENT` bit in the page table entry is unset and, conversely, when
171        // the page is paged in, the `PRESENT` bit is set again.
172        //
173        // When that happens:
174        // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
175        //   access type)
176        // - the callback will mark the page as dirty in the page table monitor
177        // - a singlestep will be performed over the instruction that tried to modify
178        //   the memory containing the page table entry
179        // - the `singlestep` handler will process the dirty page table entries and
180        //   inform the breakpoint controller to handle the changes
181        let mut ptm = PageTableMonitor::new();
182
183        // Pause the VM to avoid race conditions between inserting breakpoints
184        // and monitoring page table entries. The VM resumes when the pause
185        // guard is dropped.
186        let _pause_guard = vmi.pause_guard()?;
187
188        // Insert breakpoint for the `NtCreateFile` function.
189        let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
190        let cx_NtCreateFile = (va_NtCreateFile, root);
191        let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
192            .global()
193            .with_tag("NtCreateFile");
194        bpm.insert(&vmi, bp_NtCreateFile)?;
195        ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
196        tracing::info!(%va_NtCreateFile);
197
198        // Insert breakpoint for the `NtWriteFile` function.
199        let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
200        let cx_NtWriteFile = (va_NtWriteFile, root);
201        let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
202            .global()
203            .with_tag("NtWriteFile");
204        bpm.insert(&vmi, bp_NtWriteFile)?;
205        ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
206        tracing::info!(%va_NtWriteFile);
207
208        // Insert breakpoint for the `PspInsertProcess` function.
209        let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
210        let cx_PspInsertProcess = (va_PspInsertProcess, root);
211        let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
212            .global()
213            .with_tag("PspInsertProcess");
214        bpm.insert(&vmi, bp_PspInsertProcess)?;
215        ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
216
217        // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
218        let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
219        let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
220        let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
221            .global()
222            .with_tag("MmCleanProcessAddressSpace");
223        bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
224        ptm.monitor(
225            &vmi,
226            cx_MmCleanProcessAddressSpace,
227            view,
228            "MmCleanProcessAddressSpace",
229        )?;
230
231        Ok(Self {
232            terminate_flag,
233            view,
234            bpm,
235            ptm,
236        })
237    }
Source

pub fn without_os(&self) -> VmiSession<'a, NoOS<<Os as VmiOs>::Driver>>

Available on crate features utils and injector only.

Creates a new VMI session without an OS-specific implementation.

Source

pub fn core(&self) -> &'a VmiCore<<Os as VmiOs>::Driver>

Available on crate features utils and injector only.

Returns the VMI core.

Source

pub fn underlying_os(&self) -> &'a Os

Available on crate features utils and injector only.

Returns the underlying OS-specific implementation.

Source§

impl<'a, Os> VmiSession<'a, Os>
where Os: VmiOs, <Os as VmiOs>::Driver: VmiEventControl,

Source

pub fn wait_for_event( &self, timeout: Duration, handler: &mut impl VmiHandler<Os>, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Waits for an event to occur and processes it with the provided handler.

This method blocks until an event occurs or the specified timeout is reached. When an event occurs, it is passed to the provided callback function for processing.

Source§

impl<'a, Os> VmiSession<'a, Os>

Source

pub fn handle<Handler>( &self, handler_factory: impl FnOnce(&VmiSession<'_, Os>) -> Result<Handler, VmiError>, ) -> Result<Option<<Handler as VmiHandler<Os>>::Output>, VmiError>
where Handler: VmiHandler<Os>,

Available on crate features utils and injector only.

Enters the main event handling loop that processes VMI events until finished.

Examples found in repository?
examples/windows-recipe-messagebox.rs (lines 94-103)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let (session, _profile) = common::create_vmi_session()?;
67
68    let explorer_pid = {
69        // This block is used to drop the pause guard after the PID is found.
70        // If the `session.handle()` would be called with the VM paused, no
71        // events would be triggered.
72        let _pause_guard = session.pause_guard()?;
73
74        let registers = session.registers(VcpuId(0))?;
75        let vmi = session.with_registers(&registers);
76
77        let explorer = match common::find_process(&vmi, "explorer.exe")? {
78            Some(explorer) => explorer,
79            None => {
80                tracing::error!("explorer.exe not found");
81                return Ok(());
82            }
83        };
84
85        tracing::info!(
86            pid = %explorer.id()?,
87            object = %explorer.object()?,
88            "found explorer.exe"
89        );
90
91        explorer.id()?
92    };
93
94    session.handle(|session| {
95        UserInjectorHandler::new(
96            session,
97            recipe_factory(MessageBox::new(
98                "Hello, World!",
99                "This is a message box from the VMI!",
100            )),
101        )?
102        .with_pid(explorer_pid)
103    })?;
104
105    Ok(())
106}
More examples
Hide additional examples
examples/windows-recipe-writefile.rs (lines 232-241)
203fn main() -> Result<(), Box<dyn std::error::Error>> {
204    let (session, _profile) = common::create_vmi_session()?;
205
206    let explorer_pid = {
207        // This block is used to drop the pause guard after the PID is found.
208        // If the `session.handle()` would be called with the VM paused, no
209        // events would be triggered.
210        let _pause_guard = session.pause_guard()?;
211
212        let registers = session.registers(VcpuId(0))?;
213        let vmi = session.with_registers(&registers);
214
215        let explorer = match common::find_process(&vmi, "explorer.exe")? {
216            Some(explorer) => explorer,
217            None => {
218                tracing::error!("explorer.exe not found");
219                return Ok(());
220            }
221        };
222
223        tracing::info!(
224            pid = %explorer.id()?,
225            object = %explorer.object()?,
226            "found explorer.exe"
227        );
228
229        explorer.id()?
230    };
231
232    session.handle(|session| {
233        UserInjectorHandler::new(
234            session,
235            recipe_factory(GuestFile::new(
236                "C:\\Users\\John\\Desktop\\test.txt",
237                "Hello, World!".as_bytes(),
238            )),
239        )?
240        .with_pid(explorer_pid)
241    })?;
242
243    Ok(())
244}
examples/windows-recipe-writefile-advanced.rs (lines 335-344)
301fn main() -> Result<(), Box<dyn std::error::Error>> {
302    let (session, _profile) = common::create_vmi_session()?;
303
304    let explorer_pid = {
305        // This block is used to drop the pause guard after the PID is found.
306        // If the `session.handle()` would be called with the VM paused, no
307        // events would be triggered.
308        let _pause_guard = session.pause_guard()?;
309
310        let registers = session.registers(VcpuId(0))?;
311        let vmi = session.with_registers(&registers);
312
313        let explorer = match common::find_process(&vmi, "explorer.exe")? {
314            Some(explorer) => explorer,
315            None => {
316                tracing::error!("explorer.exe not found");
317                return Ok(());
318            }
319        };
320
321        tracing::info!(
322            pid = %explorer.id()?,
323            object = %explorer.object()?,
324            "found explorer.exe"
325        );
326
327        explorer.id()?
328    };
329
330    let mut content = Vec::new();
331    for c in 'A'..='Z' {
332        content.extend((0..2049).map(|_| c as u8).collect::<Vec<_>>());
333    }
334
335    session.handle(|session| {
336        UserInjectorHandler::new(
337            session,
338            recipe_factory(GuestFile::new(
339                "C:\\Users\\John\\Desktop\\test.txt",
340                content,
341            )),
342        )?
343        .with_pid(explorer_pid)
344    })?;
345
346    Ok(())
347}
examples/windows-breakpoint-manager.rs (line 571)
524fn main() -> Result<(), Box<dyn std::error::Error>> {
525    tracing_subscriber::fmt()
526        .with_max_level(tracing::Level::DEBUG)
527        .init();
528
529    let domain_id = 'x: {
530        for name in &["win7", "win10", "win11", "ubuntu22"] {
531            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
532                break 'x domain_id;
533            }
534        }
535
536        panic!("Domain not found");
537    };
538
539    tracing::debug!(?domain_id);
540
541    // Setup VMI.
542    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
543    let core = VmiCore::new(driver)?;
544
545    // Try to find the kernel information.
546    // This is necessary in order to load the profile.
547    let kernel_info = {
548        let _pause_guard = core.pause_guard()?;
549        let regs = core.registers(0.into())?;
550
551        WindowsOs::find_kernel(&core, &regs)?.expect("kernel information")
552    };
553
554    // Load the profile.
555    // The profile contains offsets to kernel functions and data structures.
556    let isr = IsrCache::new("cache")?;
557    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
558    let profile = entry.profile()?;
559
560    // Create the VMI session.
561    tracing::info!("Creating VMI session");
562    let terminate_flag = Arc::new(AtomicBool::new(false));
563    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
564    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
565    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
566    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
567
568    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
569    let session = VmiSession::new(&core, &os);
570
571    session.handle(|session| Monitor::new(session, &profile, terminate_flag))?;
572
573    Ok(())
574}
Source

pub fn handle_with_timeout<Handler>( &self, timeout: Duration, handler_factory: impl FnOnce(&VmiSession<'_, Os>) -> Result<Handler, VmiError>, ) -> Result<Option<<Handler as VmiHandler<Os>>::Output>, VmiError>
where Handler: VmiHandler<Os>,

Available on crate features utils and injector only.

Enters the main event handling loop that processes VMI events until finished, with a timeout for each event.

Methods from Deref<Target = VmiCore<<Os as VmiOs>::Driver>>§

Source

pub fn driver(&self) -> &Driver

Available on crate features utils and injector only.

Returns the driver used by this VmiCore instance.

Source

pub fn info(&self) -> Result<VmiInfo, VmiError>

Available on crate features utils and injector only.

Retrieves information about the virtual machine.

Examples found in repository?
examples/basic.rs (line 26)
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let domain_id = 'x: {
11        for name in &["win7", "win10", "win11", "ubuntu22"] {
12            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
13                break 'x domain_id;
14            }
15        }
16
17        panic!("Domain not found");
18    };
19
20    // Setup VMI.
21    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
22    let vmi = VmiCore::new(driver)?;
23
24    // Get the interrupt descriptor table for each vCPU and print it.
25    let _pause_guard = vmi.pause_guard()?;
26    let info = vmi.info()?;
27    for vcpu_id in 0..info.vcpus {
28        let registers = vmi.registers(VcpuId(vcpu_id))?;
29        let idt = Amd64::interrupt_descriptor_table(&vmi, &registers)?;
30
31        println!("IDT[{vcpu_id}]: {idt:#?}");
32    }
33
34    Ok(())
35}
Source

pub fn flush_gfn_cache_entry(&self, gfn: Gfn) -> Option<VmiMappedPage>

Available on crate features utils and injector only.

Removes a specific entry from the GFN cache.

Returns the removed entry if it was present. This is useful for invalidating cached data that might have become stale.

Source

pub fn flush_gfn_cache(&self)

Available on crate features utils and injector only.

Clears the entire GFN cache.

Source

pub fn flush_v2p_cache_entry(&self, ctx: AccessContext) -> Option<Pa>

Available on crate features utils and injector only.

Removes a specific entry from the V2P cache.

Returns the removed entry if it was present. This can be used to invalidate cached translations that may have become stale due to changes in the guest’s memory mapping.

Source

pub fn flush_v2p_cache(&self)

Available on crate features utils and injector only.

Clears the entire V2P cache.

This method is crucial for maintaining consistency when handling events. The guest operating system can modify page tables or other structures related to address translation between events. Using stale translations can lead to incorrect memory access and unexpected behavior. It is recommended to call this method at the beginning of each VmiHandler::handle_event loop to ensure that you are working with the most up-to-date address mappings.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 514)
512    fn handle_event(&mut self, vmi: VmiContext<WindowsOs<Driver>>) -> VmiEventResponse<Amd64> {
513        // Flush the V2P cache on every event to avoid stale translations.
514        vmi.flush_v2p_cache();
515
516        self.dispatch(&vmi).expect("dispatch")
517    }
Source

pub fn read_string_length_limit(&self) -> Option<usize>

Available on crate features utils and injector only.

Returns the current limit on the length of strings read by the read_string methods.

Source

pub fn set_read_string_length_limit(&self, limit: usize)

Available on crate features utils and injector only.

Sets a limit on the length of strings read by the read_string methods.

This method allows you to set a maximum length (in bytes) for strings read from the virtual machine’s memory. When set, string reading operations will truncate their results to this limit. This can be useful for preventing excessively long string reads, which might impact performance or consume too much memory.

If the limit is reached during a string read operation, the resulting string will be truncated to the specified length.

To remove the limit, call this method with None.

Source

pub fn read( &self, ctx: impl Into<AccessContext>, buffer: &mut [u8], ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Reads memory from the virtual machine.

Source

pub fn read_u8(&self, ctx: impl Into<AccessContext>) -> Result<u8, VmiError>

Available on crate features utils and injector only.

Reads a single byte from the virtual machine.

Source

pub fn read_u16(&self, ctx: impl Into<AccessContext>) -> Result<u16, VmiError>

Available on crate features utils and injector only.

Reads a 16-bit unsigned integer from the virtual machine.

Source

pub fn read_u32(&self, ctx: impl Into<AccessContext>) -> Result<u32, VmiError>

Available on crate features utils and injector only.

Reads a 32-bit unsigned integer from the virtual machine.

Source

pub fn read_u64(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads a 64-bit unsigned integer from the virtual machine.

Source

pub fn read_uint( &self, ctx: impl Into<AccessContext>, size: usize, ) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads an unsigned integer of the specified size from the virtual machine.

This method reads an unsigned integer of the specified size (in bytes) from the virtual machine. Note that the size must be 1, 2, 4, or 8.

The result is returned as a u64 to accommodate the widest possible integer size.

Source

pub fn read_field( &self, ctx: impl Into<AccessContext>, field: &Field, ) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads a field of a structure from the virtual machine.

This method reads a field from the virtual machine. The field is defined by the provided Field structure, which specifies the offset and size of the field within the memory region.

The result is returned as a u64 to accommodate the widest possible integer size.

Source

pub fn read_address( &self, ctx: impl Into<AccessContext>, address_width: usize, ) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads an address-sized unsigned integer from the virtual machine.

This method reads an address of the specified width (in bytes) from the given access context. It’s useful when dealing with architectures that can operate in different address modes.

Source

pub fn read_address32( &self, ctx: impl Into<AccessContext>, ) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads a 32-bit address from the virtual machine.

Source

pub fn read_address64( &self, ctx: impl Into<AccessContext>, ) -> Result<u64, VmiError>

Available on crate features utils and injector only.

Reads a 64-bit address from the virtual machine.

Source

pub fn read_va( &self, ctx: impl Into<AccessContext>, address_width: usize, ) -> Result<Va, VmiError>

Available on crate features utils and injector only.

Reads a virtual address from the virtual machine.

Source

pub fn read_va32(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError>

Available on crate features utils and injector only.

Reads a 32-bit virtual address from the virtual machine.

Source

pub fn read_va64(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError>

Available on crate features utils and injector only.

Reads a 64-bit virtual address from the virtual machine.

Source

pub fn read_string_bytes_limited( &self, ctx: impl Into<AccessContext>, limit: usize, ) -> Result<Vec<u8>, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated string of bytes from the virtual machine with a specified limit.

Source

pub fn read_string_bytes( &self, ctx: impl Into<AccessContext>, ) -> Result<Vec<u8>, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated string of bytes from the virtual machine.

Source

pub fn read_string_utf16_bytes_limited( &self, ctx: impl Into<AccessContext>, limit: usize, ) -> Result<Vec<u16>, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated wide string (UTF-16) from the virtual machine with a specified limit.

Source

pub fn read_string_utf16_bytes( &self, ctx: impl Into<AccessContext>, ) -> Result<Vec<u16>, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated wide string (UTF-16) from the virtual machine.

Source

pub fn read_string_limited( &self, ctx: impl Into<AccessContext>, limit: usize, ) -> Result<String, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated string from the virtual machine with a specified limit.

Source

pub fn read_string( &self, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated string from the virtual machine.

Source

pub fn read_string_utf16_limited( &self, ctx: impl Into<AccessContext>, limit: usize, ) -> Result<String, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated wide string (UTF-16) from the virtual machine with a specified limit.

Source

pub fn read_string_utf16( &self, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Available on crate features utils and injector only.

Reads a null-terminated wide string (UTF-16) from the virtual machine.

Source

pub fn read_struct<T>( &self, ctx: impl Into<AccessContext>, ) -> Result<T, VmiError>
where T: FromBytes + IntoBytes,

Available on crate features utils and injector only.

Reads a struct from the virtual machine.

Source

pub fn translate_address( &self, ctx: impl Into<AddressContext>, ) -> Result<Pa, VmiError>

Available on crate features utils and injector only.

Translates a virtual address to a physical address.

Source

pub fn translate_access_context( &self, ctx: AccessContext, ) -> Result<Pa, VmiError>

Available on crate features utils and injector only.

Translates an access context to a physical address.

Source

pub fn read_page(&self, gfn: Gfn) -> Result<VmiMappedPage, VmiError>

Available on crate features utils and injector only.

Reads a page of memory from the virtual machine.

Source

pub fn write( &self, ctx: impl Into<AccessContext>, buffer: &[u8], ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Writes memory to the virtual machine.

Source

pub fn write_u8( &self, ctx: impl Into<AccessContext>, value: u8, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Writes a single byte to the virtual machine.

Source

pub fn write_u16( &self, ctx: impl Into<AccessContext>, value: u16, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Writes a 16-bit unsigned integer to the virtual machine.

Source

pub fn write_u32( &self, ctx: impl Into<AccessContext>, value: u32, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Writes a 32-bit unsigned integer to the virtual machine.

Source

pub fn write_u64( &self, ctx: impl Into<AccessContext>, value: u64, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Writes a 64-bit unsigned integer to the virtual machine.

Source

pub fn write_struct<T>( &self, ctx: impl Into<AccessContext>, value: T, ) -> Result<(), VmiError>
where T: IntoBytes + Immutable,

Available on crate features utils and injector only.

Writes a struct to the virtual machine.

Source

pub fn memory_access( &self, gfn: Gfn, view: View, ) -> Result<MemoryAccess, VmiError>

Available on crate features utils and injector only.

Retrieves the memory access permissions for a specific guest frame number (GFN).

The returned MemoryAccess indicates the current read, write, and execute permissions for the specified memory page in the given view.

Source

pub fn set_memory_access( &self, gfn: Gfn, view: View, access: MemoryAccess, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Sets the memory access permissions for a specific guest frame number (GFN).

This method allows you to modify the read, write, and execute permissions for a given memory page in the specified view.

Source

pub fn set_memory_access_with_options( &self, gfn: Gfn, view: View, access: MemoryAccess, options: MemoryAccessOptions, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Sets the memory access permissions for a specific guest frame number (GFN) with additional options.

In addition to the basic read, write, and execute permissions, this method allows you to specify additional options for the memory access.

Source

pub fn registers( &self, vcpu: VcpuId, ) -> Result<<<Driver as VmiDriver>::Architecture as Architecture>::Registers, VmiError>

Available on crate features utils and injector only.

Retrieves the current state of CPU registers for a specified virtual CPU.

This method allows you to access the current values of CPU registers, which is crucial for understanding the state of the virtual machine at a given point in time.

§Notes

The exact structure and content of the returned registers depend on the specific architecture of the VM being introspected. Refer to the documentation of your Architecture implementation for details on how to interpret the register values.

Examples found in repository?
examples/basic.rs (line 28)
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let domain_id = 'x: {
11        for name in &["win7", "win10", "win11", "ubuntu22"] {
12            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
13                break 'x domain_id;
14            }
15        }
16
17        panic!("Domain not found");
18    };
19
20    // Setup VMI.
21    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
22    let vmi = VmiCore::new(driver)?;
23
24    // Get the interrupt descriptor table for each vCPU and print it.
25    let _pause_guard = vmi.pause_guard()?;
26    let info = vmi.info()?;
27    for vcpu_id in 0..info.vcpus {
28        let registers = vmi.registers(VcpuId(vcpu_id))?;
29        let idt = Amd64::interrupt_descriptor_table(&vmi, &registers)?;
30
31        println!("IDT[{vcpu_id}]: {idt:#?}");
32    }
33
34    Ok(())
35}
More examples
Hide additional examples
examples/windows-recipe-messagebox.rs (line 74)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let (session, _profile) = common::create_vmi_session()?;
67
68    let explorer_pid = {
69        // This block is used to drop the pause guard after the PID is found.
70        // If the `session.handle()` would be called with the VM paused, no
71        // events would be triggered.
72        let _pause_guard = session.pause_guard()?;
73
74        let registers = session.registers(VcpuId(0))?;
75        let vmi = session.with_registers(&registers);
76
77        let explorer = match common::find_process(&vmi, "explorer.exe")? {
78            Some(explorer) => explorer,
79            None => {
80                tracing::error!("explorer.exe not found");
81                return Ok(());
82            }
83        };
84
85        tracing::info!(
86            pid = %explorer.id()?,
87            object = %explorer.object()?,
88            "found explorer.exe"
89        );
90
91        explorer.id()?
92    };
93
94    session.handle(|session| {
95        UserInjectorHandler::new(
96            session,
97            recipe_factory(MessageBox::new(
98                "Hello, World!",
99                "This is a message box from the VMI!",
100            )),
101        )?
102        .with_pid(explorer_pid)
103    })?;
104
105    Ok(())
106}
examples/windows-recipe-writefile.rs (line 212)
203fn main() -> Result<(), Box<dyn std::error::Error>> {
204    let (session, _profile) = common::create_vmi_session()?;
205
206    let explorer_pid = {
207        // This block is used to drop the pause guard after the PID is found.
208        // If the `session.handle()` would be called with the VM paused, no
209        // events would be triggered.
210        let _pause_guard = session.pause_guard()?;
211
212        let registers = session.registers(VcpuId(0))?;
213        let vmi = session.with_registers(&registers);
214
215        let explorer = match common::find_process(&vmi, "explorer.exe")? {
216            Some(explorer) => explorer,
217            None => {
218                tracing::error!("explorer.exe not found");
219                return Ok(());
220            }
221        };
222
223        tracing::info!(
224            pid = %explorer.id()?,
225            object = %explorer.object()?,
226            "found explorer.exe"
227        );
228
229        explorer.id()?
230    };
231
232    session.handle(|session| {
233        UserInjectorHandler::new(
234            session,
235            recipe_factory(GuestFile::new(
236                "C:\\Users\\John\\Desktop\\test.txt",
237                "Hello, World!".as_bytes(),
238            )),
239        )?
240        .with_pid(explorer_pid)
241    })?;
242
243    Ok(())
244}
examples/windows-recipe-writefile-advanced.rs (line 310)
301fn main() -> Result<(), Box<dyn std::error::Error>> {
302    let (session, _profile) = common::create_vmi_session()?;
303
304    let explorer_pid = {
305        // This block is used to drop the pause guard after the PID is found.
306        // If the `session.handle()` would be called with the VM paused, no
307        // events would be triggered.
308        let _pause_guard = session.pause_guard()?;
309
310        let registers = session.registers(VcpuId(0))?;
311        let vmi = session.with_registers(&registers);
312
313        let explorer = match common::find_process(&vmi, "explorer.exe")? {
314            Some(explorer) => explorer,
315            None => {
316                tracing::error!("explorer.exe not found");
317                return Ok(());
318            }
319        };
320
321        tracing::info!(
322            pid = %explorer.id()?,
323            object = %explorer.object()?,
324            "found explorer.exe"
325        );
326
327        explorer.id()?
328    };
329
330    let mut content = Vec::new();
331    for c in 'A'..='Z' {
332        content.extend((0..2049).map(|_| c as u8).collect::<Vec<_>>());
333    }
334
335    session.handle(|session| {
336        UserInjectorHandler::new(
337            session,
338            recipe_factory(GuestFile::new(
339                "C:\\Users\\John\\Desktop\\test.txt",
340                content,
341            )),
342        )?
343        .with_pid(explorer_pid)
344    })?;
345
346    Ok(())
347}
examples/windows-dump.rs (line 412)
393fn main() -> Result<(), Box<dyn std::error::Error>> {
394    tracing_subscriber::fmt()
395        .with_max_level(tracing::Level::DEBUG)
396        .with_ansi(false)
397        .init();
398
399    // First argument is the path to the dump file.
400    let args = std::env::args().collect::<Vec<_>>();
401    if args.len() != 2 {
402        eprintln!("Usage: {} <dump-file>", args[0]);
403        std::process::exit(1);
404    }
405
406    let dump_file = &args[1];
407
408    // Setup VMI.
409    let driver = Driver::new(dump_file)?;
410    let core = VmiCore::new(driver)?;
411
412    let registers = core.registers(VcpuId(0))?;
413
414    // Try to find the kernel information.
415    // This is necessary in order to load the profile.
416    let kernel_info = WindowsOs::find_kernel(&core, &registers)?.expect("kernel information");
417    tracing::info!(?kernel_info, "Kernel information");
418
419    // Load the profile.
420    // The profile contains offsets to kernel functions and data structures.
421    let isr = IsrCache::new("cache")?;
422    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
423    let profile = entry.profile()?;
424
425    // Create the VMI session.
426    tracing::info!("Creating VMI session");
427    let os = WindowsOs::<Driver>::with_kernel_base(&profile, kernel_info.base_address)?;
428    let session = VmiSession::new(&core, &os);
429
430    let vmi = session.with_registers(&registers);
431    let root_directory = vmi.os().object_root_directory()?;
432
433    println!("Kernel Modules:");
434    println!("=================================================");
435    enumerate_kernel_modules(&vmi)?;
436
437    println!("Object Tree (root directory: {}):", root_directory.va());
438    println!("=================================================");
439    enumerate_directory_object(&root_directory, 0)?;
440
441    println!("Processes:");
442    println!("=================================================");
443    enumerate_processes(&vmi)?;
444
445    Ok(())
446}
examples/common/mod.rs (line 44)
15pub fn create_vmi_session() -> Result<Session, Box<dyn std::error::Error>> {
16    tracing_subscriber::fmt()
17        .with_max_level(tracing::Level::DEBUG)
18        .with_target(false)
19        .init();
20
21    let domain_id = 'x: {
22        for name in &["win7", "win10", "win11", "ubuntu22"] {
23            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
24                break 'x domain_id;
25            }
26        }
27
28        panic!("Domain not found");
29    };
30
31    tracing::debug!(?domain_id);
32
33    // Setup VMI.
34    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
35    let core = VmiCore::new(driver)?;
36
37    // Try to find the kernel information.
38    // This is necessary in order to load the profile.
39    let kernel_info = {
40        // Pause the vCPU to get consistent state.
41        let _pause_guard = core.pause_guard()?;
42
43        // Get the register state for the first vCPU.
44        let registers = core.registers(VcpuId(0))?;
45
46        // On AMD64 architecture, the kernel is usually found using the
47        // `MSR_LSTAR` register, which contains the address of the system call
48        // handler. This register is set by the operating system during boot
49        // and is left unchanged (unless some rootkits are involved).
50        //
51        // Therefore, we can take an arbitrary registers at any point in time
52        // (as long as the OS has booted and the page tables are set up) and
53        // use them to find the kernel.
54        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
55    };
56
57    // Load the profile.
58    // The profile contains offsets to kernel functions and data structures.
59    let isr = IsrCache::new("cache")?;
60    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
61    let entry = Box::leak(Box::new(entry));
62    let profile = entry.profile()?;
63
64    // Create the VMI session.
65    tracing::info!("Creating VMI session");
66    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
67
68    // Please don't do this in production code.
69    // This is only done for the sake of the example.
70    let core = Box::leak(Box::new(core));
71    let os = Box::leak(Box::new(os));
72
73    Ok((VmiSession::new(core, os), profile))
74}
Source

pub fn set_registers( &self, vcpu: VcpuId, registers: <<Driver as VmiDriver>::Architecture as Architecture>::Registers, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Sets the registers of a virtual CPU.

Source

pub fn default_view(&self) -> View

Available on crate features utils and injector only.

Returns the default view for the virtual machine.

The default view typically represents the normal, unmodified state of the VM’s memory.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 261)
240    fn memory_access(
241        &mut self,
242        vmi: &VmiContext<WindowsOs<Driver>>,
243    ) -> Result<VmiEventResponse<Amd64>, VmiError> {
244        let memory_access = vmi.event().reason().as_memory_access();
245
246        tracing::trace!(
247            pa = %memory_access.pa,
248            va = %memory_access.va,
249            access = %memory_access.access,
250        );
251
252        if memory_access.access.contains(MemoryAccess::W) {
253            // It is assumed that a write memory access event is caused by a
254            // page table modification.
255            //
256            // The page table entry is marked as dirty in the page table monitor
257            // and a singlestep is performed to process the dirty entries.
258            self.ptm
259                .mark_dirty_entry(memory_access.pa, self.view, vmi.event().vcpu_id());
260
261            Ok(VmiEventResponse::singlestep().with_view(vmi.default_view()))
262        }
263        else if memory_access.access.contains(MemoryAccess::R) {
264            // When the guest tries to read from the memory, a fast-singlestep
265            // is performed over the instruction that tried to read the memory.
266            // This is done to allow the instruction to read the original memory
267            // content.
268            Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
269        }
270        else {
271            panic!("Unhandled memory access: {memory_access:?}");
272        }
273    }
274
275    #[tracing::instrument(skip_all, fields(pid, process))]
276    fn interrupt(
277        &mut self,
278        vmi: &VmiContext<WindowsOs<Driver>>,
279    ) -> Result<VmiEventResponse<Amd64>, VmiError> {
280        let tag = match self.bpm.get_by_event(vmi.event(), ()) {
281            Some(breakpoints) => {
282                // Breakpoints can have multiple tags, but we have set only one
283                // tag for each breakpoint.
284                let first_breakpoint = breakpoints.into_iter().next().expect("breakpoint");
285                first_breakpoint.tag()
286            }
287            None => {
288                if BreakpointController::is_breakpoint(vmi, vmi.event())? {
289                    // This breakpoint was not set by us. Reinject it.
290                    tracing::warn!("Unknown breakpoint, reinjecting");
291                    return Ok(VmiEventResponse::reinject_interrupt());
292                }
293                else {
294                    // We have received a breakpoint event, but there is no
295                    // breakpoint instruction at the current memory location.
296                    // This can happen if the event was triggered by a breakpoint
297                    // we just removed.
298                    tracing::warn!("Ignoring old breakpoint event");
299                    return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
300                }
301            }
302        };
303
304        let process = vmi.os().current_process()?;
305        let process_id = process.id()?;
306        let process_name = process.name()?;
307        tracing::Span::current()
308            .record("pid", process_id.0)
309            .record("process", process_name);
310
311        match tag {
312            "NtCreateFile" => self.NtCreateFile(vmi)?,
313            "NtWriteFile" => self.NtWriteFile(vmi)?,
314            "PspInsertProcess" => self.PspInsertProcess(vmi)?,
315            "MmCleanProcessAddressSpace" => self.MmCleanProcessAddressSpace(vmi)?,
316            _ => panic!("Unhandled tag: {tag}"),
317        }
318
319        Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
320    }
Source

pub fn create_view( &self, default_access: MemoryAccess, ) -> Result<View, VmiError>

Available on crate features utils and injector only.

Creates a new view with the specified default access permissions.

Views allow for creating different perspectives of the VM’s memory, which can be useful for analysis or isolation purposes. The default access permissions apply to memory pages not explicitly modified within this view.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 123)
66    pub fn new(
67        session: &VmiSession<WindowsOs<Driver>>,
68        profile: &Profile,
69        terminate_flag: Arc<AtomicBool>,
70    ) -> Result<Self, VmiError> {
71        // Capture the current state of the vCPU and get the base address of
72        // the kernel.
73        //
74        // This base address is essential to correctly offset monitored
75        // functions.
76        //
77        // NOTE: `kernel_image_base` tries to find the kernel in the memory
78        //       with the help of the CPU registers. On AMD64 architecture,
79        //       the kernel image base is usually found using the `MSR_LSTAR`
80        //       register, which contains the address of the system call
81        //       handler. This register is set by the operating system during
82        //       boot and is left unchanged (unless some rootkits are involved).
83        //
84        //       Therefore, we can take an arbitrary registers at any point
85        //       in time (as long as the OS has booted and the page tables are
86        //       set up) and use them to find the kernel image base.
87        let registers = session.registers(VcpuId(0))?;
88        let vmi = session.with_registers(&registers);
89
90        let kernel_image_base = vmi.os().kernel_image_base()?;
91        tracing::info!(%kernel_image_base);
92
93        // Get the system process.
94        //
95        // The system process is the first process created by the kernel.
96        // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
97        // To monitor page table entries, we need to locate the translation root
98        // of this process.
99        let system_process = vmi.os().system_process()?;
100        tracing::info!(system_process = %system_process.object()?);
101
102        // Get the translation root of the system process.
103        // This is effectively "the CR3 of the kernel".
104        //
105        // The translation root is the root of the page table hierarchy (also
106        // known as the Directory Table Base or PML4).
107        let root = system_process.translation_root()?;
108        tracing::info!(%root);
109
110        // Load the symbols from the profile.
111        let symbols = Symbols::new(profile)?;
112
113        // Enable monitoring of the INT3 and singlestep events.
114        //
115        // INT3 is used to monitor the execution of specific functions.
116        // Singlestep is used to monitor the modifications of page table
117        // entries.
118        vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
119        vmi.monitor_enable(EventMonitor::Singlestep)?;
120
121        // Create a new view for the monitor.
122        // This view is used for monitoring function calls and memory accesses.
123        let view = vmi.create_view(MemoryAccess::RWX)?;
124        vmi.switch_to_view(view)?;
125
126        // Create a new breakpoint controller.
127        //
128        // The breakpoint controller is used to insert breakpoints for specific
129        // functions.
130        //
131        // From the guest's perspective, these breakpoints are "hidden", since
132        // the breakpoint controller will unset the read/write access to the
133        // physical memory page where the breakpoint is inserted, while keeping
134        // the execute access.
135        //
136        // This way, the guest will be able to execute the code, but attempts to
137        // read or write the memory will trigger the `memory_access` callback.
138        //
139        // When a vCPU tries to execute the breakpoint instruction:
140        // - an `interrupt` callback will be triggered
141        // - the breakpoint will be handled (e.g., log the function call)
142        // - a fast-singlestep[1] will be performed over the INT3 instruction
143        //
144        // When a vCPU tries to read from this page (e.g., a PatchGuard check):
145        // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
146        //   access type)
147        // - fast-singlestep[1] will be performed over the instruction that tried to
148        //   read the memory
149        //
150        // This way, the instruction will read the original memory content.
151        //
152        // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
153        //     to a different view, execute a single instruction, and then
154        //     switch back to the original view. In this case, the view is
155        //     switched to the `default_view` (which is unmodified).
156        let mut bpm = BreakpointManager::new();
157
158        // Create a new page table monitor.
159        //
160        // The page table monitor is used to monitor the page table entries of
161        // the hooked functions.
162        //
163        // More specifically, it is used to monitor the pages that the breakpoint
164        // was inserted into. This is necessary to handle the case when the
165        // page containing the breakpoint is paged out (and then paged in
166        // again).
167        //
168        // `PageTableMonitor` works by unsetting the write access to the page
169        // tables of the hooked functions. When the page is paged out, the
170        // `PRESENT` bit in the page table entry is unset and, conversely, when
171        // the page is paged in, the `PRESENT` bit is set again.
172        //
173        // When that happens:
174        // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
175        //   access type)
176        // - the callback will mark the page as dirty in the page table monitor
177        // - a singlestep will be performed over the instruction that tried to modify
178        //   the memory containing the page table entry
179        // - the `singlestep` handler will process the dirty page table entries and
180        //   inform the breakpoint controller to handle the changes
181        let mut ptm = PageTableMonitor::new();
182
183        // Pause the VM to avoid race conditions between inserting breakpoints
184        // and monitoring page table entries. The VM resumes when the pause
185        // guard is dropped.
186        let _pause_guard = vmi.pause_guard()?;
187
188        // Insert breakpoint for the `NtCreateFile` function.
189        let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
190        let cx_NtCreateFile = (va_NtCreateFile, root);
191        let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
192            .global()
193            .with_tag("NtCreateFile");
194        bpm.insert(&vmi, bp_NtCreateFile)?;
195        ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
196        tracing::info!(%va_NtCreateFile);
197
198        // Insert breakpoint for the `NtWriteFile` function.
199        let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
200        let cx_NtWriteFile = (va_NtWriteFile, root);
201        let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
202            .global()
203            .with_tag("NtWriteFile");
204        bpm.insert(&vmi, bp_NtWriteFile)?;
205        ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
206        tracing::info!(%va_NtWriteFile);
207
208        // Insert breakpoint for the `PspInsertProcess` function.
209        let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
210        let cx_PspInsertProcess = (va_PspInsertProcess, root);
211        let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
212            .global()
213            .with_tag("PspInsertProcess");
214        bpm.insert(&vmi, bp_PspInsertProcess)?;
215        ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
216
217        // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
218        let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
219        let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
220        let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
221            .global()
222            .with_tag("MmCleanProcessAddressSpace");
223        bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
224        ptm.monitor(
225            &vmi,
226            cx_MmCleanProcessAddressSpace,
227            view,
228            "MmCleanProcessAddressSpace",
229        )?;
230
231        Ok(Self {
232            terminate_flag,
233            view,
234            bpm,
235            ptm,
236        })
237    }
Source

pub fn destroy_view(&self, view: View) -> Result<(), VmiError>

Available on crate features utils and injector only.

Destroys a previously created view.

This method removes a view and frees associated resources. It should be called when a view is no longer needed to prevent resource leaks.

Source

pub fn switch_to_view(&self, view: View) -> Result<(), VmiError>

Available on crate features utils and injector only.

Switches to a different view for all virtual CPUs.

This method changes the current active view for all vCPUs, affecting subsequent memory operations across the entire VM. It allows for quick transitions between different memory perspectives globally.

Note the difference between this method and VmiEventResponse::with_view():

  • switch_to_view() changes the view for all vCPUs immediately.
  • VmiEventResponse::with_view() sets the view only for the specific vCPU that received the event, and the change is applied when the event handler returns.

Use switch_to_view() for global view changes, and VmiEventResponse::with_view() for targeted, event-specific view modifications on individual vCPUs.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 124)
66    pub fn new(
67        session: &VmiSession<WindowsOs<Driver>>,
68        profile: &Profile,
69        terminate_flag: Arc<AtomicBool>,
70    ) -> Result<Self, VmiError> {
71        // Capture the current state of the vCPU and get the base address of
72        // the kernel.
73        //
74        // This base address is essential to correctly offset monitored
75        // functions.
76        //
77        // NOTE: `kernel_image_base` tries to find the kernel in the memory
78        //       with the help of the CPU registers. On AMD64 architecture,
79        //       the kernel image base is usually found using the `MSR_LSTAR`
80        //       register, which contains the address of the system call
81        //       handler. This register is set by the operating system during
82        //       boot and is left unchanged (unless some rootkits are involved).
83        //
84        //       Therefore, we can take an arbitrary registers at any point
85        //       in time (as long as the OS has booted and the page tables are
86        //       set up) and use them to find the kernel image base.
87        let registers = session.registers(VcpuId(0))?;
88        let vmi = session.with_registers(&registers);
89
90        let kernel_image_base = vmi.os().kernel_image_base()?;
91        tracing::info!(%kernel_image_base);
92
93        // Get the system process.
94        //
95        // The system process is the first process created by the kernel.
96        // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
97        // To monitor page table entries, we need to locate the translation root
98        // of this process.
99        let system_process = vmi.os().system_process()?;
100        tracing::info!(system_process = %system_process.object()?);
101
102        // Get the translation root of the system process.
103        // This is effectively "the CR3 of the kernel".
104        //
105        // The translation root is the root of the page table hierarchy (also
106        // known as the Directory Table Base or PML4).
107        let root = system_process.translation_root()?;
108        tracing::info!(%root);
109
110        // Load the symbols from the profile.
111        let symbols = Symbols::new(profile)?;
112
113        // Enable monitoring of the INT3 and singlestep events.
114        //
115        // INT3 is used to monitor the execution of specific functions.
116        // Singlestep is used to monitor the modifications of page table
117        // entries.
118        vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
119        vmi.monitor_enable(EventMonitor::Singlestep)?;
120
121        // Create a new view for the monitor.
122        // This view is used for monitoring function calls and memory accesses.
123        let view = vmi.create_view(MemoryAccess::RWX)?;
124        vmi.switch_to_view(view)?;
125
126        // Create a new breakpoint controller.
127        //
128        // The breakpoint controller is used to insert breakpoints for specific
129        // functions.
130        //
131        // From the guest's perspective, these breakpoints are "hidden", since
132        // the breakpoint controller will unset the read/write access to the
133        // physical memory page where the breakpoint is inserted, while keeping
134        // the execute access.
135        //
136        // This way, the guest will be able to execute the code, but attempts to
137        // read or write the memory will trigger the `memory_access` callback.
138        //
139        // When a vCPU tries to execute the breakpoint instruction:
140        // - an `interrupt` callback will be triggered
141        // - the breakpoint will be handled (e.g., log the function call)
142        // - a fast-singlestep[1] will be performed over the INT3 instruction
143        //
144        // When a vCPU tries to read from this page (e.g., a PatchGuard check):
145        // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
146        //   access type)
147        // - fast-singlestep[1] will be performed over the instruction that tried to
148        //   read the memory
149        //
150        // This way, the instruction will read the original memory content.
151        //
152        // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
153        //     to a different view, execute a single instruction, and then
154        //     switch back to the original view. In this case, the view is
155        //     switched to the `default_view` (which is unmodified).
156        let mut bpm = BreakpointManager::new();
157
158        // Create a new page table monitor.
159        //
160        // The page table monitor is used to monitor the page table entries of
161        // the hooked functions.
162        //
163        // More specifically, it is used to monitor the pages that the breakpoint
164        // was inserted into. This is necessary to handle the case when the
165        // page containing the breakpoint is paged out (and then paged in
166        // again).
167        //
168        // `PageTableMonitor` works by unsetting the write access to the page
169        // tables of the hooked functions. When the page is paged out, the
170        // `PRESENT` bit in the page table entry is unset and, conversely, when
171        // the page is paged in, the `PRESENT` bit is set again.
172        //
173        // When that happens:
174        // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
175        //   access type)
176        // - the callback will mark the page as dirty in the page table monitor
177        // - a singlestep will be performed over the instruction that tried to modify
178        //   the memory containing the page table entry
179        // - the `singlestep` handler will process the dirty page table entries and
180        //   inform the breakpoint controller to handle the changes
181        let mut ptm = PageTableMonitor::new();
182
183        // Pause the VM to avoid race conditions between inserting breakpoints
184        // and monitoring page table entries. The VM resumes when the pause
185        // guard is dropped.
186        let _pause_guard = vmi.pause_guard()?;
187
188        // Insert breakpoint for the `NtCreateFile` function.
189        let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
190        let cx_NtCreateFile = (va_NtCreateFile, root);
191        let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
192            .global()
193            .with_tag("NtCreateFile");
194        bpm.insert(&vmi, bp_NtCreateFile)?;
195        ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
196        tracing::info!(%va_NtCreateFile);
197
198        // Insert breakpoint for the `NtWriteFile` function.
199        let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
200        let cx_NtWriteFile = (va_NtWriteFile, root);
201        let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
202            .global()
203            .with_tag("NtWriteFile");
204        bpm.insert(&vmi, bp_NtWriteFile)?;
205        ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
206        tracing::info!(%va_NtWriteFile);
207
208        // Insert breakpoint for the `PspInsertProcess` function.
209        let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
210        let cx_PspInsertProcess = (va_PspInsertProcess, root);
211        let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
212            .global()
213            .with_tag("PspInsertProcess");
214        bpm.insert(&vmi, bp_PspInsertProcess)?;
215        ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
216
217        // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
218        let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
219        let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
220        let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
221            .global()
222            .with_tag("MmCleanProcessAddressSpace");
223        bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
224        ptm.monitor(
225            &vmi,
226            cx_MmCleanProcessAddressSpace,
227            view,
228            "MmCleanProcessAddressSpace",
229        )?;
230
231        Ok(Self {
232            terminate_flag,
233            view,
234            bpm,
235            ptm,
236        })
237    }
Source

pub fn change_view_gfn( &self, view: View, old_gfn: Gfn, new_gfn: Gfn, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Changes the mapping of a guest frame number (GFN) in a specific view.

This method allows for remapping a GFN to a different physical frame within a view, enabling fine-grained control over memory layout in different views.

A notable use case for this method is implementing “stealth hooks”:

  1. Create a new GFN and copy the contents of the original page to it.
  2. Modify the new page by installing a breakpoint (e.g., 0xcc on AMD64) at a strategic location.
  3. Use this method to change the mapping of the original GFN to the new one.
  4. Set the memory access of the new GFN to non-readable.

When a read access occurs:

  • The handler should enable single-stepping.
  • Switch to an unmodified view (e.g., default_view) to execute the read instruction, which will read the original non-breakpoint byte.
  • Re-enable single-stepping afterwards.

This technique allows for transparent breakpoints that are difficult to detect by the guest OS or applications.

Source

pub fn reset_view_gfn(&self, view: View, gfn: Gfn) -> Result<(), VmiError>

Available on crate features utils and injector only.

Resets the mapping of a guest frame number (GFN) in a specific view to its original state.

This method reverts any custom mapping for the specified GFN in the given view, restoring it to the default mapping.

Source

pub fn monitor_enable( &self, option: <<Driver as VmiDriver>::Architecture as Architecture>::EventMonitor, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Enables monitoring of specific events.

This method allows you to enable monitoring of specific events, such as control register writes, interrupts, or single-step execution. Monitoring events can be useful for tracking specific guest behavior or for implementing custom analysis tools.

The type of event to monitor is defined by the architecture-specific Architecture::EventMonitor type.

When an event occurs, it will be passed to the event callback function for processing.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 118)
66    pub fn new(
67        session: &VmiSession<WindowsOs<Driver>>,
68        profile: &Profile,
69        terminate_flag: Arc<AtomicBool>,
70    ) -> Result<Self, VmiError> {
71        // Capture the current state of the vCPU and get the base address of
72        // the kernel.
73        //
74        // This base address is essential to correctly offset monitored
75        // functions.
76        //
77        // NOTE: `kernel_image_base` tries to find the kernel in the memory
78        //       with the help of the CPU registers. On AMD64 architecture,
79        //       the kernel image base is usually found using the `MSR_LSTAR`
80        //       register, which contains the address of the system call
81        //       handler. This register is set by the operating system during
82        //       boot and is left unchanged (unless some rootkits are involved).
83        //
84        //       Therefore, we can take an arbitrary registers at any point
85        //       in time (as long as the OS has booted and the page tables are
86        //       set up) and use them to find the kernel image base.
87        let registers = session.registers(VcpuId(0))?;
88        let vmi = session.with_registers(&registers);
89
90        let kernel_image_base = vmi.os().kernel_image_base()?;
91        tracing::info!(%kernel_image_base);
92
93        // Get the system process.
94        //
95        // The system process is the first process created by the kernel.
96        // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
97        // To monitor page table entries, we need to locate the translation root
98        // of this process.
99        let system_process = vmi.os().system_process()?;
100        tracing::info!(system_process = %system_process.object()?);
101
102        // Get the translation root of the system process.
103        // This is effectively "the CR3 of the kernel".
104        //
105        // The translation root is the root of the page table hierarchy (also
106        // known as the Directory Table Base or PML4).
107        let root = system_process.translation_root()?;
108        tracing::info!(%root);
109
110        // Load the symbols from the profile.
111        let symbols = Symbols::new(profile)?;
112
113        // Enable monitoring of the INT3 and singlestep events.
114        //
115        // INT3 is used to monitor the execution of specific functions.
116        // Singlestep is used to monitor the modifications of page table
117        // entries.
118        vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
119        vmi.monitor_enable(EventMonitor::Singlestep)?;
120
121        // Create a new view for the monitor.
122        // This view is used for monitoring function calls and memory accesses.
123        let view = vmi.create_view(MemoryAccess::RWX)?;
124        vmi.switch_to_view(view)?;
125
126        // Create a new breakpoint controller.
127        //
128        // The breakpoint controller is used to insert breakpoints for specific
129        // functions.
130        //
131        // From the guest's perspective, these breakpoints are "hidden", since
132        // the breakpoint controller will unset the read/write access to the
133        // physical memory page where the breakpoint is inserted, while keeping
134        // the execute access.
135        //
136        // This way, the guest will be able to execute the code, but attempts to
137        // read or write the memory will trigger the `memory_access` callback.
138        //
139        // When a vCPU tries to execute the breakpoint instruction:
140        // - an `interrupt` callback will be triggered
141        // - the breakpoint will be handled (e.g., log the function call)
142        // - a fast-singlestep[1] will be performed over the INT3 instruction
143        //
144        // When a vCPU tries to read from this page (e.g., a PatchGuard check):
145        // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
146        //   access type)
147        // - fast-singlestep[1] will be performed over the instruction that tried to
148        //   read the memory
149        //
150        // This way, the instruction will read the original memory content.
151        //
152        // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
153        //     to a different view, execute a single instruction, and then
154        //     switch back to the original view. In this case, the view is
155        //     switched to the `default_view` (which is unmodified).
156        let mut bpm = BreakpointManager::new();
157
158        // Create a new page table monitor.
159        //
160        // The page table monitor is used to monitor the page table entries of
161        // the hooked functions.
162        //
163        // More specifically, it is used to monitor the pages that the breakpoint
164        // was inserted into. This is necessary to handle the case when the
165        // page containing the breakpoint is paged out (and then paged in
166        // again).
167        //
168        // `PageTableMonitor` works by unsetting the write access to the page
169        // tables of the hooked functions. When the page is paged out, the
170        // `PRESENT` bit in the page table entry is unset and, conversely, when
171        // the page is paged in, the `PRESENT` bit is set again.
172        //
173        // When that happens:
174        // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
175        //   access type)
176        // - the callback will mark the page as dirty in the page table monitor
177        // - a singlestep will be performed over the instruction that tried to modify
178        //   the memory containing the page table entry
179        // - the `singlestep` handler will process the dirty page table entries and
180        //   inform the breakpoint controller to handle the changes
181        let mut ptm = PageTableMonitor::new();
182
183        // Pause the VM to avoid race conditions between inserting breakpoints
184        // and monitoring page table entries. The VM resumes when the pause
185        // guard is dropped.
186        let _pause_guard = vmi.pause_guard()?;
187
188        // Insert breakpoint for the `NtCreateFile` function.
189        let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
190        let cx_NtCreateFile = (va_NtCreateFile, root);
191        let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
192            .global()
193            .with_tag("NtCreateFile");
194        bpm.insert(&vmi, bp_NtCreateFile)?;
195        ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
196        tracing::info!(%va_NtCreateFile);
197
198        // Insert breakpoint for the `NtWriteFile` function.
199        let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
200        let cx_NtWriteFile = (va_NtWriteFile, root);
201        let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
202            .global()
203            .with_tag("NtWriteFile");
204        bpm.insert(&vmi, bp_NtWriteFile)?;
205        ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
206        tracing::info!(%va_NtWriteFile);
207
208        // Insert breakpoint for the `PspInsertProcess` function.
209        let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
210        let cx_PspInsertProcess = (va_PspInsertProcess, root);
211        let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
212            .global()
213            .with_tag("PspInsertProcess");
214        bpm.insert(&vmi, bp_PspInsertProcess)?;
215        ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
216
217        // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
218        let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
219        let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
220        let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
221            .global()
222            .with_tag("MmCleanProcessAddressSpace");
223        bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
224        ptm.monitor(
225            &vmi,
226            cx_MmCleanProcessAddressSpace,
227            view,
228            "MmCleanProcessAddressSpace",
229        )?;
230
231        Ok(Self {
232            terminate_flag,
233            view,
234            bpm,
235            ptm,
236        })
237    }
Source

pub fn monitor_disable( &self, option: <<Driver as VmiDriver>::Architecture as Architecture>::EventMonitor, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Disables monitoring of specific events.

This method allows you to disable monitoring of specific events that were previously enabled. It can be used to stop tracking certain hardware events or to reduce the overhead of event processing.

The type of event to disable is defined by the architecture-specific Architecture::EventMonitor type.

Source

pub fn events_pending(&self) -> usize

Available on crate features utils and injector only.

Returns the number of pending events.

This method provides a count of events that have occurred but have not yet been processed.

Source

pub fn event_processing_overhead(&self) -> Duration

Available on crate features utils and injector only.

Returns the time spent processing events by the driver.

This method provides a measure of the overhead introduced by event processing. It can be useful for performance tuning and understanding the impact of VMI operations on overall system performance.

Source

pub fn wait_for_event( &self, timeout: Duration, handler: impl FnMut(&VmiEvent<<Driver as VmiDriver>::Architecture>) -> VmiEventResponse<<Driver as VmiDriver>::Architecture>, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Waits for an event to occur and processes it with the provided handler.

This method blocks until an event occurs or the specified timeout is reached. When an event occurs, it is passed to the provided callback function for processing.

Source

pub fn pause(&self) -> Result<(), VmiError>

Available on crate features utils and injector only.

Pauses the virtual machine.

Source

pub fn resume(&self) -> Result<(), VmiError>

Available on crate features utils and injector only.

Resumes the virtual machine.

Source

pub fn pause_guard(&self) -> Result<VmiPauseGuard<'_, Driver>, VmiError>

Available on crate features utils and injector only.

Pauses the virtual machine and returns a guard that will resume it when dropped.

Examples found in repository?
examples/basic.rs (line 25)
9fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let domain_id = 'x: {
11        for name in &["win7", "win10", "win11", "ubuntu22"] {
12            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
13                break 'x domain_id;
14            }
15        }
16
17        panic!("Domain not found");
18    };
19
20    // Setup VMI.
21    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
22    let vmi = VmiCore::new(driver)?;
23
24    // Get the interrupt descriptor table for each vCPU and print it.
25    let _pause_guard = vmi.pause_guard()?;
26    let info = vmi.info()?;
27    for vcpu_id in 0..info.vcpus {
28        let registers = vmi.registers(VcpuId(vcpu_id))?;
29        let idt = Amd64::interrupt_descriptor_table(&vmi, &registers)?;
30
31        println!("IDT[{vcpu_id}]: {idt:#?}");
32    }
33
34    Ok(())
35}
More examples
Hide additional examples
examples/windows-recipe-messagebox.rs (line 72)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let (session, _profile) = common::create_vmi_session()?;
67
68    let explorer_pid = {
69        // This block is used to drop the pause guard after the PID is found.
70        // If the `session.handle()` would be called with the VM paused, no
71        // events would be triggered.
72        let _pause_guard = session.pause_guard()?;
73
74        let registers = session.registers(VcpuId(0))?;
75        let vmi = session.with_registers(&registers);
76
77        let explorer = match common::find_process(&vmi, "explorer.exe")? {
78            Some(explorer) => explorer,
79            None => {
80                tracing::error!("explorer.exe not found");
81                return Ok(());
82            }
83        };
84
85        tracing::info!(
86            pid = %explorer.id()?,
87            object = %explorer.object()?,
88            "found explorer.exe"
89        );
90
91        explorer.id()?
92    };
93
94    session.handle(|session| {
95        UserInjectorHandler::new(
96            session,
97            recipe_factory(MessageBox::new(
98                "Hello, World!",
99                "This is a message box from the VMI!",
100            )),
101        )?
102        .with_pid(explorer_pid)
103    })?;
104
105    Ok(())
106}
examples/windows-recipe-writefile.rs (line 210)
203fn main() -> Result<(), Box<dyn std::error::Error>> {
204    let (session, _profile) = common::create_vmi_session()?;
205
206    let explorer_pid = {
207        // This block is used to drop the pause guard after the PID is found.
208        // If the `session.handle()` would be called with the VM paused, no
209        // events would be triggered.
210        let _pause_guard = session.pause_guard()?;
211
212        let registers = session.registers(VcpuId(0))?;
213        let vmi = session.with_registers(&registers);
214
215        let explorer = match common::find_process(&vmi, "explorer.exe")? {
216            Some(explorer) => explorer,
217            None => {
218                tracing::error!("explorer.exe not found");
219                return Ok(());
220            }
221        };
222
223        tracing::info!(
224            pid = %explorer.id()?,
225            object = %explorer.object()?,
226            "found explorer.exe"
227        );
228
229        explorer.id()?
230    };
231
232    session.handle(|session| {
233        UserInjectorHandler::new(
234            session,
235            recipe_factory(GuestFile::new(
236                "C:\\Users\\John\\Desktop\\test.txt",
237                "Hello, World!".as_bytes(),
238            )),
239        )?
240        .with_pid(explorer_pid)
241    })?;
242
243    Ok(())
244}
examples/windows-recipe-writefile-advanced.rs (line 308)
301fn main() -> Result<(), Box<dyn std::error::Error>> {
302    let (session, _profile) = common::create_vmi_session()?;
303
304    let explorer_pid = {
305        // This block is used to drop the pause guard after the PID is found.
306        // If the `session.handle()` would be called with the VM paused, no
307        // events would be triggered.
308        let _pause_guard = session.pause_guard()?;
309
310        let registers = session.registers(VcpuId(0))?;
311        let vmi = session.with_registers(&registers);
312
313        let explorer = match common::find_process(&vmi, "explorer.exe")? {
314            Some(explorer) => explorer,
315            None => {
316                tracing::error!("explorer.exe not found");
317                return Ok(());
318            }
319        };
320
321        tracing::info!(
322            pid = %explorer.id()?,
323            object = %explorer.object()?,
324            "found explorer.exe"
325        );
326
327        explorer.id()?
328    };
329
330    let mut content = Vec::new();
331    for c in 'A'..='Z' {
332        content.extend((0..2049).map(|_| c as u8).collect::<Vec<_>>());
333    }
334
335    session.handle(|session| {
336        UserInjectorHandler::new(
337            session,
338            recipe_factory(GuestFile::new(
339                "C:\\Users\\John\\Desktop\\test.txt",
340                content,
341            )),
342        )?
343        .with_pid(explorer_pid)
344    })?;
345
346    Ok(())
347}
examples/common/mod.rs (line 41)
15pub fn create_vmi_session() -> Result<Session, Box<dyn std::error::Error>> {
16    tracing_subscriber::fmt()
17        .with_max_level(tracing::Level::DEBUG)
18        .with_target(false)
19        .init();
20
21    let domain_id = 'x: {
22        for name in &["win7", "win10", "win11", "ubuntu22"] {
23            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
24                break 'x domain_id;
25            }
26        }
27
28        panic!("Domain not found");
29    };
30
31    tracing::debug!(?domain_id);
32
33    // Setup VMI.
34    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
35    let core = VmiCore::new(driver)?;
36
37    // Try to find the kernel information.
38    // This is necessary in order to load the profile.
39    let kernel_info = {
40        // Pause the vCPU to get consistent state.
41        let _pause_guard = core.pause_guard()?;
42
43        // Get the register state for the first vCPU.
44        let registers = core.registers(VcpuId(0))?;
45
46        // On AMD64 architecture, the kernel is usually found using the
47        // `MSR_LSTAR` register, which contains the address of the system call
48        // handler. This register is set by the operating system during boot
49        // and is left unchanged (unless some rootkits are involved).
50        //
51        // Therefore, we can take an arbitrary registers at any point in time
52        // (as long as the OS has booted and the page tables are set up) and
53        // use them to find the kernel.
54        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
55    };
56
57    // Load the profile.
58    // The profile contains offsets to kernel functions and data structures.
59    let isr = IsrCache::new("cache")?;
60    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
61    let entry = Box::leak(Box::new(entry));
62    let profile = entry.profile()?;
63
64    // Create the VMI session.
65    tracing::info!("Creating VMI session");
66    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
67
68    // Please don't do this in production code.
69    // This is only done for the sake of the example.
70    let core = Box::leak(Box::new(core));
71    let os = Box::leak(Box::new(os));
72
73    Ok((VmiSession::new(core, os), profile))
74}
examples/basic-process-list.rs (line 32)
13fn main() -> Result<(), Box<dyn std::error::Error>> {
14    let domain_id = 'x: {
15        for name in &["win7", "win10", "win11", "ubuntu22"] {
16            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
17                break 'x domain_id;
18            }
19        }
20
21        panic!("Domain not found");
22    };
23
24    // Setup VMI.
25    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
26    let core = VmiCore::new(driver)?;
27
28    // Try to find the kernel information.
29    // This is necessary in order to load the profile.
30    let kernel_info = {
31        // Pause the VM to get consistent state.
32        let _pause_guard = core.pause_guard()?;
33
34        // Get the register state for the first vCPU.
35        let registers = core.registers(VcpuId(0))?;
36
37        // On AMD64 architecture, the kernel is usually found using the
38        // `MSR_LSTAR` register, which contains the address of the system call
39        // handler. This register is set by the operating system during boot
40        // and is left unchanged (unless some rootkits are involved).
41        //
42        // Therefore, we can take an arbitrary registers at any point in time
43        // (as long as the OS has booted and the page tables are set up) and
44        // use them to find the kernel.
45        WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
46    };
47
48    // Load the profile.
49    // The profile contains offsets to kernel functions and data structures.
50    let isr = IsrCache::new("cache")?;
51    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
52    let profile = entry.profile()?;
53
54    // Create the VMI session.
55    tracing::info!("Creating VMI session");
56    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
57    let session = VmiSession::new(&core, &os);
58
59    // Pause the VM again to get consistent state.
60    let _pause_guard = session.pause_guard()?;
61
62    // Create a new `VmiState` with the current register.
63    let registers = session.registers(VcpuId(0))?;
64    let vmi = session.with_registers(&registers);
65
66    // Get the list of processes and print them.
67    for process in vmi.os().processes()? {
68        let process = process?;
69
70        println!(
71            "{} [{}] {} (root @ {})",
72            process.object()?,
73            process.id()?,
74            process.name()?,
75            process.translation_root()?
76        );
77    }
78
79    Ok(())
80}
Source

pub fn allocate_gfn(&self) -> Result<Gfn, VmiError>

Available on crate features utils and injector only.

Allocates a guest frame number (GFN).

This method allocates a new GFN, with the driver responsible for choosing the specific frame to allocate. It’s useful when you need to allocate new memory pages for the VM without caring about the specific location.

Source

pub fn allocate_gfn_at(&self, gfn: Gfn) -> Result<(), VmiError>

Available on crate features utils and injector only.

Allocates a guest frame number (GFN) at a specific location.

This method allows you to allocate a particular GFN. It’s useful when you need to allocate a specific memory page for the VM.

Source

pub fn free_gfn(&self, gfn: Gfn) -> Result<(), VmiError>

Available on crate features utils and injector only.

Frees a previously allocated guest frame number (GFN).

This method deallocates a GFN that was previously allocated. It’s important to free GFNs when they’re no longer needed to prevent memory leaks in the VM.

Source

pub fn inject_interrupt( &self, vcpu: VcpuId, interrupt: <<Driver as VmiDriver>::Architecture as Architecture>::Interrupt, ) -> Result<(), VmiError>

Available on crate features utils and injector only.

Injects an interrupt into a specific virtual CPU.

This method allows for the injection of architecture-specific interrupts into a given vCPU. It can be used to simulate hardware events or to manipulate the guest’s execution flow for analysis purposes.

The type of interrupt and its parameters are defined by the architecture-specific Architecture::Interrupt type.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 498)
478    fn dispatch(
479        &mut self,
480        vmi: &VmiContext<WindowsOs<Driver>>,
481    ) -> Result<VmiEventResponse<Amd64>, VmiError> {
482        let event = vmi.event();
483        let result = match event.reason() {
484            EventReason::MemoryAccess(_) => self.memory_access(vmi),
485            EventReason::Interrupt(_) => self.interrupt(vmi),
486            EventReason::Singlestep(_) => self.singlestep(vmi),
487            _ => panic!("Unhandled event: {:?}", event.reason()),
488        };
489
490        // If VMI tries to read from a page that is not present, it will return
491        // a page fault error. In this case, we inject a page fault interrupt
492        // to the guest.
493        //
494        // Once the guest handles the page fault, it will retry to execute the
495        // instruction that caused the page fault.
496        if let Err(VmiError::Translation(pfs)) = result {
497            tracing::warn!(?pfs, "Page fault, injecting");
498            vmi.inject_interrupt(event.vcpu_id(), Interrupt::page_fault(pfs[0].va, 0))?;
499            return Ok(VmiEventResponse::default());
500        }
501
502        result
503    }
Source

pub fn reset_state(&self) -> Result<(), VmiError>

Available on crate features utils and injector only.

Resets the state of the VMI system.

This method clears all event monitors, caches, and any other stateful data maintained by the VMI system. It’s useful for bringing the VMI system back to a known clean state, which can be necessary when switching between different analysis tasks or recovering from error conditions.

Trait Implementations§

Source§

impl<Os> Clone for VmiSession<'_, Os>
where Os: VmiOs,

Source§

fn clone(&self) -> VmiSession<'_, Os>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<Os> Deref for VmiSession<'_, Os>
where Os: VmiOs,

Source§

type Target = VmiCore<<Os as VmiOs>::Driver>

The resulting type after dereferencing.
Source§

fn deref(&self) -> &<VmiSession<'_, Os> as Deref>::Target

Dereferences the value.
Source§

impl<Os> Copy for VmiSession<'_, Os>
where Os: VmiOs,

Auto Trait Implementations§

§

impl<'a, Os> Freeze for VmiSession<'a, Os>

§

impl<'a, Os> !RefUnwindSafe for VmiSession<'a, Os>

§

impl<'a, Os> !Send for VmiSession<'a, Os>

§

impl<'a, Os> !Sync for VmiSession<'a, Os>

§

impl<'a, Os> Unpin for VmiSession<'a, Os>

§

impl<'a, Os> UnsafeUnpin for VmiSession<'a, Os>

§

impl<'a, Os> !UnwindSafe for VmiSession<'a, Os>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more