Skip to main content

WindowsOs

Struct WindowsOs 

Source
pub struct WindowsOs<Driver>
where Driver: VmiDriver,
{ /* private fields */ }
Available on crate feature os-windows only.
Expand description

VMI operations for the Windows operating system.

WindowsOs provides methods and utilities for introspecting a Windows-based virtual machine. It encapsulates Windows-specific knowledge and operations, allowing for high-level interactions with the guest OS structures and processes.

§Usage

Create an instance of WindowsOs using a Profile that contains information about the specific Windows version being introspected:

use isr::cache::IsrCache;
use vmi::{
    VcpuId, VmiCore,
    arch::amd64::Amd64,
    driver::{VmiQueryRegisters, VmiRead, VmiVmControl},
    os::windows::WindowsOs,
};

// Setup VMI.
let core = VmiCore::new(driver)?;

// Try to find the kernel information.
// This is necessary in order to load the profile.
let kernel_info = {
    let _guard = core.pause_guard()?;
    let registers = core.registers(VcpuId(0))?;

    WindowsOs::find_kernel(&core, &registers)?.expect("kernel information")
};

// Load the profile using the ISR library.
let isr = IsrCache::new("cache")?;
let entry = isr.entry_from_codeview(kernel_info.codeview)?;
let profile = entry.profile()?;

// Create a new `WindowsOs` instance.
let os = WindowsOs::<Driver>::new(&profile)?;

§Important Notes

  • Many methods of this struct require pausing the VM to ensure consistency. Use pause_guard when performing operations that might be affected by concurrent changes in the guest OS.

  • The behavior and accuracy of some methods may vary depending on the Windows version being introspected. Always ensure your Profile matches the guest OS version.

§Examples

Retrieving information about the current process:

let process = vmi.os().current_process()?;
let process_id = process.id()?;
let process_name = process.name()?;
println!("Current process: {} (PID: {})", process_name, process_id);

Enumerating all processes:

for process in vmi.os().processes()? {
    let process = process?;
    println!("Process: {} (PID: {})", process.name()?, process.id()?);
}

§Safety

While this struct doesn’t use unsafe code directly, many of its methods interact with raw memory of the guest OS. Incorrect usage can lead to invalid memory access or misinterpretation of data. Always ensure you’re working with the correct memory regions and OS structures.

Implementations§

Source§

impl<Driver> WindowsOs<Driver>
where Driver: VmiRead, <Driver as VmiDriver>::Architecture: ArchAdapter<Driver>,

Source

pub const NtCurrentProcess32: u64 = 0xffff_ffff

32-bit current process pseudo-handle (-1).

Source

pub const NtCurrentProcess64: u64 = 0xffff_ffff_ffff_ffff

64-bit current process pseudo-handle (-1).

Source

pub const NtCurrentThread32: u64 = 0xffff_fffe

32-bit current thread pseudo-handle (-2).

Source

pub const NtCurrentThread64: u64 = 0xffff_ffff_ffff_fffe

64-bit current thread pseudo-handle (-2).

Source

