use isr::cache::IsrCache;
use vmi::{
VcpuId, VmiCore, VmiError, VmiSession, VmiState, VmiVa as _,
arch::amd64::Amd64,
driver::kdmp::VmiKdmpDriver,
os::{
VmiOsMapped as _, VmiOsModule as _, VmiOsProcess as _, VmiOsRegion as _, VmiOsRegionKind,
VmiOsThread as _,
windows::{WindowsDirectoryObject, WindowsOs, WindowsOsExt, WindowsProcess},
},
};
type Arch = Amd64;
type Driver = VmiKdmpDriver<Arch>;
fn handle_error(err: VmiError) -> Result<String, VmiError> {
match err {
VmiError::Translation(pf) => Ok(format!("PF({pf:?})")),
_ => Err(err),
}
}
fn enumerate_kernel_modules(vmi: &VmiState<WindowsOs<Driver>>) -> Result<(), VmiError> {
for module in vmi.os().modules()? {
let module = module?;
let module_va = module.va();
let base_address = module.base_address()?; let size = module.size()?; let name = module.name()?; let full_name = match module.full_name() {
Ok(full_name) => full_name,
Err(err) => handle_error(err)?,
};
println!("Module @ {module_va}");
println!(" Base Address: {base_address}");
println!(" Size: {size}");
println!(" Name: {name}");
println!(" Full Name: {full_name}");
}
Ok(())
}
fn enumerate_directory_object(
directory_object: &WindowsDirectoryObject<Driver>,
level: usize,
) -> Result<(), VmiError> {
for object in directory_object.iter()? {
for _ in 0..level {
print!(" ");
}
let object = match object {
Ok(object) => object,
Err(err) => {
println!("{}", handle_error(err)?);
continue;
}
};
let object_va = object.va();
let type_kind = match object.type_kind() {
Ok(Some(typ)) => format!("{typ:?}"),
Ok(None) => String::from("<unknown>"),
Err(err) => handle_error(err)?,
};
print!("{type_kind}: ");
let name = match object.full_path() {
Ok(Some(name)) => name,
Ok(None) => String::from("<unnamed>"),
Err(err) => handle_error(err)?,
};
println!("{name} (Object: {object_va})");
if let Ok(Some(next)) = object.as_directory() {
enumerate_directory_object(&next, level + 1)?;
}
}
Ok(())
}
fn enumerate_handle_table(process: &WindowsProcess<Driver>) -> Result<(), VmiError> {
const OBJ_PROTECT_CLOSE: u32 = 0x00000001;
const OBJ_INHERIT: u32 = 0x00000002;
const OBJ_AUDIT_OBJECT_CLOSE: u32 = 0x00000004;
static LABEL_PROTECTED: [&str; 2] = ["", " (Protected)"];
static LABEL_INHERIT: [&str; 2] = ["", " (Inherit)"];
static LABEL_AUDIT: [&str; 2] = ["", " (Audit)"];
let handle_table = match process.handle_table() {
Ok(Some(handle_table)) => handle_table,
Ok(None) => {
println!(" (No handle table)");
return Ok(());
}
Err(err) => {
tracing::error!(%err, "Failed to get handle table");
return Ok(());
}
};
for handle_entry in handle_table.iter()? {
let (handle, entry) = match handle_entry {
Ok(entry) => entry,
Err(err) => {
println!("Failed to get handle entry: {}", handle_error(err)?);
continue;
}
};
let attributes = match entry.attributes() {
Ok(attributes) => attributes,
Err(err) => {
println!("Failed to get attributes: {}", handle_error(err)?);
continue;
}
};
let granted_access = match entry.granted_access() {
Ok(granted_access) => granted_access,
Err(err) => {
println!("Failed to get granted access: {}", handle_error(err)?);
continue;
}
};
let object = match entry.object() {
Ok(Some(object)) => object,
Ok(None) => {
println!("<NULL>");
continue;
}
Err(err) => {
println!("Failed to get object: {}", handle_error(err)?);
continue;
}
};
let type_name = match object.type_name() {
Ok(type_name) => type_name,
Err(err) => handle_error(err)?,
};
let full_path = match object.full_path() {
Ok(Some(path)) => path,
Ok(None) => String::from("<no-path>"),
Err(err) => handle_error(err)?,
};
println!(
" {:04x}: Object: {:x} GrantedAccess: {:08x}{}{}{} Entry: {}",
handle,
object.va().0,
granted_access,
LABEL_PROTECTED[((attributes & OBJ_PROTECT_CLOSE) != 0) as usize],
LABEL_INHERIT[((attributes & OBJ_INHERIT) != 0) as usize],
LABEL_AUDIT[((attributes & OBJ_AUDIT_OBJECT_CLOSE) != 0) as usize],
entry.va(),
);
println!(" Type: {type_name}, Path: {full_path}");
}
Ok(())
}
fn enumerate_regions(process: &WindowsProcess<Driver>) -> Result<(), VmiError> {
for region in process.regions()? {
let region = region?;
let region_va = region.va();
let start = region.start()?;
let end = region.end()?;
let protection = region.protection()?;
let kind = region.kind()?;
print!(" Region @ {region_va}: {start}-{end} {protection:?}");
match &kind {
VmiOsRegionKind::Private => println!(" Private"),
VmiOsRegionKind::MappedImage(mapped) => {
let path = match mapped.path() {
Ok(Some(path)) => path,
Ok(None) => String::from("<Pagefile>"),
Err(err) => handle_error(err)?,
};
println!(" Mapped (Exe): {path}");
}
VmiOsRegionKind::MappedData(mapped) => {
let path = match mapped.path() {
Ok(Some(path)) => path,
Ok(None) => String::from("<Pagefile>"),
Err(err) => handle_error(err)?,
};
println!(" Mapped: {path}");
}
}
}
Ok(())
}
fn enumerate_threads(process: &WindowsProcess<Driver>) -> Result<(), VmiError> {
for thread in process.threads()? {
let thread = thread?;
let tid = thread.id()?;
let object = thread.object()?;
println!(" Thread @ {object}, TID: {tid}");
}
Ok(())
}
fn print_process_parameters(process: &WindowsProcess<Driver>) -> Result<(), VmiError> {
let peb = match process.peb() {
Ok(Some(peb)) => peb,
Ok(None) => {
println!(" (No PEB)");
return Ok(());
}
Err(err) => {
println!("Failed to get PEB: {}", handle_error(err)?);
return Ok(());
}
};
let current_directory = match peb.current_directory() {
Ok(current_directory) => current_directory,
Err(err) => handle_error(err)?,
};
let dll_path = match peb.dll_path() {
Ok(dll_path) => dll_path,
Err(err) => handle_error(err)?,
};
let image_path_name = match peb.image_path_name() {
Ok(image_path_name) => image_path_name,
Err(err) => handle_error(err)?,
};
let command_line = match peb.command_line() {
Ok(command_line) => command_line,
Err(err) => handle_error(err)?,
};
println!(" Current Directory: {current_directory}");
println!(" DLL Path: {dll_path}");
println!(" Image Path Name: {image_path_name}");
println!(" Command Line: {command_line}");
Ok(())
}
fn enumerate_processes(vmi: &VmiState<WindowsOs<Driver>>) -> Result<(), VmiError> {
for process in vmi.os().processes()? {
let process = process?;
let pid = process.id()?; let object = process.object()?; let name = process.name()?; let session = process.session()?;
println!("Process @ {object}, PID: {pid}");
println!(" Name: {name}");
if let Some(session) = session {
println!(" Session: {}", session.id()?); }
println!(" Threads:");
enumerate_threads(&process)?;
println!(" Regions:");
enumerate_regions(&process)?;
println!(" PEB:");
print_process_parameters(&process)?;
println!(" Handles:");
enumerate_handle_table(&process)?;
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_ansi(false)
.init();
let args = std::env::args().collect::<Vec<_>>();
if args.len() != 2 {
eprintln!("Usage: {} <dump-file>", args[0]);
std::process::exit(1);
}
let dump_file = &args[1];
let driver = Driver::new(dump_file)?;
let core = VmiCore::new(driver)?;
let registers = core.registers(VcpuId(0))?;
let kernel_info = WindowsOs::find_kernel(&core, ®isters)?.expect("kernel information");
tracing::info!(?kernel_info, "Kernel information");
let isr = IsrCache::new("cache")?;
let entry = isr.entry_from_codeview(kernel_info.codeview)?;
let profile = entry.profile()?;
tracing::info!("Creating VMI session");
let os = WindowsOs::<Driver>::with_kernel_base(&profile, kernel_info.base_address)?;
let session = VmiSession::new(&core, &os);
let vmi = session.with_registers(®isters);
let root_directory = vmi.os().object_root_directory()?;
println!("Kernel Modules:");
println!("=================================================");
enumerate_kernel_modules(&vmi)?;
println!("Object Tree (root directory: {}):", root_directory.va());
println!("=================================================");
enumerate_directory_object(&root_directory, 0)?;
println!("Processes:");
println!("=================================================");
enumerate_processes(&vmi)?;
Ok(())
}