pub fn new(profile: &Profile<'_>) -> Result<WindowsOs<Driver>, VmiError>

Creates a new WindowsOs instance.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 569)
525fn main() -> Result<(), Box<dyn std::error::Error>> {
526    tracing_subscriber::fmt()
527        .with_max_level(tracing::Level::DEBUG)
528        .init();
529
530    let domain_id = 'x: {
531        for name in &["win7", "win10", "win11", "ubuntu22"] {
532            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
533                break 'x domain_id;
534            }
535        }
536
537        panic!("Domain not found");
538    };
539
540    tracing::debug!(?domain_id);
541
542    // Setup VMI.
543    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
544    let core = VmiCore::new(driver)?;
545
546    // Try to find the kernel information.
547    // This is necessary in order to load the profile.
548    let kernel_info = {
549        let _pause_guard = core.pause_guard()?;
550        let regs = core.registers(0.into())?;
551
552        WindowsOs::find_kernel(&core, &regs)?.expect("kernel information")
553    };
554
555    // Load the profile.
556    // The profile contains offsets to kernel functions and data structures.
557    let isr = IsrCache::new("cache")?;
558    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
559    let profile = entry.profile()?;
560
561    // Create the VMI session.
562    tracing::info!("Creating VMI session");
563    let terminate_flag = Arc::new(AtomicBool::new(false));
564    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
565    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
566    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
567    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
568
569    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
570    let session = VmiSession::new(&core, &os);
571
572    session.handle(|session| Monitor::new(session, &profile, terminate_flag))?;
573
574    Ok(())
575}
More examples
Hide additional examples
examples/common/mod.rs (line 66)
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 56)
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 paused = session.pause_guard()?;
61
62    // Create a new `VmiState` with the boot CPU registers.
63    let vmi = paused.state();
64
65    // Get the list of processes and print them.
66    for process in vmi.os().processes()? {
67        let process = process?;
68
69        println!(
70            "{} [{}] {} (root @ {})",
71            process.object()?,
72            process.id()?,
73            process.name()?,
74            process.translation_root()?
75        );
76    }
77
78    Ok(())
79}
examples/windows-reactor/main.rs (line 304)
245fn main() -> Result<(), Error> {
246    let filter = EnvFilter::default()
247        .add_directive(tracing::Level::DEBUG.into())
248        .add_directive("reqwest=warn".parse()?)
249        .add_directive("rustls=warn".parse()?);
250
251    tracing_subscriber::fmt()
252        .with_env_filter(filter)
253        .with_target(false)
254        .init();
255
256    let domain_id = match std::env::var("VMI_XEN_DOMAIN_ID") {
257        Ok(domain_id) => XenDomainId(
258            domain_id
259                .parse()
260                .context("invalid VMI_XEN_DOMAIN_ID environment variable")?,
261        ),
262        Err(_) => {
263            let domain_name = std::env::var("VMI_XEN_DOMAIN_NAME")
264                .context("invalid VMI_XEN_DOMAIN_NAME environment variable")?;
265
266            tracing::info!(%domain_name, "resolving domain ID");
267
268            match XenStore::new()?.domain_id_from_name(&domain_name)? {
269                Some(domain_id) => domain_id,
270                None => return Err(anyhow::anyhow!("domain not found: {domain_name}")),
271            }
272        }
273    };
274
275    // Setup VMI.
276    tracing::info!(%domain_id, "setting up VMI");
277    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
278    let core = VmiCore::new(driver)?;
279
280    // Try to find the kernel information.
281    // This is necessary in order to load the profile.
282    let kernel_info = {
283        let _pause_guard = core.pause_guard()?;
284        let registers = core.registers(VcpuId(0))?;
285
286        WindowsOs::find_kernel(&core, &registers)?.context("cannot find kernel information")?
287    };
288
289    // Load the kernel profile.
290    // The profile contains offsets to kernel functions and data structures.
291    tracing::info!(codeview = ?kernel_info.codeview, "loading kernel profile");
292    let isr = IsrCache::new("cache")?;
293    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
294    let profile = entry.profile()?;
295
296    // Create the VMI session.
297    tracing::info!("creating VMI session");
298    let terminate_flag = Arc::new(AtomicBool::new(false));
299    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
300    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
301    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
302    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
303
304    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
305    let session = VmiSession::new(&core, &os);
306
307    let handler = NetIo::default();
308
309    //
310    // The following `let ncrypt_* = ...` lines demonstrate how to manually
311    // resolve a module, load its profile (symbols) and add it to the resolver
312    // via `with_module(_in_process)`.
313    //
314    // Note that this is not strictly necessary, as `ModuleResolver::resolve()`
315    // will automatically resolve modules if they are not explicitly added.
316    //
317    // Manually resolving modules can be useful in cases where you want to deal
318    // with the resolved information (base address, profile) in other places.
319    //
320
321    let ncrypt_resolved = {
322        let paused = session.pause_guard()?;
323        let vmi = paused.state();
324
325        // Calling `resolve_user_module(&vmi, &isr, "ncrypt.dll", "lsass.exe")`
326        // would also work, but this demonstrates how to use a custom predicate.
327        //
328        // Also, `match_lsass` is more strict, because it specifically looks
329        // for "lsass.exe" in SessionId 0 (therefore, avoiding potential false
330        // positives or potential malicious processes).
331        vmi::utils::resolver::resolve_user_module(&vmi, &isr, "ncrypt.dll", match_lsass)?
332            .context("ncrypt.dll not found in lsass.exe")?
333    };
334
335    let ncrypt_process = ncrypt_resolved
336        .process
337        .context("resolved ncrypt.dll is not associated with a process")?;
338
339    let ncrypt_entry = isr
340        .entry_from_codeview(ncrypt_resolved.debug_signature)
341        .context("cannot find symbols for ncrypt.dll")?;
342
343    let ncrypt_profile = ncrypt_entry
344        .profile()
345        .context("cannot load profile for ncrypt.dll")?;
346
347    // The `SymbolCache` holds the resolved `isr::Entry` items.
348    let mut cache = SymbolCache::default();
349    let modules = ModuleResolver::default()
350        // `with_kernel` MUST be called if `Event` variants reference kernel
351        // symbols - like `NtWriteFile` in this example.
352        //
353        // This is because the "kernel" module is always optional.
354        .with_kernel(kernel_info.base_address, profile)
355        .with_module_in_process(
356            Module::NcryptDll,
357            ncrypt_process,
358            ncrypt_resolved.image_base,
359            ncrypt_profile,
360        )
361        // This will automatically resolve the `netio.sys` module and load
362        // its profile.
363        //
364        // Note that if we hadn't called `with_module_in_process` for
365        // `ncrypt.dll`, it would also be automatically resolved here.
366        .resolve(&session, &isr, &mut cache)?;
367
368    // Finally, we collect the events according to the resolved information
369    // and the metadata.
370    //
371    // For example, if some module/event is marked as `optional` and the
372    // resolver fails to resolve it, then it will simply not be included
373    // in the `events`.
374    let events = modules.into_events()?;
375
376    // And we're ready to create the reactor!
377    session.handle(|session| {
378        Ok(Reactor::new(session, handler, events)?.with_termination_flag(terminate_flag))
379    })?;
380
381    Ok(())
382}
Source

pub fn with_kernel_base( profile: &Profile<'_>, kernel_base: Va, ) -> Result<WindowsOs<Driver>, VmiError>

Creates a new WindowsOs instance with a known kernel base address.

Examples found in repository?
examples/windows-dump.rs (line 427)
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}
Source

pub fn offsets(vmi: VmiState<'_, WindowsOs<Driver>>) -> &Offsets

Returns a reference to the Windows-specific memory offsets.

Source

pub fn symbols(vmi: VmiState<'_, WindowsOs<Driver>>) -> &Symbols

Returns a reference to the Windows-specific symbols.

Source

pub fn find_kernel( vmi: &VmiCore<Driver>, registers: &<<Driver as VmiDriver>::Architecture as Architecture>::Registers, ) -> Result<Option<WindowsKernelInformation>, VmiError>

Locates the Windows kernel in memory based on the CPU registers. This function is architecture-specific.

On AMD64, the kernel is located by taking the MSR_LSTAR value and reading the virtual memory page by page backwards until the MZ header is found.

Examples found in repository?
examples/windows-breakpoint-manager.rs (line 552)
525fn main() -> Result<(), Box<dyn std::error::Error>> {
526    tracing_subscriber::fmt()
527        .with_max_level(tracing::Level::DEBUG)
528        .init();
529
530    let domain_id = 'x: {
531        for name in &["win7", "win10", "win11", "ubuntu22"] {
532            if let Some(domain_id) = XenStore::new()?.domain_id_from_name(name)? {
533                break 'x domain_id;
534            }
535        }
536
537        panic!("Domain not found");
538    };
539
540    tracing::debug!(?domain_id);
541
542    // Setup VMI.
543    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
544    let core = VmiCore::new(driver)?;
545
546    // Try to find the kernel information.
547    // This is necessary in order to load the profile.
548    let kernel_info = {
549        let _pause_guard = core.pause_guard()?;
550        let regs = core.registers(0.into())?;
551
552        WindowsOs::find_kernel(&core, &regs)?.expect("kernel information")
553    };
554
555    // Load the profile.
556    // The profile contains offsets to kernel functions and data structures.
557    let isr = IsrCache::new("cache")?;
558    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
559    let profile = entry.profile()?;
560
561    // Create the VMI session.
562    tracing::info!("Creating VMI session");
563    let terminate_flag = Arc::new(AtomicBool::new(false));
564    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
565    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
566    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
567    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
568
569    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
570    let session = VmiSession::new(&core, &os);
571
572    session.handle(|session| Monitor::new(session, &profile, terminate_flag))?;
573
574    Ok(())
575}
More examples
Hide additional examples
examples/windows-dump.rs (line 416)
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 54)
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 45)
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 paused = session.pause_guard()?;
61
62    // Create a new `VmiState` with the boot CPU registers.
63    let vmi = paused.state();
64
65    // Get the list of processes and print them.
66    for process in vmi.os().processes()? {
67        let process = process?;
68
69        println!(
70            "{} [{}] {} (root @ {})",
71            process.object()?,
72            process.id()?,
73            process.name()?,
74            process.translation_root()?
75        );
76    }
77
78    Ok(())
79}
examples/windows-reactor/main.rs (line 286)
245fn main() -> Result<(), Error> {
246    let filter = EnvFilter::default()
247        .add_directive(tracing::Level::DEBUG.into())
248        .add_directive("reqwest=warn".parse()?)
249        .add_directive("rustls=warn".parse()?);
250
251    tracing_subscriber::fmt()
252        .with_env_filter(filter)
253        .with_target(false)
254        .init();
255
256    let domain_id = match std::env::var("VMI_XEN_DOMAIN_ID") {
257        Ok(domain_id) => XenDomainId(
258            domain_id
259                .parse()
260                .context("invalid VMI_XEN_DOMAIN_ID environment variable")?,
261        ),
262        Err(_) => {
263            let domain_name = std::env::var("VMI_XEN_DOMAIN_NAME")
264                .context("invalid VMI_XEN_DOMAIN_NAME environment variable")?;
265
266            tracing::info!(%domain_name, "resolving domain ID");
267
268            match XenStore::new()?.domain_id_from_name(&domain_name)? {
269                Some(domain_id) => domain_id,
270                None => return Err(anyhow::anyhow!("domain not found: {domain_name}")),
271            }
272        }
273    };
274
275    // Setup VMI.
276    tracing::info!(%domain_id, "setting up VMI");
277    let driver = VmiXenDriver::<Amd64>::new(domain_id)?;
278    let core = VmiCore::new(driver)?;
279
280    // Try to find the kernel information.
281    // This is necessary in order to load the profile.
282    let kernel_info = {
283        let _pause_guard = core.pause_guard()?;
284        let registers = core.registers(VcpuId(0))?;
285
286        WindowsOs::find_kernel(&core, &registers)?.context("cannot find kernel information")?
287    };
288
289    // Load the kernel profile.
290    // The profile contains offsets to kernel functions and data structures.
291    tracing::info!(codeview = ?kernel_info.codeview, "loading kernel profile");
292    let isr = IsrCache::new("cache")?;
293    let entry = isr.entry_from_codeview(kernel_info.codeview)?;
294    let profile = entry.profile()?;
295
296    // Create the VMI session.
297    tracing::info!("creating VMI session");
298    let terminate_flag = Arc::new(AtomicBool::new(false));
299    signal_hook::flag::register(signal_hook::consts::SIGHUP, terminate_flag.clone())?;
300    signal_hook::flag::register(signal_hook::consts::SIGINT, terminate_flag.clone())?;
301    signal_hook::flag::register(signal_hook::consts::SIGALRM, terminate_flag.clone())?;
302    signal_hook::flag::register(signal_hook::consts::SIGTERM, terminate_flag.clone())?;
303
304    let os = WindowsOs::<VmiXenDriver<Amd64>>::new(&profile)?;
305    let session = VmiSession::new(&core, &os);
306
307    let handler = NetIo::default();
308
309    //
310    // The following `let ncrypt_* = ...` lines demonstrate how to manually
311    // resolve a module, load its profile (symbols) and add it to the resolver
312    // via `with_module(_in_process)`.
313    //
314    // Note that this is not strictly necessary, as `ModuleResolver::resolve()`
315    // will automatically resolve modules if they are not explicitly added.
316    //
317    // Manually resolving modules can be useful in cases where you want to deal
318    // with the resolved information (base address, profile) in other places.
319    //
320
321    let ncrypt_resolved = {
322        let paused = session.pause_guard()?;
323        let vmi = paused.state();
324
325        // Calling `resolve_user_module(&vmi, &isr, "ncrypt.dll", "lsass.exe")`
326        // would also work, but this demonstrates how to use a custom predicate.
327        //
328        // Also, `match_lsass` is more strict, because it specifically looks
329        // for "lsass.exe" in SessionId 0 (therefore, avoiding potential false
330        // positives or potential malicious processes).
331        vmi::utils::resolver::resolve_user_module(&vmi, &isr, "ncrypt.dll", match_lsass)?
332            .context("ncrypt.dll not found in lsass.exe")?
333    };
334
335    let ncrypt_process = ncrypt_resolved
336        .process
337        .context("resolved ncrypt.dll is not associated with a process")?;
338
339    let ncrypt_entry = isr
340        .entry_from_codeview(ncrypt_resolved.debug_signature)
341        .context("cannot find symbols for ncrypt.dll")?;
342
343    let ncrypt_profile = ncrypt_entry
344        .profile()
345        .context("cannot load profile for ncrypt.dll")?;
346
347    // The `SymbolCache` holds the resolved `isr::Entry` items.
348    let mut cache = SymbolCache::default();
349    let modules = ModuleResolver::default()
350        // `with_kernel` MUST be called if `Event` variants reference kernel
351        // symbols - like `NtWriteFile` in this example.
352        //
353        // This is because the "kernel" module is always optional.
354        .with_kernel(kernel_info.base_address, profile)
355        .with_module_in_process(
356            Module::NcryptDll,
357            ncrypt_process,
358            ncrypt_resolved.image_base,
359            ncrypt_profile,
360        )
361        // This will automatically resolve the `netio.sys` module and load
362        // its profile.
363        //
364        // Note that if we hadn't called `with_module_in_process` for
365        // `ncrypt.dll`, it would also be automatically resolved here.
366        .resolve(&session, &isr, &mut cache)?;
367
368    // Finally, we collect the events according to the resolved information
369    // and the metadata.
370    //
371    // For example, if some module/event is marked as `optional` and the
372    // resolver fails to resolve it, then it will simply not be included
373    // in the `events`.
374    let events = modules.into_events()?;
375
376    // And we're ready to create the reactor!
377    session.handle(|session| {
378        Ok(Reactor::new(session, handler, events)?.with_termination_flag(terminate_flag))
379    })?;
380
381    Ok(())
382}
Source

pub fn kernel_build_number( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<u32, VmiError>

Returns the kernel build number.

§Notes

The value is cached after the first read.

§Implementation Details

Corresponds to NtBuildNumber symbol.

Source

pub fn kernel_information_string_ex( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Option<String>, VmiError>

Returns the kernel information string.

§Notes

The kernel information string is cached after the first read.

§Implementation Details

Corresponds to NtBuildLab symbol.

Source

pub fn is_kernel_handle( vmi: VmiState<'_, WindowsOs<Driver>>, handle: u64, ) -> Result<bool, VmiError>

Checks if the given handle is a kernel handle.

A kernel handle is a handle with the highest bit set.

Source

pub fn lowest_user_address( _vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Va, VmiError>

Returns the lowest user-mode address.

This method returns a constant value (0x10000) representing the lowest address that can be used by user-mode applications in Windows.

§Notes
  • Windows creates a NO_ACCESS VAD (Virtual Address Descriptor) for the first 64KB of virtual memory. This means the VA range 0-0x10000 is off-limits for usage.

  • This behavior is consistent across all Windows versions from XP through recent Windows 11, and applies to x86, x64, and ARM64 architectures.

  • Many Windows APIs leverage this fact to determine whether an input argument is a pointer or not. Here are two notable examples:

    1. The FindResource() function accepts an lpName parameter of type LPCTSTR, which can be either:

      • A pointer to a valid string

      • A value created by MAKEINTRESOURCE(ID)

        This allows FindResource() to accept WORD values (unsigned shorts) with a maximum value of 0xFFFF, distinguishing them from valid memory addresses.

    2. The AddAtom() function similarly accepts an lpString parameter of type LPCTSTR. This parameter can be:

      • A pointer to a null-terminated string (max 255 bytes)
      • An integer atom converted using the MAKEINTATOM(ID) macro

    In both cases, the API can distinguish between valid pointers (which will be above 0x10000) and integer values (which will be below 0x10000), allowing for flexible parameter usage without ambiguity.

Source

pub fn highest_user_address( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Va, VmiError>

Returns the highest user-mode address.

This method reads the highest user-mode address from the Windows kernel. The value is cached after the first read for performance.

§Notes

This value is cached after the first read.

§Implementation Details

Corresponds to MmHighestUserAddress symbol.

Source

pub fn is_valid_user_address( vmi: VmiState<'_, WindowsOs<Driver>>, address: Va, ) -> Result<bool, VmiError>

Checks if a given address is a valid user-mode address.

This method determines whether the provided address falls within the range of valid user-mode addresses in Windows.

Source

pub fn unloaded_modules<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<impl Iterator<Item = Result<WindowsUnloadedDriver<'a, Driver>, VmiError>> + use<'a, Driver>, VmiError>

Returns an iterator over recently unloaded drivers.

Walks the kernel’s fixed-size circular buffer of unloaded driver records, newest first. The iterator stops at the first empty slot or after the buffer’s 50-entry capacity has been visited.

§Implementation Details

Corresponds to MmUnloadedDrivers and MmLastUnloadedDriver.

Source

pub fn number_of_processors( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<u16, VmiError>

Returns the number of active processors in the system.

§Implementation Details

Corresponds to KeNumberProcessors.

Source

pub fn current_kpcr(vmi: VmiState<'_, WindowsOs<Driver>>) -> Va

Returns the virtual address of the current Kernel Processor Control Region (KPCR).

The KPCR is a per-processor data structure in Windows that contains critical information about the current processor state. This method returns the virtual address of the KPCR for the current processor.

Source

pub fn kprcb<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, vcpu_id: VcpuId, ) -> Result<WindowsKernelProcessorBlock<'a, Driver>, VmiError>

Returns a Kernel Processor Control Block (KPRCB) for the specified processor.

The KPRCB is an opaque, per-processor structure embedded within the KPCR.

§Implementation Details

Corresponds to KiProcessorBlock symbol, offset by the processor ID.

Source

pub fn current_kprcb<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<WindowsKernelProcessorBlock<'a, Driver>, VmiError>

Returns the Kernel Processor Control Block (KPRCB) for the processor whose register state is currently loaded.

Unlike kprcb, which indexes the KiProcessorBlock array by processor ID, this resolves the PRCB from the current KPCR (gs-relative on AMD64) through KPCR.Prcb.

§Implementation Details

Corresponds to KPCR.Prcb.

Source

pub fn exception_record( vmi: VmiState<'_, WindowsOs<Driver>>, address: Va, ) -> Result<WindowsExceptionRecord, VmiError>

Returns information from an exception record at the specified address.

This method reads and parses an EXCEPTION_RECORD structure from memory, providing detailed information about an exception that has occurred in the system. The returned WindowsExceptionRecord contains data such as the exception code, flags, and related memory addresses.

Source

pub fn last_status( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Option<u32>, VmiError>

Returns the last status value for the current thread.

In Windows, the last status value is typically used to store error codes or success indicators from system calls. This method reads this value from the Thread Environment Block (TEB) of the current thread, providing insight into the outcome of recent operations performed by the thread.

Returns None if the TEB is not available.

§Notes

LastStatusValue is a NTSTATUS value, whereas LastError is a Win32 error code. The two values are related but not identical. You can obtain the Win32 error code by calling VmiOs::last_error.

§Implementation Details

Corresponds to NtCurrentTeb()->LastStatusValue.

Source

pub fn idle_process<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<WindowsProcess<'a, Driver>, VmiError>

Returns the idle process (PID 0).

The idle process is a special system process that runs when no other threads are ready to execute on a processor.

§Notes

This value is cached after the first read.

§Implementation Details

Corresponds to PsIdleProcess symbol.

Source

pub fn idle_thread<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<WindowsThread<'a, Driver>, VmiError>

Returns the idle thread for the current processor.

The idle thread is a per-processor thread that runs when no other threads are scheduled on that processor.

§Implementation Details

Corresponds to KPCR.Prcb.IdleThread.

Source

pub fn object<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, va: Va, ) -> Result<WindowsObject<'a, Driver>, VmiError>

Returns the Windows object.

Source

pub fn object_type<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, kind: WindowsObjectTypeKind, ) -> Result<WindowsObjectType<'a, Driver>, VmiError>

Returns a Windows object type for the given object kind.

§Notes

The object type is cached after the first read.

§Implementation Details
  • File corresponds to IoFileObjectType.
  • Job corresponds to PsJobType.
  • Key corresponds to CmKeyObjectType.
  • Process corresponds to PsProcessType.
  • Thread corresponds to PsThreadType.
  • Token corresponds to SeTokenObjectType.
  • Other types are not supported.
Source

pub fn object_root_directory<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<WindowsDirectoryObject<'a, Driver>, VmiError>

Returns the root directory object for the Windows kernel.

§Notes

The object root directory is cached after the first read.

§Implementation Details

Corresponds to ObpRootDirectoryObject symbol.

Returns the object header cookie used for obfuscating object types. Returns None if the cookie is not present in the kernel image.

§Notes

Windows 10 introduced a security feature that obfuscates the type of kernel objects by XORing the TypeIndex field in the object header with a random cookie value. This method fetches that cookie, which is essential for correctly interpreting object headers in memory.

The cookie is cached after the first read.

§Implementation Details

Corresponds to ObHeaderCookie symbol.

Source

pub fn object_attributes<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, object_attributes: Va, ) -> Result<WindowsObjectAttributes<'a, Driver>, VmiError>

Returns the Windows object attributes.

Source

pub fn lookup_object<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, path: impl AsRef<str>, ) -> Result<Option<WindowsObject<'a, Driver>>, VmiError>

Resolves an absolute object-namespace path to a named object.

Descends from the root directory (object_root_directory) one component at a time.

Returns Ok(None) when a component does not exist, or when an intermediate component resolves to something other than a Directory. The final component may be any object type.

Does not follow SymbolicLink objects.

Source

pub fn hives<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<impl Iterator<Item = Result<WindowsHive<'a, Driver>, VmiError>> + use<'a, Driver>, VmiError>

Returns an iterator over all loaded registry hives.

This method returns an iterator over all loaded registry hives. It reads the CmpHiveListHead symbol from the kernel image and iterates over the linked list of _CMHIVE structures representing each hive.

Source

pub fn lookup_key<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, path: impl AsRef<str>, ) -> Result<Option<WindowsKeyNode<'a, Driver>>, VmiError>

Resolves an absolute registry path against the loaded hive set.

Picks the hive whose HiveRootPath is the longest component-aligned case-insensitive prefix of path, strips that prefix, and forwards the remainder to WindowsHive::lookup.

Returns Ok(None) when no hive root prefixes the path or when the descent does not find the key.

Hives with an empty HiveRootPath match any input at depth zero, so they only win when no loaded hive owns a real prefix.

Does not follow HIVE_EXIT links into other hives.

Source

pub fn read_fast_ref( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Va, VmiError>

Reads an _EX_FAST_REF and returns the referenced object’s virtual address, with the low reference-count bits masked off.

Source

pub fn read_ansi_string_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from an _ANSI_STRING structure.

This method reads a native _ANSI_STRING structure which contains an ASCII/ANSI string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_ansi_string32_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from a 32-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_ansi_string64_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from a 64-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_ansi_string( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from an _ANSI_STRING structure.

This method reads a native _ANSI_STRING structure which contains an ASCII/ANSI string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_ansi_string32( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from a 32-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_ansi_string64( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from a 64-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_unicode_string_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u16>, VmiError>

Reads string from a _UNICODE_STRING structure.

This method reads a native _UNICODE_STRING structure which contains a UTF-16 string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_unicode_string32_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u16>, VmiError>

Reads string from a 32-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_unicode_string64_bytes( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<Vec<u16>, VmiError>

Reads string from a 64-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_unicode_string( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from a _UNICODE_STRING structure.

This method reads a native _UNICODE_STRING structure which contains a UTF-16 string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_unicode_string32( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from a 32-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_unicode_string64( vmi: VmiState<'_, WindowsOs<Driver>>, va: Va, ) -> Result<String, VmiError>

Reads string from a 64-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_ansi_string_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from an _ANSI_STRING structure.

This method reads a native _ANSI_STRING structure which contains an ASCII/ANSI string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_ansi_string32_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from a 32-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_ansi_string64_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u8>, VmiError>

Reads string of bytes from a 64-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_ansi_string_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from an _ANSI_STRING structure.

This method reads a native _ANSI_STRING structure which contains an ASCII/ANSI string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_ansi_string32_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from a 32-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_ansi_string64_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from a 64-bit version of _ANSI_STRING structure.

This method is specifically for reading _ANSI_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_unicode_string_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u16>, VmiError>

Reads string from a _UNICODE_STRING structure.

This method reads a native _UNICODE_STRING structure which contains a UTF-16 string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_unicode_string32_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u16>, VmiError>

Reads string from a 32-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_unicode_string64_bytes_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<Vec<u16>, VmiError>

Reads string from a 64-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn read_unicode_string_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from a _UNICODE_STRING structure.

This method reads a native _UNICODE_STRING structure which contains a UTF-16 string. The structure is read according to the current OS’s architecture (32-bit or 64-bit).

Source

pub fn read_unicode_string32_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from a 32-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 32-bit processes or WoW64 processes where pointers are 32 bits.

Source

pub fn read_unicode_string64_in( vmi: VmiState<'_, WindowsOs<Driver>>, ctx: impl Into<AccessContext>, ) -> Result<String, VmiError>

Reads string from a 64-bit version of _UNICODE_STRING structure.

This method is specifically for reading _UNICODE_STRING structures in 64-bit processes where pointers are 64 bits.

Source

pub fn linked_list<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, list_head: Va, offset: u64, ) -> Result<impl Iterator<Item = Result<Va, VmiError>> + use<'a, Driver>, VmiError>

Returns an iterator over a doubly-linked list of LIST_ENTRY structures.

This method is used to iterate over a doubly-linked list of LIST_ENTRY structures in memory. It returns an iterator that yields the virtual addresses of each LIST_ENTRY structure in the list.

Source

pub fn lock_pfn( vmi: VmiState<'_, WindowsOs<Driver>>, pfn: Gfn, ) -> Result<Option<u16>, VmiError>
where Driver: VmiWrite,

Increments the reference count of a Page Frame Number (PFN).

Source

pub fn unlock_pfn( vmi: VmiState<'_, WindowsOs<Driver>>, pfn: Gfn, ) -> Result<Option<u16>, VmiError>
where Driver: VmiWrite,

Decrements the reference count of a Page Frame Number (PFN).

Trait Implementations§

Source§

impl<Driver, T, Bridge> InjectorExecutionAdapter<KernelMode, T, Bridge> for WindowsOs<Driver>

Source§

type Handler = KernelInjectorHandler<Driver, T, Bridge>

The concrete handler type for this OS and execution mode.
Source§

impl<Driver, T, Bridge> InjectorExecutionAdapter<UserMode, T, Bridge> for WindowsOs<Driver>

Source§

type Handler = UserInjectorHandler<Driver, T, Bridge>

The concrete handler type for this OS and execution mode.
Source§

impl<Driver> OsAdapter for WindowsOs<Driver>
where Driver: VmiDriver<Architecture = Amd64> + VmiRead + VmiWrite,

Source§

fn prepare_function_call( &self, vmi: &VmiCore<<WindowsOs<Driver> as VmiOs>::Driver>, registers: &mut Registers, builder: CallBuilder, ) -> Result<(), VmiError>

Prepares registers and stack for a function call according to OS conventions.
Source§

impl<Driver> OsAdapter for WindowsOs<Driver>
where Driver: VmiRead, <Driver as VmiDriver>::Architecture: ArchAdapter<Driver> + ArchAdapter<Driver>,

Source§

type DebugSignature = CodeView

OS-specific identifier that locates its debug symbols. Read more
Source§

fn resolve_kernel_module( vmi: &VmiState<'_, WindowsOs<Driver>>, isr: &IsrCache, name: &str, ) -> Result<Option<Resolved<WindowsOs<Driver>>>, VmiError>

Source§

fn resolve_user_module( vmi: &VmiState<'_, WindowsOs<Driver>>, isr: &IsrCache, name: &str, process: impl ProcessPredicate<WindowsOs<Driver>>, ) -> Result<Option<Resolved<WindowsOs<Driver>>>, VmiError>

Source§

impl<Driver, Handler> VmiHandler<WindowsOs<Driver>> for Reactor<WindowsOs<Driver>, Handler>
where Driver: VmiFullDriver<Architecture = Amd64>, Handler: ReactorHandler<WindowsOs<Driver>>, <<Handler as ReactorHandler<WindowsOs<Driver>>>::Event as ReactorEvent>::Module: ReactorModule<WindowsOs<Driver>>,

Source§

type Output = Option<<Handler as ReactorHandler<WindowsOs<Driver>>>::Output>

The output type of the handler.
Source§

fn handle_event( &mut self, vmi: VmiContext<'_, WindowsOs<Driver>>, ) -> VmiEventResponse<<Driver as VmiDriver>::Architecture>

Called for each VMI event. Read more
Source§

fn cleanup(&mut self, vmi: &VmiSession<'_, WindowsOs<Driver>>)

Called once before the session tears down monitoring. Read more
Source§

fn poll( &self, ) -> Option<<Reactor<WindowsOs<Driver>, Handler> as VmiHandler<WindowsOs<Driver>>>::Output>

Checks if the handler has completed. Read more
Source§

fn handle_timeout(&mut self, _session: &VmiSession<'_, Os>)

Called when the event loop times out waiting for the next event. Read more
Source§

fn handle_interrupted(&mut self, _session: &VmiSession<'_, Os>)

Called when the event loop is interrupted by a signal. Read more
Source§

impl<Driver> VmiOs for WindowsOs<Driver>
where Driver: VmiRead, <Driver as VmiDriver>::Architecture: ArchAdapter<Driver>,

Source§

fn kpti_enabled(vmi: VmiState<'_, WindowsOs<Driver>>) -> Result<bool, VmiError>

Checks if Kernel Virtual Address Shadow (KVA Shadow) is enabled.

KVA Shadow is a security feature introduced in Windows 10 that mitigates Meltdown and Spectre vulnerabilities by isolating kernel memory from user-mode processes.

§Notes

This value is cached after the first read.

§Implementation Details

Corresponds to KiKvaShadow symbol.

Source§

fn modules<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<impl Iterator<Item = Result<<WindowsOs<Driver> as VmiOs>::Module<'a>, VmiError>> + use<'a, Driver>, VmiError>

Returns an iterator over all loaded Windows Driver modules.

This method returns an iterator over all loaded Windows Driver modules. It reads the PsLoadedModuleList symbol from the kernel image and iterates over the linked list of KLDR_DATA_TABLE_ENTRY structures representing each loaded module.

Source§

fn processes<'a>( vmi: VmiState<'a, WindowsOs<Driver>>, ) -> Result<impl Iterator<Item = Result<<WindowsOs<Driver> as VmiOs>::Process<'a>, VmiError>> + use<'a, Driver>, VmiError>

Returns an iterator over all Windows processes.

This method returns an iterator over all Windows processes. It reads the PsActiveProcessHead symbol from the kernel image and iterates over the linked list of EPROCESS structures representing each process.

Source§

fn current_process( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<<WindowsOs<Driver> as VmiOs>::Process<'_>, VmiError>

Returns the current process.

Source§

fn system_process( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<<WindowsOs<Driver> as VmiOs>::Process<'_>, VmiError>

Returns the system process.

The system process is the first process created by the Windows kernel during system initialization. It is the parent process of all other processes in the system.

Source§

fn current_thread( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<<WindowsOs<Driver> as VmiOs>::Thread<'_>, VmiError>

Returns the current thread.

Source§

type Architecture = <Driver as VmiDriver>::Architecture

The architecture.
Source§

type Driver = Driver

The driver.
Source§

type Process<'a> = WindowsProcess<'a, Driver>

The process type.
Source§

type Thread<'a> = WindowsThread<'a, Driver>

The thread type.
Source§

type Image<'a> = WindowsImage<'a, Driver>

The image type.
Source§

type Module<'a> = WindowsModule<'a, Driver>

The kernel module type.
Source§

type UserModule<'a> = WindowsUserModule<'a, Driver>

The user-mode module type.
Source§

type Region<'a> = WindowsRegion<'a, Driver>

The memory region type.
Source§

type Mapped<'a> = WindowsControlArea<'a, Driver>

The memory mapped region type.
Source§

fn kernel_image_base( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Va, VmiError>

Retrieves the base address of the kernel image. Read more
Source§

fn kernel_information_string( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<String, VmiError>

Retrieves an implementation-specific string containing kernel information. Read more
Source§

fn process( vmi: VmiState<'_, WindowsOs<Driver>>, process: ProcessObject, ) -> Result<<WindowsOs<Driver> as VmiOs>::Process<'_>, VmiError>

Returns the process corresponding to the given process object.
Source§

fn thread( vmi: VmiState<'_, WindowsOs<Driver>>, thread: ThreadObject, ) -> Result<<WindowsOs<Driver> as VmiOs>::Thread<'_>, VmiError>

Returns the thread corresponding to the given thread object.
Source§

fn image( vmi: VmiState<'_, WindowsOs<Driver>>, image_base: Va, ) -> Result<<WindowsOs<Driver> as VmiOs>::Image<'_>, VmiError>

Returns the image corresponding to the given base address.
Source§

fn module( vmi: VmiState<'_, WindowsOs<Driver>>, module: Va, ) -> Result<<WindowsOs<Driver> as VmiOs>::Module<'_>, VmiError>

Returns the kernel module corresponding to the given base address.
Source§

fn user_module( vmi: VmiState<'_, WindowsOs<Driver>>, module: Va, root: Pa, ) -> Result<<WindowsOs<Driver> as VmiOs>::UserModule<'_>, VmiError>

Returns the user-mode module corresponding to the given base address.
Source§

fn region( vmi: VmiState<'_, WindowsOs<Driver>>, region: Va, ) -> Result<<WindowsOs<Driver> as VmiOs>::Region<'_>, VmiError>

Returns the memory region corresponding to the given address. Read more
Source§

fn syscall_argument( vmi: VmiState<'_, WindowsOs<Driver>>, index: u64, ) -> Result<u64, VmiError>

Retrieves a specific syscall argument according to the system call ABI. Read more
Source§

fn function_argument( vmi: VmiState<'_, WindowsOs<Driver>>, index: u64, ) -> Result<u64, VmiError>

Retrieves a specific function argument according to the calling convention of the operating system. Read more
Source§

fn function_return_value( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<u64, VmiError>

Retrieves the return value of a function. Read more
Source§

fn last_error( vmi: VmiState<'_, WindowsOs<Driver>>, ) -> Result<Option<u32>, VmiError>

Retrieves the last error value. Read more

Auto Trait Implementations§

§

impl<Driver> !Freeze for WindowsOs<Driver>

§

impl<Driver> !RefUnwindSafe for WindowsOs<Driver>

§

impl<Driver> !Sync for WindowsOs<Driver>

§

impl<Driver> Send for WindowsOs<Driver>
where Driver: Send,

§

impl<Driver> Unpin for WindowsOs<Driver>
where Driver: Unpin,

§

impl<Driver> UnsafeUnpin for WindowsOs<Driver>

§

impl<Driver> UnwindSafe for WindowsOs<Driver>
where Driver: UnwindSafe,

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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: Sized + 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: Sized + 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<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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> VmiOsExt for T
where T: VmiOs,

Source§

fn find_process<'a>( vmi: VmiState<'a, Self>, predicate: impl ProcessPredicate<Self>, ) -> Result<Option<Self::Process<'a>>, VmiError>

Returns the first process matching predicate, or Ok(None) if no live process matches.
Source§

fn filter_processes<'a>( vmi: VmiState<'a, Self>, predicate: impl ProcessPredicate<Self>, ) -> Result<impl Iterator<Item = Result<Self::Process<'a>, VmiError>>, VmiError>

Returns an iterator over the processes matching predicate.
Source§

fn find_module<'a>( vmi: VmiState<'a, Self>, predicate: impl ModulePredicate<Self>, ) -> Result<Option<Self::Module<'a>>, VmiError>

Returns the first kernel module matching predicate, or Ok(None) if no loaded module matches.
Source§

fn filter_modules<'a>( vmi: VmiState<'a, Self>, predicate: impl ModulePredicate<Self>, ) -> Result<impl Iterator<Item = Result<Self::Module<'a>, VmiError>>, VmiError>

Returns an iterator over the kernel modules matching predicate.
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