use std::collections::HashMap;
use std::ffi::{CStr, CString, c_char, c_int};
use std::fmt;
use anyhow::{anyhow, Context};
use serde::{Serialize, Deserialize};
pub type ResultEx<T> = anyhow::Result<T>;
pub const FLAG_NOCACHE : u64 = 0x0001;
pub const FLAG_ZEROPAD_ON_FAIL : u64 = 0x0002;
pub const FLAG_FORCECACHE_READ : u64 = 0x0008;
pub const FLAG_NOPAGING : u64 = 0x0010;
pub const FLAG_NOPAGING_IO : u64 = 0x0020;
pub const FLAG_NOCACHEPUT : u64 = 0x0100;
pub const FLAG_CACHE_RECENT_ONLY : u64 = 0x0200;
pub const FLAG_NO_PREDICTIVE_READ : u64 = 0x0400;
pub const FLAG_FORCECACHE_READ_DISABLE : u64 = 0x0800;
pub const FLAG_SCATTER_PREPAREEX_NOMEMZERO : u64 = 0x1000;
pub const CONFIG_OPT_CORE_PRINTF_ENABLE : u64 = 0x4000000100000000;
pub const CONFIG_OPT_CORE_VERBOSE : u64 = 0x4000000200000000;
pub const CONFIG_OPT_CORE_VERBOSE_EXTRA : u64 = 0x4000000300000000;
pub const CONFIG_OPT_CORE_VERBOSE_EXTRA_TLP : u64 = 0x4000000400000000;
pub const CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS : u64 = 0x4000000800000000;
pub const CONFIG_OPT_CORE_LEECHCORE_HANDLE : u64 = 0x4000001000000000;
pub const CONFIG_OPT_CORE_VMM_ID : u64 = 0x4000002000000000;
pub const CONFIG_OPT_CORE_SYSTEM : u64 = 0x2000000100000000;
pub const CONFIG_OPT_CORE_MEMORYMODEL : u64 = 0x2000000200000000;
pub const CONFIG_OPT_CONFIG_IS_REFRESH_ENABLED : u64 = 0x2000000300000000;
pub const CONFIG_OPT_CONFIG_TICK_PERIOD : u64 = 0x2000000400000000;
pub const CONFIG_OPT_CONFIG_READCACHE_TICKS : u64 = 0x2000000500000000;
pub const CONFIG_OPT_CONFIG_TLBCACHE_TICKS : u64 = 0x2000000600000000;
pub const CONFIG_OPT_CONFIG_PROCCACHE_TICKS_PARTIAL : u64 = 0x2000000700000000;
pub const CONFIG_OPT_CONFIG_PROCCACHE_TICKS_TOTAL : u64 = 0x2000000800000000;
pub const CONFIG_OPT_CONFIG_VMM_VERSION_MAJOR : u64 = 0x2000000900000000;
pub const CONFIG_OPT_CONFIG_VMM_VERSION_MINOR : u64 = 0x2000000A00000000;
pub const CONFIG_OPT_CONFIG_VMM_VERSION_REVISION : u64 = 0x2000000B00000000;
pub const CONFIG_OPT_CONFIG_STATISTICS_FUNCTIONCALL : u64 = 0x2000000C00000000;
pub const CONFIG_OPT_CONFIG_IS_PAGING_ENABLED : u64 = 0x2000000D00000000;
pub const CONFIG_OPT_CONFIG_DEBUG : u64 = 0x2000000E00000000;
pub const CONFIG_OPT_WIN_VERSION_MAJOR : u64 = 0x2000010100000000;
pub const CONFIG_OPT_WIN_VERSION_MINOR : u64 = 0x2000010200000000;
pub const CONFIG_OPT_WIN_VERSION_BUILD : u64 = 0x2000010300000000;
pub const CONFIG_OPT_WIN_SYSTEM_UNIQUE_ID : u64 = 0x2000010400000000;
pub const CONFIG_OPT_FORENSIC_MODE : u64 = 0x2000020100000000;
pub const CONFIG_OPT_REFRESH_ALL : u64 = 0x2001ffff00000000;
pub const CONFIG_OPT_REFRESH_FREQ_MEM : u64 = 0x2001100000000000;
pub const CONFIG_OPT_REFRESH_FREQ_MEM_PARTIAL : u64 = 0x2001000200000000;
pub const CONFIG_OPT_REFRESH_FREQ_TLB : u64 = 0x2001080000000000;
pub const CONFIG_OPT_REFRESH_FREQ_TLB_PARTIAL : u64 = 0x2001000400000000;
pub const CONFIG_OPT_REFRESH_FREQ_FAST : u64 = 0x2001040000000000;
pub const CONFIG_OPT_REFRESH_FREQ_MEDIUM : u64 = 0x2001000100000000;
pub const CONFIG_OPT_REFRESH_FREQ_SLOW : u64 = 0x2001001000000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_HEAP_ALLOC : u64 = 0x2003000100000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_KOBJECT : u64 = 0x2003000200000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_NET : u64 = 0x2003000300000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_PFN : u64 = 0x2003000400000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_PHYSMEMMAP : u64 = 0x2003000500000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_POOL : u64 = 0x2003000600000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_REGISTRY : u64 = 0x2003000700000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_SERVICES : u64 = 0x2003000800000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_THREADCS : u64 = 0x2003000900000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_USER : u64 = 0x2003000A00000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_VM : u64 = 0x2003000B00000000;
pub const VMMDLL_OPT_REFRESH_SPECIFIC_PROCESS : u64 = 0x2002000300000000;
pub const CONFIG_OPT_PROCESS_DTB : u64 = 0x2002000100000000;
pub const CONFIG_OPT_PROCESS_DTB_FAST_LOWINTEGRITY : u64 = 0x2002000200000000;
pub const PLUGIN_NOTIFY_VERBOSITYCHANGE : u32 = 0x01;
pub const PLUGIN_NOTIFY_REFRESH_FAST : u32 = 0x05;
pub const PLUGIN_NOTIFY_REFRESH_MEDIUM : u32 = 0x02;
pub const PLUGIN_NOTIFY_REFRESH_SLOW : u32 = 0x04;
pub const PLUGIN_NOTIFY_FORENSIC_INIT : u32 = 0x01000100;
pub const PLUGIN_NOTIFY_FORENSIC_INIT_COMPLETE : u32 = 0x01000200;
pub const PLUGIN_NOTIFY_VM_ATTACH_DETACH : u32 = 0x01000400;
#[allow(dead_code)]
#[derive(Debug)]
pub struct Vmm<'a> {
path_lc : String,
path_vmm : String,
native : VmmNative,
parent_vmm : Option<&'a Vmm<'a>>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmLogLevel {
_1Critical,
_2Warning,
_3Info,
_4Verbose,
_5Debug,
_6Trace,
_7None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapNetEntry {
pub pid : u32,
pub state : u32,
pub address_family : u16,
pub src_is_valid : bool,
pub src_port : u16,
pub src_addr_raw : [u8; 16],
pub src_str : String,
pub dst_is_valid : bool,
pub dst_port : u16,
pub dst_addr_raw : [u8; 16],
pub dst_str : String,
pub va_object : u64,
pub filetime : u64,
pub pool_tag : u32,
pub desc : String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmMapPfnType {
Zero,
Free,
Standby,
Modified,
ModifiedNoWrite,
Bad,
Active,
Transition,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmMapPfnTypeExtended {
Unknown,
Unused,
ProcessPrivate,
PageTable,
LargePage,
DriverLocked,
Shareable,
File,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmMapPfnEntry {
pub pfn : u32,
pub location : VmmMapPfnType,
pub is_prototype : bool,
pub color : u32,
pub is_extended : bool,
pub tp_ex : VmmMapPfnTypeExtended,
pub pid : u32,
pub ptes : [u32; 5], pub va : u64,
pub va_pte : u64,
pub pte_original : u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapKDeviceEntry {
pub va : u64,
pub depth : u32,
pub device_type : u32,
pub device_type_name : String,
pub va_driver_object : u64,
pub va_attached_device : u64,
pub va_file_system_device : u64,
pub volume_info : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapKDriverEntry {
pub va : u64,
pub va_driver_start : u64,
pub cb_driver_size : u64,
pub va_device_object : u64,
pub name : String,
pub path : String,
pub service_key_name : String,
pub major_function : [u64; 28],
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapKObjectEntry {
pub va : u64,
pub va_parent : u64,
pub children : Vec<u64>,
pub name : String,
pub object_type : String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmMapPoolEntry {
pub va : u64,
pub cb : u32,
pub tag : u32,
pub is_alloc : bool,
pub tp_pool : u8, pub tp_subsegment : u8, }
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmMapMemoryEntry {
pub pa : u64,
pub cb : u64
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapServiceEntry {
pub ordinal : u32,
pub va_object : u64,
pub pid : u32,
pub start_type : u32,
pub service_type : u32,
pub current_state : u32,
pub controls_accepted : u32,
pub win32_exit_code : u32,
pub service_specific_exit_code : u32,
pub check_point : u32,
pub wait_hint : u32,
pub name : String,
pub name_display : String,
pub path : String,
pub user_type : String,
pub user_account : String,
pub image_path : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapUserEntry {
pub user : String,
pub sid : String,
pub va_reg_hive : u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmMapVirtualMachineEntry {
h_vmm : usize,
h_vm : usize,
pub name : String,
pub gpa_max : u64,
pub tp_vm : u32,
pub is_active : bool,
pub is_readonly : bool,
pub is_physicalonly : bool,
pub partition_id : u32,
pub guest_os_version_build : u32,
pub guest_tp_system : u32,
pub parent_mount_id : u32,
pub vmmem_pid : u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmVfsEntry {
pub name : String,
pub is_directory : bool,
pub size : u64,
}
impl Vmm<'_> {
pub fn new<'a>(vmm_lib_path : &str, args: &Vec<&str>) -> ResultEx<Vmm<'a>> {
return crate::impl_new(vmm_lib_path, None, 0, args);
}
pub fn new_from_leechcore<'a>(leechcore_existing : &LeechCore, args: &Vec<&str>) -> ResultEx<Vmm<'a>> {
return crate::impl_new_from_leechcore(leechcore_existing, args);
}
pub fn new_from_virtual_machine<'a>(vmm_parent : &'a Vmm, vm_entry : &VmmMapVirtualMachineEntry) -> ResultEx<Vmm<'a>> {
return impl_new_from_virtual_machine(vmm_parent, vm_entry);
}
pub fn get_leechcore(&self) -> ResultEx<LeechCore> {
return self.impl_get_leechcore();
}
pub fn process_from_pid(&self, pid : u32) -> ResultEx<VmmProcess> {
return self.impl_process_from_pid(pid);
}
pub fn process_from_name(&self, process_name : &str) -> ResultEx<VmmProcess> {
return self.impl_process_from_name(process_name);
}
pub fn process_list(&self) -> ResultEx<Vec<VmmProcess>> {
return self.impl_process_list();
}
pub fn process_map(&self) -> ResultEx<HashMap<u32, VmmProcess>> {
return Ok(self.impl_process_list()?.into_iter().map(|s| (s.pid, s)).collect());
}
pub fn get_config(&self, config_id : u64) -> ResultEx<u64> {
return self.impl_get_config(config_id);
}
pub fn set_config(&self, config_id : u64, config_value : u64) -> ResultEx<()> {
return self.impl_set_config(config_id, config_value);
}
pub fn kernel(&self) -> VmmKernel {
return VmmKernel { vmm : &self };
}
pub fn log(&self, log_level : &VmmLogLevel, log_message : &str) {
self.impl_log(VMMDLL_MID_RUST, log_level, log_message);
}
pub fn map_memory(&self) -> ResultEx<Vec<VmmMapMemoryEntry>> {
return self.impl_map_memory();
}
pub fn map_net(&self) -> ResultEx<Vec<VmmMapNetEntry>> {
return self.impl_map_net();
}
pub fn map_pfn(&self, pfns : &Vec<u32>, is_extended : bool) -> ResultEx<Vec<VmmMapPfnEntry>> {
return self.impl_map_pfn(pfns, is_extended);
}
pub fn map_kdevice(&self) -> ResultEx<Vec<VmmMapKDeviceEntry>> {
return self.impl_map_kdevice();
}
pub fn map_kdriver(&self) -> ResultEx<Vec<VmmMapKDriverEntry>> {
return self.impl_map_kdriver();
}
pub fn map_kobject(&self) -> ResultEx<Vec<VmmMapKObjectEntry>> {
return self.impl_map_kobject();
}
pub fn map_pool(&self, is_bigpool_only : bool) -> ResultEx<Vec<VmmMapPoolEntry>> {
return self.impl_map_pool(is_bigpool_only);
}
pub fn map_service(&self) -> ResultEx<Vec<VmmMapServiceEntry>> {
return self.impl_map_service();
}
pub fn map_user(&self) -> ResultEx<Vec<VmmMapUserEntry>> {
return self.impl_map_user();
}
pub fn map_virtual_machine(&self) -> ResultEx<Vec<VmmMapVirtualMachineEntry>> {
return self.impl_map_virtual_machine();
}
pub fn mem_read(&self, pa : u64, size : usize) -> ResultEx<Vec<u8>> {
return self.impl_mem_read(u32::MAX, pa, size, 0);
}
pub fn mem_read_ex(&self, pa : u64, size : usize, flags : u64) -> ResultEx<Vec<u8>> {
return self.impl_mem_read(u32::MAX, pa, size, flags);
}
pub fn mem_read_into(&self, pa : u64, flags : u64, data : &mut [u8]) -> ResultEx<usize> {
return self.impl_mem_read_into(u32::MAX, pa, flags, data);
}
pub fn mem_read_as<T>(&self, pa : u64, flags : u64) -> ResultEx<T> {
return self.impl_mem_read_as(u32::MAX, pa, flags);
}
pub fn mem_scatter(&self, flags : u64) -> ResultEx<VmmScatterMemory> {
return self.impl_mem_scatter(u32::MAX, flags);
}
pub fn mem_write(&self, pa : u64, data : &[u8]) -> ResultEx<()> {
return self.impl_mem_write(u32::MAX, pa, data);
}
pub fn mem_write_as<T>(&self, pa : u64, data : &T) -> ResultEx<()> {
return self.impl_mem_write_as(u32::MAX, pa, data);
}
pub fn vfs_list(&self, path : &str) -> ResultEx<Vec<VmmVfsEntry>> {
return self.impl_vfs_list(path);
}
pub fn vfs_read(&self, filename : &str, size : u32, offset : u64) -> ResultEx<Vec<u8>> {
return self.impl_vfs_read(filename, size, offset);
}
pub fn vfs_write(&self, filename : &str, data : Vec<u8>, offset : u64) {
return self.impl_vfs_write(filename, &data, offset);
}
pub fn reg_hive_list(&self) -> ResultEx<Vec<VmmRegHive>> {
return self.impl_reg_hive_list();
}
pub fn reg_key(&self, path : &str) -> ResultEx<VmmRegKey> {
return self.impl_reg_key(path);
}
pub fn reg_value(&self, path : &str) -> ResultEx<VmmRegValue> {
return self.impl_reg_value(path);
}
pub fn search(&self, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmSearch> {
return VmmSearch::impl_new(&self, u32::MAX, addr_min, addr_max, num_results_max, flags);
}
pub fn search_yara(&self, rules : Vec<&str>, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmYara> {
return VmmYara::impl_new(&self, rules, u32::MAX, addr_min, addr_max, num_results_max, flags);
}
}
impl VmmMapPoolEntry {
pub fn tag_to_string(&self) -> String {
let tag_chars = [((self.tag >> 0) & 0xff) as u8, ((self.tag >> 8) & 0xff) as u8, ((self.tag >> 16) & 0xff) as u8, ((self.tag >> 24) & 0xff) as u8];
return String::from_utf8_lossy(&tag_chars).to_string();
}
}
#[derive(Clone, Copy, Debug)]
pub struct VmmKernel<'a> {
vmm : &'a Vmm<'a>,
}
impl VmmKernel<'_> {
pub fn build(&self) -> u32 {
return self.vmm.get_config(CONFIG_OPT_WIN_VERSION_BUILD).unwrap_or_default().try_into().unwrap_or_default();
}
pub fn process(&self) -> VmmProcess {
return VmmProcess { vmm : self.vmm, pid : 4 };
}
pub fn pdb(&self) -> VmmPdb {
return VmmPdb { vmm : self.vmm, module : String::from("nt") };
}
}
#[derive(Clone, Debug)]
pub struct VmmPdb<'a> {
vmm : &'a Vmm<'a>,
pub module : String,
}
impl VmmPdb<'_> {
pub fn symbol_name_from_address(&self, va_or_offset : u64) -> ResultEx<(String, u32)> {
return self.impl_symbol_name_from_address(va_or_offset);
}
pub fn symbol_address_from_name(&self, symbol_name : &str) -> ResultEx<u64> {
return self.impl_symbol_address_from_name(symbol_name);
}
pub fn type_size(&self, type_name : &str) -> ResultEx<u32> {
return self.impl_type_size(type_name);
}
pub fn type_child_offset(&self, type_name : &str, type_child_name : &str) -> ResultEx<u32> {
return self.impl_type_child_offset(type_name, type_child_name);
}
}
#[derive(Debug)]
pub struct VmmScatterMemory<'a> {
vmm : &'a Vmm<'a>,
hs : usize,
pid : u32,
flags : u32,
is_scatter_ex : bool,
}
impl <'a> VmmScatterMemory<'a> {
pub fn prepare_ex(&mut self, data_to_read : &'a mut (u64, Vec<u8>, u32)) -> ResultEx<()> {
return self.impl_prepare_ex(data_to_read);
}
pub fn prepare_ex_as<T>(&mut self, data_to_read : &'a mut (u64, T, u32)) -> ResultEx<()> {
return self.impl_prepare_ex_as(data_to_read);
}
}
impl VmmScatterMemory<'_> {
pub fn prepare(&self, va : u64, size : usize) -> ResultEx<()> {
return self.impl_prepare(va, size);
}
pub fn prepare_as<T>(&self, va : u64) -> ResultEx<()> {
return self.impl_prepare(va, std::mem::size_of::<T>());
}
pub fn prepare_write(&self, va : u64, data : &[u8]) -> ResultEx<()> {
return self.impl_prepare_write(va, data);
}
pub fn prepare_write_as<T>(&self, va : u64, data : &T) -> ResultEx<()> {
return self.impl_prepare_write_as(va, data);
}
pub fn execute(&self) -> ResultEx<()> {
return self.impl_execute();
}
pub fn read(&self, va : u64, size : usize) -> ResultEx<Vec<u8>> {
return self.impl_read(va, size);
}
pub fn read_as<T>(&self, va : u64) -> ResultEx<T> {
return self.impl_read_as(va);
}
pub fn read_into(&self, va : u64, data : &mut [u8]) -> ResultEx<usize> {
return self.impl_read_into(va, data);
}
pub fn clear(&self) -> ResultEx<()> {
return self.impl_clear();
}
}
#[derive(Clone, Copy, Debug)]
pub struct VmmProcess<'a> {
pub vmm : &'a Vmm<'a>,
pub pid : u32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmIntegrityLevelType {
Unknown,
Untrusted,
Low,
Medium,
MediumPlus,
High,
System,
Protected,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmMemoryModelType {
NA,
X86,
X86PAE,
X64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmSystemType {
UnknownPhysical,
UnknownX64,
WindowsX64,
UnknownX86,
WindowsX86,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessInfo {
pub pid : u32,
pub ppid : u32,
pub name : String,
pub name_long : String,
pub tp_system : VmmSystemType,
pub tp_memorymodel : VmmMemoryModelType,
pub is_user_mode : bool,
pub state : u32,
pub pa_dtb : u64,
pub pa_dtb_user : u64,
pub va_eprocess : u64,
pub va_peb : u64,
pub is_wow64 : bool,
pub va_peb32 : u32,
pub session_id : u32,
pub luid : u64,
pub sid : String,
pub integrity_level : VmmIntegrityLevelType,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmProcessMapDirectoryEntry {
pub pid : u32,
pub name : &'static str,
pub virtual_address : u32,
pub size : u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapEatEntry {
pub pid : u32,
pub va_function : u64,
pub ordinal : u32,
pub function : String,
pub forwarded_function : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapHandleEntry {
pub pid : u32,
pub va_object : u64,
pub handle_id : u32,
pub granted_access : u32,
pub type_index : u32,
pub handle_count : u64,
pub pointer_count : u64,
pub va_object_create_info : u64,
pub va_security_descriptor : u64,
pub handle_pid : u32,
pub pool_tag : u32,
pub info : String,
pub tp : String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmProcessMapHeapType {
NA,
NtHeap,
SegmentHeap,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmProcessMapHeapEntry {
pub pid : u32,
pub tp : VmmProcessMapHeapType,
pub is_32 : bool,
pub index : u32,
pub number : u32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmProcessMapHeapAllocType {
NA,
NtHeap,
NtLFH,
NtLarge,
NtNA,
SegVS,
SegLFH,
SegLarge,
SegNA,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmProcessMapHeapAllocEntry {
pub pid : u32,
pub va : u64,
pub size : u32,
pub tp : VmmProcessMapHeapAllocType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapIatEntry {
pub pid : u32,
pub va_function : u64,
pub function : String,
pub module : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapModuleDebugEntry {
pub pid : u32,
pub age : u32,
pub raw_guid : [u8; 16],
pub guid : String,
pub pdb_filename : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapModuleVersionEntry {
pub pid : u32,
pub company_name : String,
pub file_description : String,
pub file_version : String,
pub internal_name : String,
pub legal_copyright : String,
pub original_file_name : String,
pub product_name : String,
pub product_version : String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmProcessMapModuleType {
Normal,
Data,
NotLinked,
Injected,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapModuleEntry {
pub pid : u32,
pub va_base : u64,
pub va_entry : u64,
pub image_size : u32,
pub is_wow64 : bool,
pub tp : VmmProcessMapModuleType,
pub name : String,
pub full_name : String,
pub file_size_raw : u32,
pub section_count : u32,
pub eat_count : u32,
pub iat_count : u32,
pub debug_info : Option<VmmProcessMapModuleDebugEntry>,
pub version_info : Option<VmmProcessMapModuleVersionEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapPteEntry {
pub pid : u32,
pub va_base : u64,
pub page_count : u64,
pub page_software_count : u32,
pub is_wow64 : bool,
pub info : String,
pub is_r : bool,
pub is_w : bool,
pub is_x : bool,
pub is_s : bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessSectionEntry {
pub pid : u32,
pub index : u32,
pub name : String,
pub name_raw : [u8; 8],
pub misc_virtual_size : u32,
pub virtual_address : u32,
pub size_of_raw_data : u32,
pub pointer_to_raw_data : u32,
pub pointer_to_relocations : u32,
pub pointer_to_linenumbers : u32,
pub number_of_relocations : u16,
pub number_of_linenumbers : u16,
pub characteristics : u32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct VmmProcessMapThreadEntry {
pub pid : u32,
pub thread_id : u32,
pub thread_pid : u32,
pub exit_status : u32,
pub state : u8,
pub running : u8,
pub priority : u8,
pub priority_base : u8,
pub va_ethread : u64,
pub va_teb : u64,
pub ft_create_time : u64,
pub ft_exit_time : u64,
pub va_start_address : u64,
pub va_win32_start_address : u64,
pub va_stack_user_base : u64,
pub va_stack_user_limit : u64,
pub va_stack_kernel_base : u64,
pub va_stack_kernel_limit : u64,
pub va_trap_frame : u64,
pub va_impersonation_token : u64,
pub va_rip : u64,
pub va_rsp : u64,
pub affinity : u64,
pub user_time : u32,
pub kernel_time : u32,
pub suspend_count : u8,
pub wait_reason : u8
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapThreadCallstackEntry {
pub pid : u32,
pub tid : u32,
pub i : u32,
pub is_reg_present : bool,
pub va_ret_addr : u64,
pub va_rsp : u64,
pub va_base_sp : u64,
pub displacement : i32,
pub module : String,
pub function : String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapUnloadedModuleEntry {
pub pid : u32,
pub va_base : u64,
pub image_size : u32,
pub is_wow64 : bool,
pub name : String,
pub checksum : u32, pub timedatestamp : u32, pub ft_unload : u64, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapVadEntry {
pub pid : u32,
pub va_start : u64,
pub va_end : u64,
pub va_vad : u64,
pub u0 : u32,
pub u1 : u32,
pub u2 : u32,
pub commit_charge : u32,
pub is_mem_commit : bool,
pub cb_prototype_pte : u32,
pub va_prototype_pte : u64,
pub va_subsection : u64,
pub va_file_object : u64,
pub info : String,
pub vadex_page_base : u32,
pub vadex_page_count : u32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum VmmProcessMapVadExType {
NA,
Hardware,
Transition,
Prototype,
DemandZero,
Compressed,
Pagefile,
File,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmProcessMapVadExEntry {
pub pid : u32,
pub tp : VmmProcessMapVadExType,
pub i_pml : u8,
pub va : u64,
pub pa : u64,
pub pte : u64,
pub pte_flags : u8,
pub proto_tp : VmmProcessMapVadExType,
pub proto_pa : u64,
pub proto_pte : u64,
pub va_vad_base : u64,
}
impl VmmProcess<'_> {
pub fn get_module_base(&self, module_name : &str) -> ResultEx<u64> {
return self.impl_get_module_base(module_name);
}
pub fn get_proc_address(&self, module_name : &str, function_name : &str) -> ResultEx<u64> {
return self.impl_get_proc_address(module_name, function_name);
}
pub fn get_path_kernel(&self) -> ResultEx<String> {
return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL);
}
pub fn get_path_user(&self) -> ResultEx<String> {
return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE);
}
pub fn get_cmdline(&self) -> ResultEx<String> {
return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE);
}
pub fn info(&self) -> ResultEx<VmmProcessInfo> {
return self.impl_info();
}
pub fn map_handle(&self) -> ResultEx<Vec<VmmProcessMapHandleEntry>> {
return self.impl_map_handle();
}
pub fn map_heap(&self) -> ResultEx<Vec<VmmProcessMapHeapEntry>> {
return self.impl_map_heap();
}
pub fn map_heapalloc(&self, heap_number_or_address : u64) -> ResultEx<Vec<VmmProcessMapHeapAllocEntry>> {
return self.impl_map_heapalloc(heap_number_or_address);
}
pub fn map_module(&self, is_info_debug : bool, is_info_version : bool) -> ResultEx<Vec<VmmProcessMapModuleEntry>> {
return self.impl_map_module(is_info_debug, is_info_version);
}
pub fn map_module_data_directory(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapDirectoryEntry>> {
return self.impl_map_module_data_directory(module_name);
}
pub fn map_module_eat(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapEatEntry>> {
return self.impl_map_module_eat(module_name);
}
pub fn map_module_iat(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapIatEntry>> {
return self.impl_map_module_iat(module_name);
}
pub fn map_module_section(&self, module_name : &str) -> ResultEx<Vec<VmmProcessSectionEntry>> {
return self.impl_map_module_section(module_name);
}
pub fn map_pte(&self, is_identify_modules : bool) -> ResultEx<Vec<VmmProcessMapPteEntry>> {
return self.impl_map_pte(is_identify_modules);
}
pub fn map_thread(&self) -> ResultEx<Vec<VmmProcessMapThreadEntry>> {
return self.impl_map_thread();
}
pub fn map_thread_callstack(&self, tid : u32) -> ResultEx<Vec<VmmProcessMapThreadCallstackEntry>> {
return self.impl_map_thread_callstack(tid, 0);
}
pub fn map_thread_callstack_ex(&self, tid : u32, flags : u32) -> ResultEx<Vec<VmmProcessMapThreadCallstackEntry>> {
return self.impl_map_thread_callstack(tid, flags);
}
pub fn map_unloaded_module(&self) -> ResultEx<Vec<VmmProcessMapUnloadedModuleEntry>> {
return self.impl_map_unloaded_module();
}
pub fn map_vad(&self, is_identify_modules : bool) -> ResultEx<Vec<VmmProcessMapVadEntry>> {
return self.impl_map_vad(is_identify_modules);
}
pub fn map_vadex(&self, offset_pages : u32, count_pages : u32) -> ResultEx<Vec<VmmProcessMapVadExEntry>> {
return self.impl_map_vadex(offset_pages, count_pages);
}
pub fn mem_read(&self, va : u64, size : usize) -> ResultEx<Vec<u8>> {
return self.vmm.impl_mem_read(self.pid, va, size, 0);
}
pub fn mem_read_ex(&self, va : u64, size : usize, flags : u64) -> ResultEx<Vec<u8>> {
return self.vmm.impl_mem_read(self.pid, va, size, flags);
}
pub fn mem_read_into(&self, va : u64, flags : u64, data : &mut [u8]) -> ResultEx<usize> {
return self.vmm.impl_mem_read_into(self.pid, va, flags, data);
}
pub fn mem_read_as<T>(&self, va : u64, flags : u64) -> ResultEx<T> {
return self.vmm.impl_mem_read_as(self.pid, va, flags);
}
pub fn mem_scatter(&self, flags : u64) -> ResultEx<VmmScatterMemory> {
return self.vmm.impl_mem_scatter(self.pid, flags);
}
pub fn mem_virt2phys(&self, va : u64) -> ResultEx<u64> {
return self.vmm.impl_mem_virt2phys(self.pid, va);
}
pub fn mem_write(&self, va : u64, data : &[u8]) -> ResultEx<()> {
return self.vmm.impl_mem_write(self.pid, va, data);
}
pub fn mem_write_as<T>(&self, va : u64, data : &T) -> ResultEx<()> {
return self.vmm.impl_mem_write_as(self.pid, va, data);
}
pub fn pdb_from_module_address(&self, va_module_base : u64) -> ResultEx<VmmPdb> {
return self.impl_pdb_from_module_address(va_module_base);
}
pub fn pdb_from_module_name(&self, module_name : &str) -> ResultEx<VmmPdb> {
return self.impl_pdb_from_module_name(module_name);
}
pub fn search(&self, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmSearch> {
return VmmSearch::impl_new(self.vmm, self.pid, addr_min, addr_max, num_results_max, flags);
}
pub fn search_yara(&self, rules : Vec<&str>, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmYara> {
return VmmYara::impl_new(self.vmm, rules, self.pid, addr_min, addr_max, num_results_max, flags);
}
}
#[derive(Debug, Clone)]
pub struct VmmRegHive<'a> {
vmm : &'a Vmm<'a>,
pub va : u64,
pub va_baseblock : u64,
pub size : u32,
pub name : String,
pub name_short : String,
pub path : String,
}
impl VmmRegHive<'_> {
pub fn reg_hive_read(&self, ra : u32, size : usize, flags : u64) -> ResultEx<Vec<u8>> {
return self.impl_reg_hive_read(ra, size, flags);
}
pub fn reg_hive_write(&self, ra : u32, data : &[u8]) -> ResultEx<()> {
return self.impl_reg_hive_write(ra, data);
}
}
#[derive(Clone, Debug)]
pub struct VmmRegKey<'a> {
vmm : &'a Vmm<'a>,
pub name : String,
pub path : String,
pub ft_last_write : u64,
}
impl VmmRegKey<'_> {
pub fn parent(&self) -> ResultEx<VmmRegKey> {
return self.impl_parent();
}
pub fn subkeys(&self) -> ResultEx<Vec<VmmRegKey>> {
return self.impl_subkeys();
}
pub fn subkeys_map(&self) -> ResultEx<HashMap<String, VmmRegKey>> {
return Ok(self.impl_subkeys()?.into_iter().map(|s| (s.name.clone(), s)).collect());
}
pub fn values(&self) -> ResultEx<Vec<VmmRegValue>> {
return self.impl_values();
}
pub fn values_map(&self) -> ResultEx<HashMap<String, VmmRegValue>> {
return Ok(self.impl_values()?.into_iter().map(|s| (s.name.clone(), s)).collect());
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum VmmRegValueType {
REG_NONE,
REG_SZ(String),
REG_EXPAND_SZ(String),
REG_BINARY(Vec<u8>),
REG_DWORD(u32),
REG_DWORD_BIG_ENDIAN(u32),
REG_LINK(String),
REG_MULTI_SZ(Vec<String>),
REG_RESOURCE_LIST(Vec<u8>),
REG_FULL_RESOURCE_DESCRIPTOR(Vec<u8>),
REG_RESOURCE_REQUIREMENTS_LIST(Vec<u8>),
REG_QWORD(u64),
}
#[derive(Clone, Debug)]
pub struct VmmRegValue<'a> {
vmm : &'a Vmm<'a>,
pub name : String,
pub path : String,
pub raw_type : u32,
pub raw_size : u32,
raw_value : Option<Vec<u8>>,
}
impl VmmRegValue<'_> {
pub fn parent(&self) -> ResultEx<VmmRegKey> {
return self.impl_parent();
}
pub fn value(&self) -> ResultEx<VmmRegValueType> {
return self.impl_value();
}
pub fn raw_value(&self) -> ResultEx<Vec<u8>> {
return self.impl_raw_value();
}
}
#[derive(Debug)]
pub struct VmmSearch<'a> {
vmm : &'a Vmm<'a>,
pid : u32,
is_started : bool,
is_completed : bool,
is_completed_success : bool,
native_search : CVMMDLL_MEM_SEARCH_CONTEXT,
search_terms : Vec<CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY>,
thread : Option<std::thread::JoinHandle<bool>>,
result : Vec<(u64, u32)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmSearchResult {
pub is_started : bool,
pub is_completed : bool,
pub is_completed_success : bool,
pub addr_min : u64,
pub addr_max : u64,
pub addr_current : u64,
pub total_read_bytes : u64,
pub total_results : u32,
pub result : Vec<(u64, u32)>,
}
impl VmmSearch<'_> {
pub fn add_search(&mut self, search_bytes : &[u8]) -> ResultEx<u32> {
return self.impl_add_search(search_bytes, None, 1);
}
pub fn add_search_ex(&mut self, search_bytes : &[u8], search_skipmask : Option<&[u8]>, byte_align : u32) -> ResultEx<u32> {
return self.impl_add_search(search_bytes, search_skipmask, byte_align);
}
pub fn start(&mut self) {
self.impl_start();
}
pub fn abort(&mut self) {
self.impl_abort();
}
pub fn poll(&mut self) -> VmmSearchResult {
return self.impl_poll();
}
pub fn result(&mut self) -> VmmSearchResult {
return self.impl_result();
}
}
#[derive(Debug)]
pub struct VmmYara<'a> {
vmm : &'a Vmm<'a>,
pid : u32,
is_started : bool,
is_completed : bool,
is_completed_success : bool,
native : CVMMDLL_YARA_CONFIG,
_native_args_rules : Vec<CString>,
_native_argv_rules : Vec<*const c_char>,
thread : Option<std::thread::JoinHandle<bool>>,
result : Vec<VmmYaraMatch>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmYaraResult {
pub is_completed : bool,
pub is_completed_success : bool,
pub addr_min : u64,
pub addr_max : u64,
pub addr_current : u64,
pub total_read_bytes : u64,
pub total_results : u32,
pub result : Vec<VmmYaraMatch>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmYaraMatchString {
pub match_string : String,
pub addresses : Vec<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmYaraMatch {
pub addr : u64,
pub rule : String,
pub tags : Vec<String>,
pub meta : Vec<(String, String)>,
pub match_strings : Vec<VmmYaraMatchString>,
}
impl VmmYara<'_> {
pub fn start(&mut self) {
self.impl_start();
}
pub fn abort(&mut self) {
self.impl_abort();
}
pub fn poll(&mut self) -> VmmYaraResult {
return self.impl_poll();
}
pub fn result(&mut self) -> VmmYaraResult {
return self.impl_result();
}
}
pub fn new_plugin_initialization<T>(native_h : usize, native_reginfo : usize) -> ResultEx<(VmmPluginInitializationInfo, VmmPluginInitializationContext<T>)> {
return impl_new_plugin_initialization::<T>(native_h, native_reginfo);
}
pub struct VmmPluginContext<'a, T> {
pub vmm : Vmm<'a>,
pub ctxlock : std::sync::RwLock<T>,
fn_list : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, path : &str, file_list : &VmmPluginFileList) -> ResultEx<()>>,
fn_read : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, file_name : &str, cb : u32, cb_offset : u64) -> ResultEx<Vec<u8>>>,
fn_write : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, file_name : &str, data : Vec<u8>, cb_offset : u64) -> ResultEx<()>>,
fn_visible : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>) -> ResultEx<bool>>,
fn_notify : Option<fn(ctxp : &VmmPluginContext<T>, event_id : u32) -> ResultEx<()>>,
}
#[derive(Debug)]
pub struct VmmPluginFileList<'a> {
vmm : &'a Vmm<'a>,
h_file_list : usize,
}
impl VmmPluginFileList<'_> {
pub fn add_file(&self, name : &str, size : u64) {
self.impl_add_file(name, size);
}
pub fn add_directory(&self, name : &str) {
self.impl_add_directory(name);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmmPluginInitializationInfo {
pub tp_system : VmmSystemType,
pub tp_memorymodel : VmmMemoryModelType,
pub version_major : u32,
pub version_minor : u32,
pub version_build : u32,
}
pub struct VmmPluginInitializationContext<T> {
h_vmm : usize,
h_reginfo : usize,
pub ctx : Option<T>,
pub path_name : String,
pub is_root_module : bool,
pub is_root_module_hidden : bool,
pub is_process_module : bool,
pub is_process_module_hidden : bool,
pub fn_list : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, path : &str, file_list : &VmmPluginFileList) -> ResultEx<()>>,
pub fn_read : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, file_name : &str, cb : u32, cb_offset : u64) -> ResultEx<Vec<u8>>>,
pub fn_write : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>, file_name : &str, data : Vec<u8>, cb_offset : u64) -> ResultEx<()>>,
pub fn_visible : Option<fn(ctxp : &VmmPluginContext<T>, process : Option<VmmProcess>) -> ResultEx<bool>>,
pub fn_notify : Option<fn(ctxp : &VmmPluginContext<T>, event_id : u32) -> ResultEx<()>>,
}
impl<T> VmmPluginInitializationContext<T> {
pub fn register(self) -> ResultEx<()> {
return self.impl_register();
}
}
#[derive(Debug)]
pub struct LeechCore {
path_lc : String,
native : LcNative,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct LcBar {
pub is_valid : bool,
pub is_io : bool,
pub is_64bit : bool,
pub is_prefetchable : bool,
pub bar_index : u32,
pub pa : u64,
pub cb : u64,
}
#[derive(Debug)]
pub struct LcBarRequest {
native : *mut LC_BAR_REQUEST,
pub bar : LcBar,
pub tag : u8,
pub be_first : u8,
pub be_last : u8,
pub is_64bit : bool,
pub is_read : bool,
pub is_write : bool,
pub data_size : u32,
pub data_offset : u64,
pub data_write : Option<Vec<u8>>,
}
pub struct LcBarContext<'a, T> {
pub lc : &'a LeechCore,
pub ctxlock : std::sync::RwLock<T>,
fn_callback : fn(ctx : &LcBarContext<T>, req : &LcBarRequest) -> ResultEx<()>,
native_ctx : usize,
}
pub struct LcBarContextWrap<'a, T> {
pub ctx : &'a LcBarContext::<'a, T>,
native : *mut LcBarContext::<'a, T>,
}
pub struct LcTlpContext<'a, T> {
pub lc : &'a LeechCore,
pub ctxlock : std::sync::RwLock<T>,
fn_callback : fn(ctx : &LcTlpContext<T>, tlp : &[u8], tlp_str : &str) -> ResultEx<()>,
native_ctx : usize,
}
pub struct LcTlpContextWrap<'a, T> {
pub ctx : &'a LcTlpContext::<'a, T>,
native : *mut LcTlpContext::<'a, T>,
}
impl LeechCore {
pub const LC_CONFIG_VERSION : u32 = 0xc0fd0002;
pub const LC_CONFIG_PRINTF_NONE : u32 = 0x00000000;
pub const LC_CONFIG_PRINTF_ENABLED : u32 = 0x00000001;
pub const LC_CONFIG_PRINTF_V : u32 = 0x00000002;
pub const LC_CONFIG_PRINTF_VV : u32 = 0x00000004;
pub const LC_CONFIG_PRINTF_VVV : u32 = 0x00000008;
pub fn new(lc_lib_path : &str, device_config : &str, lc_config_printf_verbosity : u32) -> ResultEx<LeechCore> {
return LeechCore::impl_new(lc_lib_path, device_config, "", lc_config_printf_verbosity, 0);
}
pub fn new_ex(lc_lib_path : &str, device_config : &str, lc_config_printf_verbosity : u32, remote_config : &str, pa_max : u64) -> ResultEx<LeechCore> {
return LeechCore::impl_new(lc_lib_path, device_config, remote_config, lc_config_printf_verbosity, pa_max);
}
pub const LC_OPT_CORE_PRINTF_ENABLE : u64 = 0x4000000100000000;
pub const LC_OPT_CORE_VERBOSE : u64 = 0x4000000200000000;
pub const LC_OPT_CORE_VERBOSE_EXTRA : u64 = 0x4000000300000000;
pub const LC_OPT_CORE_VERBOSE_EXTRA_TLP : u64 = 0x4000000400000000;
pub const LC_OPT_CORE_VERSION_MAJOR : u64 = 0x4000000500000000;
pub const LC_OPT_CORE_VERSION_MINOR : u64 = 0x4000000600000000;
pub const LC_OPT_CORE_VERSION_REVISION : u64 = 0x4000000700000000;
pub const LC_OPT_CORE_ADDR_MAX : u64 = 0x1000000800000000;
pub const LC_OPT_CORE_STATISTICS_CALL_COUNT : u64 = 0x4000000900000000;
pub const LC_OPT_CORE_STATISTICS_CALL_TIME : u64 = 0x4000000a00000000;
pub const LC_OPT_CORE_VOLATILE : u64 = 0x1000000b00000000;
pub const LC_OPT_CORE_READONLY : u64 = 0x1000000c00000000;
pub const LC_OPT_MEMORYINFO_VALID : u64 = 0x0200000100000000;
pub const LC_OPT_MEMORYINFO_FLAG_32BIT : u64 = 0x0200000300000000;
pub const LC_OPT_MEMORYINFO_FLAG_PAE : u64 = 0x0200000400000000;
pub const LC_OPT_MEMORYINFO_ARCH : u64 = 0x0200001200000000;
pub const LC_OPT_MEMORYINFO_OS_VERSION_MINOR : u64 = 0x0200000500000000;
pub const LC_OPT_MEMORYINFO_OS_VERSION_MAJOR : u64 = 0x0200000600000000;
pub const LC_OPT_MEMORYINFO_OS_DTB : u64 = 0x0200000700000000;
pub const LC_OPT_MEMORYINFO_OS_PFN : u64 = 0x0200000800000000;
pub const LC_OPT_MEMORYINFO_OS_PSLOADEDMODULELIST : u64 = 0x0200000900000000;
pub const LC_OPT_MEMORYINFO_OS_PSACTIVEPROCESSHEAD : u64 = 0x0200000a00000000;
pub const LC_OPT_MEMORYINFO_OS_MACHINE_IMAGE_TP : u64 = 0x0200000b00000000;
pub const LC_OPT_MEMORYINFO_OS_NUM_PROCESSORS : u64 = 0x0200000c00000000;
pub const LC_OPT_MEMORYINFO_OS_SYSTEMTIME : u64 = 0x0200000d00000000;
pub const LC_OPT_MEMORYINFO_OS_UPTIME : u64 = 0x0200000e00000000;
pub const LC_OPT_MEMORYINFO_OS_KERNELBASE : u64 = 0x0200000f00000000;
pub const LC_OPT_MEMORYINFO_OS_KERNELHINT : u64 = 0x0200001000000000;
pub const LC_OPT_MEMORYINFO_OS_KDDEBUGGERDATABLOCK : u64 = 0x0200001100000000;
pub const LC_OPT_FPGA_PROBE_MAXPAGES : u64 = 0x0300000100000000;
pub const LC_OPT_FPGA_MAX_SIZE_RX : u64 = 0x0300000300000000;
pub const LC_OPT_FPGA_MAX_SIZE_TX : u64 = 0x0300000400000000;
pub const LC_OPT_FPGA_DELAY_PROBE_READ : u64 = 0x0300000500000000;
pub const LC_OPT_FPGA_DELAY_PROBE_WRITE : u64 = 0x0300000600000000;
pub const LC_OPT_FPGA_DELAY_WRITE : u64 = 0x0300000700000000;
pub const LC_OPT_FPGA_DELAY_READ : u64 = 0x0300000800000000;
pub const LC_OPT_FPGA_RETRY_ON_ERROR : u64 = 0x0300000900000000;
pub const LC_OPT_FPGA_DEVICE_ID : u64 = 0x0300008000000000;
pub const LC_OPT_FPGA_FPGA_ID : u64 = 0x0300008100000000;
pub const LC_OPT_FPGA_VERSION_MAJOR : u64 = 0x0300008200000000;
pub const LC_OPT_FPGA_VERSION_MINOR : u64 = 0x0300008300000000;
pub const LC_OPT_FPGA_ALGO_TINY : u64 = 0x0300008400000000;
pub const LC_OPT_FPGA_ALGO_SYNCHRONOUS : u64 = 0x0300008500000000;
pub const LC_OPT_FPGA_CFGSPACE_XILINX : u64 = 0x0300008600000000;
pub const LC_OPT_FPGA_TLP_READ_CB_WITHINFO : u64 = 0x0300009000000000;
pub const LC_OPT_FPGA_TLP_READ_CB_FILTERCPL : u64 = 0x0300009100000000;
pub fn get_option(&self, config_id : u64) -> ResultEx<u64> {
return self.impl_get_option(config_id);
}
pub fn set_option(&self, config_id : u64, config_value : u64) -> ResultEx<()> {
return self.impl_set_option(config_id, config_value);
}
pub const LC_CMD_FPGA_PCIECFGSPACE : u64 = 0x0000010300000000;
pub const LC_CMD_FPGA_CFGREGPCIE : u64 = 0x0000010400000000;
pub const LC_CMD_FPGA_CFGREGCFG : u64 = 0x0000010500000000;
pub const LC_CMD_FPGA_CFGREGDRP : u64 = 0x0000010600000000;
pub const LC_CMD_FPGA_CFGREGCFG_MARKWR : u64 = 0x0000010700000000;
pub const LC_CMD_FPGA_CFGREGPCIE_MARKWR : u64 = 0x0000010800000000;
pub const LC_CMD_FPGA_PROBE : u64 = 0x0000010b00000000;
pub const LC_CMD_FPGA_CFGSPACE_SHADOW_RD : u64 = 0x0000010c00000000;
pub const LC_CMD_FPGA_CFGSPACE_SHADOW_WR : u64 = 0x0000010d00000000;
pub const LC_CMD_FPGA_TLP_WRITE_SINGLE : u64 = 0x0000011000000000;
pub const LC_CMD_FPGA_TLP_WRITE_MULTIPLE : u64 = 0x0000011100000000;
pub const LC_CMD_FPGA_TLP_TOSTRING : u64 = 0x0000011200000000;
pub const LC_CMD_FPGA_TLP_CONTEXT : u64 = 0x2000011400000000;
pub const LC_CMD_FPGA_TLP_CONTEXT_RD : u64 = 0x2000011b00000000;
pub const LC_CMD_FPGA_TLP_FUNCTION_CALLBACK : u64 = 0x2000011500000000;
pub const LC_CMD_FPGA_TLP_FUNCTION_CALLBACK_RD : u64 = 0x2000011c00000000;
pub const LC_CMD_FPGA_BAR_CONTEXT : u64 = 0x2000012000000000;
pub const LC_CMD_FPGA_BAR_CONTEXT_RD : u64 = 0x2000012100000000;
pub const LC_CMD_FPGA_BAR_FUNCTION_CALLBACK : u64 = 0x2000012200000000;
pub const LC_CMD_FPGA_BAR_FUNCTION_CALLBACK_RD : u64 = 0x2000012300000000;
pub const LC_CMD_FPGA_BAR_INFO : u64 = 0x0000012400000000;
pub const LC_CMD_FILE_DUMPHEADER_GET : u64 = 0x0000020100000000;
pub const LC_CMD_STATISTICS_GET : u64 = 0x4000010000000000;
pub const LC_CMD_MEMMAP_GET : u64 = 0x4000020000000000;
pub const LC_CMD_MEMMAP_SET : u64 = 0x4000030000000000;
pub const LC_CMD_MEMMAP_GET_STRUCT : u64 = 0x4000040000000000;
pub const LC_CMD_MEMMAP_SET_STRUCT : u64 = 0x4000050000000000;
pub fn command(&self, command_id : u64, data : Option<&Vec<u8>>) -> ResultEx<Option<Vec<u8>>> {
return self.impl_command(command_id, data);
}
pub fn mem_read(&self, pa : u64, size : usize) -> ResultEx<Vec<u8>> {
return self.impl_mem_read(pa, size);
}
pub fn mem_read_as<T>(&self, pa : u64) -> ResultEx<T> {
return self.impl_mem_read_as(pa);
}
pub fn mem_write(&self, pa : u64, data : &Vec<u8>) -> ResultEx<()> {
return self.impl_mem_write(pa, data);
}
pub fn mem_write_as<T>(&self, pa : u64, data : &T) -> ResultEx<()> {
return self.impl_mem_write_as(pa, data);
}
pub fn get_memmap(&self) -> ResultEx<String> {
return self.impl_get_memmap();
}
pub fn set_memmap(&self, str_memmap : &str) -> ResultEx<()> {
return self.impl_set_memmap(str_memmap);
}
pub fn pcie_bar_info(&self) -> ResultEx<[LcBar; 6]> {
return self.impl_pcie_bar_info();
}
pub fn pcie_bar_callback<T>(&self, ctx : T, fn_bar_callback : fn(ctx : &LcBarContext<T>, req : &LcBarRequest) -> ResultEx<()>) -> ResultEx<LcBarContextWrap<T>> {
return self.impl_pcie_bar_callback(ctx, fn_bar_callback);
}
pub fn pcie_tlp_callback<T>(&self, ctx : T, fn_tlp_callback : fn(ctx : &LcTlpContext<T>, tlp : &[u8], tlp_str : &str) -> ResultEx<()>) -> ResultEx<LcTlpContextWrap<T>> {
return self.impl_pcie_tlp_callback(ctx, fn_tlp_callback);
}
pub fn pcie_tlp_write(&self, tlp : &[u8]) -> ResultEx<()> {
return self.impl_pcie_tlp_write(tlp);
}
}
impl LcBarRequest {
pub fn read_reply(&self, data_reply : &[u8]) -> ResultEx<()> {
return self.impl_read_reply(data_reply, false);
}
pub fn read_reply_fail(&self) -> ResultEx<()> {
let data = [0u8; 0];
return self.impl_read_reply(&data, true);
}
}
#[allow(dead_code)]
#[allow(non_snake_case)]
#[derive(Debug)]
struct VmmNative {
h : usize,
is_close_h : bool,
library_lc : Option<libloading::Library>,
library_vmm : Option<libloading::Library>,
VMMDLL_Initialize : extern "C" fn(argc: c_int, argv: *const *const c_char) -> usize,
VMMDLL_InitializePlugins : extern "C" fn(hVMM : usize) -> bool,
VMMDLL_Close : extern "C" fn(hVMM : usize),
VMMDLL_ConfigGet : extern "C" fn(hVMM : usize, fOption : u64, pqwValue : *mut u64) -> bool,
VMMDLL_ConfigSet : extern "C" fn(hVMM : usize, fOption : u64, qwValue : u64) -> bool,
VMMDLL_MemFree : extern "C" fn(pvMem : usize),
VMMDLL_Log : extern "C" fn(hVMM : usize, MID : u32, dwLogLevel : u32, uszFormat : *const c_char, uszParam : *const c_char),
VMMDLL_MemSearch : extern "C" fn(hVMM : usize, pid : u32, ctx : *mut CVMMDLL_MEM_SEARCH_CONTEXT, ppva : *mut u64, pcva : *mut u32) -> bool,
VMMDLL_YaraSearch : extern "C" fn(hVMM : usize, pid : u32, ctx : *mut CVMMDLL_YARA_CONFIG, ppva : *mut u64, pcva : *mut u32) -> bool,
VMMDLL_MemReadEx : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pb : *mut u8, cb : u32, pcbReadOpt : *mut u32, flags : u64) -> bool,
VMMDLL_MemWrite : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pb : *const u8, cb : u32) -> bool,
VMMDLL_MemVirt2Phys : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pqwPA : *mut u64) -> bool,
VMMDLL_Scatter_Initialize : extern "C" fn(hVMM : usize, pid : u32, flags : u32) -> usize,
VMMDLL_Scatter_Prepare : extern "C" fn(hS : usize, va : u64, cb : u32) -> bool,
VMMDLL_Scatter_PrepareEx : extern "C" fn(hS : usize, va : u64, cb : u32, pb : *mut u8, pcbRead : *mut u32) -> bool,
VMMDLL_Scatter_PrepareWrite : extern "C" fn(hS : usize, va : u64, pb : *const u8, cb : u32) -> bool,
VMMDLL_Scatter_Execute : extern "C" fn(hS : usize) -> bool,
VMMDLL_Scatter_Read : extern "C" fn(hS : usize, va : u64, cb : u32, pb : *mut u8, pcbRead : *mut u32) -> bool,
VMMDLL_Scatter_Clear : extern "C" fn(hS : usize, pid : u32, flags : u32) -> bool,
VMMDLL_Scatter_CloseHandle : extern "C" fn(hS : usize),
VMMDLL_PidGetFromName : extern "C" fn(hVMM : usize, szProcName : *const c_char, pdwPID : *mut u32) -> bool,
VMMDLL_PidList : extern "C" fn(hVMM : usize, pPIDs : *mut u32, pcPIDs : *mut usize) -> bool,
VMMDLL_WinReg_HiveList : extern "C" fn(hVMM : usize, pHives : *mut CRegHive, cHives : u32, pcHives : *mut u32) -> bool,
VMMDLL_WinReg_HiveReadEx : extern "C" fn(hVMM : usize, vaCMHive : u64, ra : u32, pb : *mut u8, cb : u32, pcbReadOpt : *mut u32, flags : u64) -> bool,
VMMDLL_WinReg_HiveWrite : extern "C" fn(hVMM : usize, vaCMHive : u64, ra : u32, pb : *const u8, cb : u32) -> bool,
VMMDLL_WinReg_EnumKeyExU : extern "C" fn(hVMM : usize, uszFullPathKey : *const c_char, dwIndex : u32, lpcchName : *mut c_char, lpcchName : *mut u32, lpftLastWriteTime : *mut u64) -> bool,
VMMDLL_WinReg_EnumValueU : extern "C" fn(hVMM : usize, uszFullPathKey : *const c_char, dwIndex : u32, lpValueName : *mut c_char, lpcchValueName : *mut u32, lpType : *mut u32, lpcbData : *mut u32) -> bool,
VMMDLL_WinReg_QueryValueExU : extern "C" fn(hVMM : usize, uszFullPathKeyValue : *const c_char, lpType : *mut u32, lpData : *mut u8, lpcbData : *mut u32) -> bool,
VMMDLL_ProcessGetModuleBaseU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char) -> u64,
VMMDLL_ProcessGetProcAddressU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, szFunctionName : *const c_char) -> u64,
VMMDLL_ProcessGetInformation : extern "C" fn(hVMM : usize, pid : u32, pProcessInformation : *mut CProcessInformation, pcbProcessInformation : *mut usize) -> bool,
VMMDLL_ProcessGetInformationString : extern "C" fn(hVMM : usize, pid : u32, fOptionString : u32) -> *const c_char,
VMMDLL_Map_GetKDeviceU : extern "C" fn(hVMM : usize, ppPoolMap : *mut *mut CKDeviceMap) -> bool,
VMMDLL_Map_GetKDriverU : extern "C" fn(hVMM : usize, ppPoolMap : *mut *mut CKDriverMap) -> bool,
VMMDLL_Map_GetKObjectU : extern "C" fn(hVMM : usize, ppPoolMap : *mut *mut CKObjectMap) -> bool,
VMMDLL_Map_GetNetU : extern "C" fn(hVMM : usize, ppNetMap : *mut *mut CNetMap) -> bool,
VMMDLL_Map_GetPfnEx : extern "C" fn(hVMM : usize, pPfns : *const u32, cPfns : u32, ppPfnMap : *mut *mut CPfnMap, flags : u32) -> bool,
VMMDLL_Map_GetPhysMem : extern "C" fn(hVMM : usize, ppPhysMemMap : *mut *mut CMemoryMap) -> bool,
VMMDLL_Map_GetPool : extern "C" fn(hVMM : usize, ppPoolMap : *mut *mut CPoolMap, flags : u32) -> bool,
VMMDLL_Map_GetServicesU : extern "C" fn(hVMM : usize, ppServiceMap : *mut *mut CServiceMap) -> bool,
VMMDLL_Map_GetUsersU : extern "C" fn(hVMM : usize, ppUserMap : *mut *mut CUserMap) -> bool,
VMMDLL_Map_GetVMU : extern "C" fn(hVMM : usize, ppVmMap : *mut *mut CVmMap) -> bool,
VMMDLL_PdbLoad : extern "C" fn(hVMM : usize, dwPID : u32, vaModuleBase : u64, szModuleName : *mut c_char) -> bool,
VMMDLL_PdbSymbolName : extern "C" fn(hVMM : usize, szModule : *const c_char, cbSymbolAddressOrOffset : u64, szSymbolName : *mut c_char, pdwSymbolDisplacement : *mut u32) -> bool,
VMMDLL_PdbSymbolAddress : extern "C" fn(hVMM : usize, szModule : *const c_char, szSymbolName : *const c_char, pvaSymbolAddress : *mut u64) -> bool,
VMMDLL_PdbTypeSize : extern "C" fn(hVMM : usize, szModule : *const c_char, szTypeName : *const c_char, pcbTypeSize : *mut u32) -> bool,
VMMDLL_PdbTypeChildOffset : extern "C" fn(hVMM : usize, szModule : *const c_char, uszTypeName : *const c_char, uszTypeChildName : *const c_char, pcbTypeChildOffset : *mut u32) -> bool,
VMMDLL_Map_GetEATU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, ppEatMap : *mut *mut CEatMap) -> bool,
VMMDLL_Map_GetHandleU : extern "C" fn(hVMM : usize, pid : u32, ppHandleMap : *mut *mut CHandleMap) -> bool,
VMMDLL_Map_GetHeap : extern "C" fn(hVMM : usize, pid : u32, ppHeapMap : *mut *mut CHeapMap) -> bool,
VMMDLL_Map_GetHeapAlloc : extern "C" fn(hVMM : usize, pid : u32, qwHeapNumOrAddress : u64, ppHeapAllocMap : *mut *mut CHeapAllocMap) -> bool,
VMMDLL_Map_GetIATU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, ppIatMap : *mut *mut CIatMap) -> bool,
VMMDLL_Map_GetModuleU : extern "C" fn(hVMM : usize, pid : u32, ppModuleMap : *mut *mut CModuleMap, flags : u32) -> bool,
VMMDLL_Map_GetPteU : extern "C" fn(hVMM : usize, pid : u32, fIdentifyModules : bool, ppPteMap : *mut *mut CPteMap) -> bool,
VMMDLL_Map_GetThread : extern "C" fn(hVMM : usize, pid : u32, ppThreadMap : *mut *mut CThreadMap) -> bool,
VMMDLL_Map_GetThreadCallstackU: extern "C" fn(hVMM : usize, pid : u32, tid : u32, flags : u32, ppThreadCallstack : *mut *mut CThreadCallstackMap) -> bool,
VMMDLL_Map_GetUnloadedModuleU : extern "C" fn(hVMM : usize, pid : u32, ppUnloadedModuleMap : *mut *mut CUnloadedModuleMap) -> bool,
VMMDLL_Map_GetVadU : extern "C" fn(hVMM : usize, pid : u32, fIdentifyModules : bool, ppVadMap : *mut *mut CVadMap) -> bool,
VMMDLL_Map_GetVadEx : extern "C" fn(hVMM : usize, pid : u32, oPage : u32, cPage : u32, ppVadExMap : *mut *mut CVadExMap) -> bool,
VMMDLL_ProcessGetDirectoriesU : extern "C" fn(hVMM : usize, pid : u32, uszModule : *const c_char, pDataDirectories : *mut CIMAGE_DATA_DIRECTORY) -> bool,
VMMDLL_ProcessGetSectionsU : extern "C" fn(hVMM : usize, pid : u32, uszModule : *const c_char, pSections : *mut CIMAGE_SECTION_HEADER, cSections : u32, pcSections : *mut u32) -> bool,
VMMDLL_VfsListU : extern "C" fn(hVMM : usize, uszPath : *const c_char, pFileList : *mut CVMMDLL_VFS_FILELIST2) -> bool,
VMMDLL_VfsReadU : extern "C" fn(hVMM : usize, uszFileName : *const c_char, pb : *mut u8, cb : u32, pcbRead : *mut u32, cbOffset : u64) -> u32,
VMMDLL_VfsWriteU : extern "C" fn(hVMM : usize, uszFileName : *const c_char, pb : *const u8, cb : u32, pcbWrite : *mut u32, cbOffset : u64) -> u32,
VMMDLL_VmGetVmmHandle : extern "C" fn(hVMM : usize, hVM : usize) -> usize,
VMMDLL_VfsList_AddFile : extern "C" fn(pFileList : usize, uszName : *const c_char, cb : u64, pExInfo : usize),
VMMDLL_VfsList_AddDirectory : extern "C" fn(pFileList : usize, uszName : *const c_char, pExInfo : usize),
}
#[allow(non_snake_case)]
fn impl_new<'a>(vmm_lib_path : &str, lc_existing_opt : Option<&LeechCore>, h_vmm_existing_opt : usize, args: &Vec<&str>) -> ResultEx<Vmm<'a>> {
unsafe {
let path_vmm = std::path::Path::new(vmm_lib_path).canonicalize()?;
let mut path_lc = path_vmm.parent().unwrap().canonicalize()?;
if cfg!(windows) {
path_lc = path_lc.join("leechcore.dll");
} else if cfg!(target_os = "macos") {
path_lc = path_lc.join("leechcore.dylib");
} else {
path_lc = path_lc.join("leechcore.so");
}
let str_path_lc = path_lc.to_str().unwrap_or("");
let str_path_vmm = path_vmm.to_str().unwrap_or("");
let lib_lc : libloading::Library = libloading::Library::new(str_path_lc)
.with_context(|| format!("Failed to load leechcore library at: {}", str_path_lc))?;
let lib : libloading::Library = libloading::Library::new(str_path_vmm)
.with_context(|| format!("Failed to load vmm library at: {}", str_path_vmm))?;
let VMMDLL_Initialize : extern "C" fn(argc: c_int, argv: *const *const c_char) -> usize = *lib.get(b"VMMDLL_Initialize")?;
let VMMDLL_InitializePlugins : extern "C" fn(usize) -> bool = *lib.get(b"VMMDLL_InitializePlugins")?;
let VMMDLL_Close = *lib.get(b"VMMDLL_Close")?;
let VMMDLL_ConfigGet = *lib.get(b"VMMDLL_ConfigGet")?;
let VMMDLL_ConfigSet = *lib.get(b"VMMDLL_ConfigSet")?;
let VMMDLL_MemFree = *lib.get(b"VMMDLL_MemFree")?;
let VMMDLL_Log = *lib.get(b"VMMDLL_Log")?;
let VMMDLL_MemSearch = *lib.get(b"VMMDLL_MemSearch")?;
let VMMDLL_YaraSearch = *lib.get(b"VMMDLL_YaraSearch")?;
let VMMDLL_MemReadEx = *lib.get(b"VMMDLL_MemReadEx")?;
let VMMDLL_MemWrite = *lib.get(b"VMMDLL_MemWrite")?;
let VMMDLL_MemVirt2Phys = *lib.get(b"VMMDLL_MemVirt2Phys")?;
let VMMDLL_Scatter_Initialize = *lib.get(b"VMMDLL_Scatter_Initialize")?;
let VMMDLL_Scatter_Prepare = *lib.get(b"VMMDLL_Scatter_Prepare")?;
let VMMDLL_Scatter_PrepareEx = *lib.get(b"VMMDLL_Scatter_PrepareEx")?;
let VMMDLL_Scatter_PrepareWrite = *lib.get(b"VMMDLL_Scatter_PrepareWrite")?;
let VMMDLL_Scatter_Execute = *lib.get(b"VMMDLL_Scatter_Execute")?;
let VMMDLL_Scatter_Read = *lib.get(b"VMMDLL_Scatter_Read")?;
let VMMDLL_Scatter_Clear = *lib.get(b"VMMDLL_Scatter_Clear")?;
let VMMDLL_Scatter_CloseHandle = *lib.get(b"VMMDLL_Scatter_CloseHandle")?;
let VMMDLL_PidGetFromName = *lib.get(b"VMMDLL_PidGetFromName")?;
let VMMDLL_PidList = *lib.get(b"VMMDLL_PidList")?;
let VMMDLL_WinReg_HiveList = *lib.get(b"VMMDLL_WinReg_HiveList")?;
let VMMDLL_WinReg_HiveReadEx = *lib.get(b"VMMDLL_WinReg_HiveReadEx")?;
let VMMDLL_WinReg_HiveWrite = *lib.get(b"VMMDLL_WinReg_HiveWrite")?;
let VMMDLL_WinReg_EnumKeyExU = *lib.get(b"VMMDLL_WinReg_EnumKeyExU")?;
let VMMDLL_WinReg_EnumValueU = *lib.get(b"VMMDLL_WinReg_EnumValueU")?;
let VMMDLL_WinReg_QueryValueExU = *lib.get(b"VMMDLL_WinReg_QueryValueExU")?;
let VMMDLL_ProcessGetModuleBaseU = *lib.get(b"VMMDLL_ProcessGetModuleBaseU")?;
let VMMDLL_ProcessGetProcAddressU = *lib.get(b"VMMDLL_ProcessGetProcAddressU")?;
let VMMDLL_ProcessGetInformation = *lib.get(b"VMMDLL_ProcessGetInformation")?;
let VMMDLL_ProcessGetInformationString = *lib.get(b"VMMDLL_ProcessGetInformationString")?;
let VMMDLL_Map_GetKDeviceU = *lib.get(b"VMMDLL_Map_GetKDeviceU")?;
let VMMDLL_Map_GetKDriverU = *lib.get(b"VMMDLL_Map_GetKDriverU")?;
let VMMDLL_Map_GetKObjectU = *lib.get(b"VMMDLL_Map_GetKObjectU")?;
let VMMDLL_Map_GetNetU = *lib.get(b"VMMDLL_Map_GetNetU")?;
let VMMDLL_Map_GetPfnEx = *lib.get(b"VMMDLL_Map_GetPfnEx")?;
let VMMDLL_Map_GetPhysMem = *lib.get(b"VMMDLL_Map_GetPhysMem")?;
let VMMDLL_Map_GetPool = *lib.get(b"VMMDLL_Map_GetPool")?;
let VMMDLL_Map_GetUsersU = *lib.get(b"VMMDLL_Map_GetUsersU")?;
let VMMDLL_Map_GetServicesU = *lib.get(b"VMMDLL_Map_GetServicesU")?;
let VMMDLL_Map_GetVMU = *lib.get(b"VMMDLL_Map_GetVMU")?;
let VMMDLL_PdbLoad = *lib.get(b"VMMDLL_PdbLoad")?;
let VMMDLL_PdbSymbolName = *lib.get(b"VMMDLL_PdbSymbolName")?;
let VMMDLL_PdbSymbolAddress = *lib.get(b"VMMDLL_PdbSymbolAddress")?;
let VMMDLL_PdbTypeSize = *lib.get(b"VMMDLL_PdbTypeSize")?;
let VMMDLL_PdbTypeChildOffset = *lib.get(b"VMMDLL_PdbTypeChildOffset")?;
let VMMDLL_Map_GetEATU = *lib.get(b"VMMDLL_Map_GetEATU")?;
let VMMDLL_Map_GetHandleU = *lib.get(b"VMMDLL_Map_GetHandleU")?;
let VMMDLL_Map_GetHeap = *lib.get(b"VMMDLL_Map_GetHeap")?;
let VMMDLL_Map_GetHeapAlloc = *lib.get(b"VMMDLL_Map_GetHeapAlloc")?;
let VMMDLL_Map_GetIATU = *lib.get(b"VMMDLL_Map_GetIATU")?;
let VMMDLL_Map_GetModuleU = *lib.get(b"VMMDLL_Map_GetModuleU")?;
let VMMDLL_Map_GetPteU = *lib.get(b"VMMDLL_Map_GetPteU")?;
let VMMDLL_Map_GetThread = *lib.get(b"VMMDLL_Map_GetThread")?;
let VMMDLL_Map_GetThreadCallstackU = *lib.get(b"VMMDLL_Map_GetThread_CallstackU")?;
let VMMDLL_Map_GetUnloadedModuleU = *lib.get(b"VMMDLL_Map_GetUnloadedModuleU")?;
let VMMDLL_Map_GetVadU = *lib.get(b"VMMDLL_Map_GetVadU")?;
let VMMDLL_Map_GetVadEx = *lib.get(b"VMMDLL_Map_GetVadEx")?;
let VMMDLL_ProcessGetDirectoriesU = *lib.get(b"VMMDLL_ProcessGetDirectoriesU")?;
let VMMDLL_ProcessGetSectionsU = *lib.get(b"VMMDLL_ProcessGetSectionsU")?;
let VMMDLL_VfsListU = *lib.get(b"VMMDLL_VfsListU")?;
let VMMDLL_VfsReadU = *lib.get(b"VMMDLL_VfsReadU")?;
let VMMDLL_VfsWriteU = *lib.get(b"VMMDLL_VfsWriteU")?;
let VMMDLL_VmGetVmmHandle = *lib.get(b"VMMDLL_VmGetVmmHandle")?;
let VMMDLL_VfsList_AddFile = *lib.get(b"VMMDLL_VfsList_AddFile")?;
let VMMDLL_VfsList_AddDirectory = *lib.get(b"VMMDLL_VfsList_AddDirectory")?;
let h;
if h_vmm_existing_opt != 0 {
h = h_vmm_existing_opt;
} else {
let mut args = args.clone();
let lc_existing_device : String;
if let Some(lc_existing) = lc_existing_opt {
lc_existing_device = format!("existing://0x{:x}", lc_existing.native.h);
args.push("-device");
args.push(lc_existing_device.as_str());
}
let args = args.iter().map(|arg| CString::new(*arg).unwrap()).collect::<Vec<CString>>();
let argv: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
let argc: c_int = args.len() as c_int;
h = (VMMDLL_Initialize)(argc, argv.as_ptr());
if h == 0 {
return Err(anyhow!("VMMDLL_Initialize: fail"));
}
let r = (VMMDLL_InitializePlugins)(h);
if !r {
return Err(anyhow!("VMMDLL_InitializePlugins: fail"));
}
}
let native = VmmNative {
h,
is_close_h : h_vmm_existing_opt == 0,
library_lc : Some(lib_lc),
library_vmm : Some(lib),
VMMDLL_Initialize,
VMMDLL_InitializePlugins,
VMMDLL_Close,
VMMDLL_ConfigGet,
VMMDLL_ConfigSet,
VMMDLL_MemFree,
VMMDLL_Log,
VMMDLL_MemSearch,
VMMDLL_YaraSearch,
VMMDLL_MemReadEx,
VMMDLL_MemWrite,
VMMDLL_MemVirt2Phys,
VMMDLL_Scatter_Initialize,
VMMDLL_Scatter_Prepare,
VMMDLL_Scatter_PrepareEx,
VMMDLL_Scatter_PrepareWrite,
VMMDLL_Scatter_Execute,
VMMDLL_Scatter_Read,
VMMDLL_Scatter_Clear,
VMMDLL_Scatter_CloseHandle,
VMMDLL_PidGetFromName,
VMMDLL_PidList,
VMMDLL_WinReg_HiveList,
VMMDLL_WinReg_HiveReadEx,
VMMDLL_WinReg_HiveWrite,
VMMDLL_WinReg_EnumKeyExU,
VMMDLL_WinReg_EnumValueU,
VMMDLL_WinReg_QueryValueExU,
VMMDLL_ProcessGetModuleBaseU,
VMMDLL_ProcessGetProcAddressU,
VMMDLL_ProcessGetInformation,
VMMDLL_ProcessGetInformationString,
VMMDLL_Map_GetKDeviceU,
VMMDLL_Map_GetKDriverU,
VMMDLL_Map_GetKObjectU,
VMMDLL_Map_GetNetU,
VMMDLL_Map_GetPfnEx,
VMMDLL_Map_GetPhysMem,
VMMDLL_Map_GetPool,
VMMDLL_Map_GetUsersU,
VMMDLL_Map_GetServicesU,
VMMDLL_Map_GetVMU,
VMMDLL_PdbLoad,
VMMDLL_PdbSymbolName,
VMMDLL_PdbSymbolAddress,
VMMDLL_PdbTypeSize,
VMMDLL_PdbTypeChildOffset,
VMMDLL_Map_GetEATU,
VMMDLL_Map_GetHandleU,
VMMDLL_Map_GetHeap,
VMMDLL_Map_GetHeapAlloc,
VMMDLL_Map_GetIATU,
VMMDLL_Map_GetModuleU,
VMMDLL_Map_GetPteU,
VMMDLL_Map_GetThread,
VMMDLL_Map_GetThreadCallstackU,
VMMDLL_Map_GetUnloadedModuleU,
VMMDLL_Map_GetVadU,
VMMDLL_Map_GetVadEx,
VMMDLL_ProcessGetDirectoriesU,
VMMDLL_ProcessGetSectionsU,
VMMDLL_VfsListU,
VMMDLL_VfsReadU,
VMMDLL_VfsWriteU,
VMMDLL_VmGetVmmHandle,
VMMDLL_VfsList_AddFile,
VMMDLL_VfsList_AddDirectory,
};
let vmm = Vmm {
path_lc : str_path_lc.to_string(),
path_vmm : str_path_vmm.to_string(),
native,
parent_vmm : None,
};
return Ok(vmm);
}
}
fn impl_new_from_leechcore<'a>(leechcore_existing : &LeechCore, args: &Vec<&str>) -> ResultEx<Vmm<'a>> {
let path_vmm = std::path::Path::new(leechcore_existing.path_lc.as_str()).canonicalize()?;
let mut path_vmm = path_vmm.parent().unwrap().canonicalize()?;
if cfg!(windows) {
path_vmm = path_vmm.join("vmm.dll");
} else if cfg!(target_os = "macos") {
path_vmm = path_vmm.join("vmm.dylib");
} else {
path_vmm = path_vmm.join("vmm.so");
}
let str_path_vmm = path_vmm.to_str().unwrap_or("");
return crate::impl_new(str_path_vmm, Some(leechcore_existing), 0, args)
}
#[allow(non_snake_case)]
fn impl_new_from_virtual_machine<'a>(vmm_parent : &'a Vmm, vm_entry : &VmmMapVirtualMachineEntry) -> ResultEx<Vmm<'a>> {
if vmm_parent.native.h != vm_entry.h_vmm {
return Err(anyhow!("Invalid parent/vm relationship."));
}
let h_vmm_vm = (vmm_parent.native.VMMDLL_VmGetVmmHandle)(vmm_parent.native.h, vm_entry.h_vm);
if h_vmm_vm == 0 {
return Err(anyhow!("VMMDLL_VmGetVmmHandle: fail."));
}
let native = VmmNative {
h: vmm_parent.native.h,
library_lc : None,
library_vmm : None,
..vmm_parent.native
};
let vmm = Vmm {
path_lc : vmm_parent.path_lc.clone(),
path_vmm : vmm_parent.path_vmm.clone(),
native : native,
parent_vmm : Some(vmm_parent),
};
return Ok(vmm);
}
const MAX_PATH : usize = 260;
const VMMDLL_MEM_SEARCH_VERSION : u32 = 0xfe3e0003;
const VMMDLL_YARA_CONFIG_VERSION : u32 = 0xdec30001;
const VMMYARA_RULE_MATCH_VERSION : u32 = 0xfedc0005;
const VMMYARA_RULE_MATCH_TAG_MAX : usize = 27;
const VMMYARA_RULE_MATCH_META_MAX : usize = 32;
const VMMYARA_RULE_MATCH_STRING_MAX : usize = 16;
const VMMYARA_RULE_MATCH_OFFSET_MAX : usize = 24;
const VMMDLL_VFS_FILELIST_VERSION : u32 = 2;
const VMMDLL_MAP_EAT_VERSION : u32 = 3;
const VMMDLL_MAP_HANDLE_VERSION : u32 = 3;
const VMMDLL_MAP_HEAP_VERSION : u32 = 4;
const VMMDLL_MAP_HEAPALLOC_VERSION : u32 = 1;
const VMMDLL_MAP_IAT_VERSION : u32 = 2;
const VMMDLL_MAP_KDEVICE_VERSION : u32 = 1;
const VMMDLL_MAP_KDRIVER_VERSION : u32 = 1;
const VMMDLL_MAP_KOBJECT_VERSION : u32 = 1;
const VMMDLL_MAP_POOL_VERSION : u32 = 2;
const VMMDLL_MAP_PTE_VERSION : u32 = 2;
const VMMDLL_MAP_MODULE_VERSION : u32 = 6;
const VMMDLL_MAP_NET_VERSION : u32 = 3;
const VMMDLL_MAP_PFN_VERSION : u32 = 1;
const VMMDLL_MAP_PHYSMEM_VERSION : u32 = 2;
const VMMDLL_MAP_SERVICE_VERSION : u32 = 3;
const VMMDLL_MAP_THREAD_VERSION : u32 = 4;
const VMMDLL_MAP_THREAD_CALLSTACK_VERSION : u32 = 1;
const VMMDLL_MAP_UNLOADEDMODULE_VERSION : u32 = 2;
const VMMDLL_MAP_USER_VERSION : u32 = 2;
const VMMDLL_MAP_VAD_VERSION : u32 = 6;
const VMMDLL_MAP_VADEX_VERSION : u32 = 4;
const VMMDLL_MAP_VM_VERSION : u32 = 2;
const VMMDLL_MID_RUST : u32 = 0x80000004;
const VMMDLL_PLUGIN_CONTEXT_MAGIC : u64 = 0xc0ffee663df9301c;
const VMMDLL_PLUGIN_CONTEXT_VERSION : u16 = 5;
const VMMDLL_PLUGIN_REGINFO_MAGIC : u64 = 0xc0ffee663df9301d;
const VMMDLL_PLUGIN_REGINFO_VERSION : u16 = 18;
const VMMDLL_STATUS_SUCCESS : u32 = 0x00000000;
const VMMDLL_STATUS_END_OF_FILE : u32 = 0xC0000011;
const VMMDLL_STATUS_FILE_INVALID : u32 = 0xC0000098;
const VMMDLL_PROCESS_INFORMATION_MAGIC : u64 = 0xc0ffee663df9301e;
const VMMDLL_PROCESS_INFORMATION_VERSION : u16 = 7;
const VMMDLL_REGISTRY_HIVE_INFORMATION_MAGIC : u64 = 0xc0ffee653df8d01e;
const VMMDLL_REGISTRY_HIVE_INFORMATION_VERSION : u16 = 5;
const VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL : u32 = 1;
const VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE : u32 = 2;
const VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE : u32 = 3;
const DIRECTORY_NAMES : [&str; 16] = ["EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED"];
impl Drop for Vmm<'_> {
fn drop(&mut self) {
if self.native.is_close_h {
(self.native.VMMDLL_Close)(self.native.h);
}
}
}
impl Clone for Vmm<'_> {
fn clone(&self) -> Self {
let vmmid = self.get_config(CONFIG_OPT_CORE_VMM_ID).unwrap();
let vmmid_str = vmmid.to_string();
let vmm_clone_args = ["-create-from-vmmid", &vmmid_str].to_vec();
let vmm_clone = Vmm::new(&self.path_vmm, &vmm_clone_args).unwrap();
return vmm_clone;
}
}
impl fmt::Display for Vmm<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Vmm")
}
}
impl fmt::Display for VmmLogLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmLogLevel::_1Critical => "Critical(1)",
VmmLogLevel::_2Warning => "Warning(2)",
VmmLogLevel::_3Info => "Info(3)",
VmmLogLevel::_4Verbose => "Verbose(4)",
VmmLogLevel::_5Debug => "Debug(5)",
VmmLogLevel::_6Trace => "Trace(6)",
VmmLogLevel::_7None => "None(7)",
};
write!(f, "{v}")
}
}
impl From<u32> for VmmMemoryModelType {
fn from(v : u32) -> Self {
return match v {
1 => VmmMemoryModelType::X86,
2 => VmmMemoryModelType::X86PAE,
3 => VmmMemoryModelType::X64,
_ => VmmMemoryModelType::NA,
};
}
}
impl fmt::Display for VmmMemoryModelType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmMemoryModelType::NA => "NA",
VmmMemoryModelType::X86 => "X86",
VmmMemoryModelType::X86PAE => "X86PAE",
VmmMemoryModelType::X64 => "X64",
};
write!(f, "{v}")
}
}
impl From<u32> for VmmSystemType {
fn from(v : u32) -> Self {
return match v {
1 => VmmSystemType::UnknownX64,
2 => VmmSystemType::WindowsX64,
3 => VmmSystemType::UnknownX86,
4 => VmmSystemType::WindowsX86,
_ => VmmSystemType::UnknownPhysical,
};
}
}
impl fmt::Display for VmmSystemType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmSystemType::UnknownPhysical => "UnknownPhysical",
VmmSystemType::UnknownX64 => "UnknownX64",
VmmSystemType::WindowsX64 => "WindowsX64",
VmmSystemType::UnknownX86 => "UnknownX86",
VmmSystemType::WindowsX86 => "WindowsX86",
};
write!(f, "{v}")
}
}
impl From<u32> for VmmIntegrityLevelType {
fn from(v : u32) -> Self {
return match v {
1 => VmmIntegrityLevelType::Untrusted,
2 => VmmIntegrityLevelType::Low,
3 => VmmIntegrityLevelType::Medium,
4 => VmmIntegrityLevelType::MediumPlus,
5 => VmmIntegrityLevelType::High,
6 => VmmIntegrityLevelType::System,
7 => VmmIntegrityLevelType::Protected,
_ => VmmIntegrityLevelType::Unknown,
};
}
}
impl fmt::Display for VmmIntegrityLevelType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmIntegrityLevelType::Untrusted => "Untrusted",
VmmIntegrityLevelType::Low => "Low",
VmmIntegrityLevelType::Medium => "Medium",
VmmIntegrityLevelType::MediumPlus => "MediumPlus",
VmmIntegrityLevelType::High => "High",
VmmIntegrityLevelType::System => "System",
VmmIntegrityLevelType::Protected => "Protected",
VmmIntegrityLevelType::Unknown => "Unknown",
};
write!(f, "{v}")
}
}
impl From<u32> for VmmMapPfnType {
fn from(v : u32) -> Self {
return match v {
0 => VmmMapPfnType::Zero,
1 => VmmMapPfnType::Free,
2 => VmmMapPfnType::Standby,
3 => VmmMapPfnType::Modified,
4 => VmmMapPfnType::ModifiedNoWrite,
5 => VmmMapPfnType::Bad,
6 => VmmMapPfnType::Active,
_ => VmmMapPfnType::Transition,
};
}
}
impl fmt::Display for VmmMapPfnType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmMapPfnType::Zero => "Zero",
VmmMapPfnType::Free => "Free",
VmmMapPfnType::Standby => "Standby",
VmmMapPfnType::Modified => "Modified",
VmmMapPfnType::ModifiedNoWrite => "ModifiedNoWrite",
VmmMapPfnType::Bad => "Bad",
VmmMapPfnType::Active => "Active",
VmmMapPfnType::Transition => "Transition",
};
write!(f, "{v}")
}
}
impl From<u32> for VmmMapPfnTypeExtended {
fn from(v : u32) -> Self {
return match v {
1 => VmmMapPfnTypeExtended::Unused,
2 => VmmMapPfnTypeExtended::ProcessPrivate,
3 => VmmMapPfnTypeExtended::PageTable,
4 => VmmMapPfnTypeExtended::LargePage,
5 => VmmMapPfnTypeExtended::DriverLocked,
6 => VmmMapPfnTypeExtended::Shareable,
7 => VmmMapPfnTypeExtended::File,
_ => VmmMapPfnTypeExtended::Unknown,
};
}
}
impl fmt::Display for VmmMapPfnTypeExtended {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmMapPfnTypeExtended::Unused => "Unused",
VmmMapPfnTypeExtended::ProcessPrivate => "ProcessPrivate",
VmmMapPfnTypeExtended::PageTable => "PageTable",
VmmMapPfnTypeExtended::LargePage => "LargePage",
VmmMapPfnTypeExtended::DriverLocked => "DriverLocked",
VmmMapPfnTypeExtended::Shareable => "Shareable",
VmmMapPfnTypeExtended::File => "File",
VmmMapPfnTypeExtended::Unknown => "Unknown",
};
write!(f, "{v}")
}
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVmEntry {
hVM : usize,
uszName : *const c_char,
gpaMax : u64,
tp : u32,
fActive : bool,
fReadOnly : bool,
fPhysicalOnly : bool,
dwPartitionID : u32,
dwVersionBuild : u32,
tpSystem : u32,
dwParentVmmMountID : u32,
dwVmMemPID : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVmMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CVmEntry,
}
impl fmt::Display for VmmMapPfnEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapPfnEntry:{}", self.pfn)
}
}
impl fmt::Display for VmmMapMemoryEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapMemoryEntry:{:x}->{:x}", self.pa, self.pa + self.cb - 1)
}
}
impl fmt::Display for VmmMapNetEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapNetEntry:'{}'", self.desc)
}
}
impl PartialEq for VmmMapNetEntry {
fn eq(&self, other: &Self) -> bool {
self.va_object == other.va_object
}
}
impl fmt::Display for VmmMapKDeviceEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapKDeviceEntry:{:x}:'{}'", self.va, self.device_type_name)
}
}
impl PartialEq for VmmMapKDeviceEntry {
fn eq(&self, other: &Self) -> bool {
self.va == other.va
}
}
impl fmt::Display for VmmMapKDriverEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapKDriverEntry::{:x}'{}'", self.va, self.name)
}
}
impl PartialEq for VmmMapKDriverEntry {
fn eq(&self, other: &Self) -> bool {
self.va == other.va
}
}
impl fmt::Display for VmmMapKObjectEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapKObjectEntry:{:x}:{}:'{}'", self.va, self.object_type, self.name)
}
}
impl PartialEq for VmmMapKObjectEntry {
fn eq(&self, other: &Self) -> bool {
self.va == other.va
}
}
impl fmt::Display for VmmMapPoolEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapPoolEntry:'{}':{:x}", self.tag_to_string(), self.va)
}
}
impl PartialEq for VmmMapPoolEntry {
fn eq(&self, other: &Self) -> bool {
self.va == other.va
}
}
impl fmt::Display for VmmMapServiceEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapServiceEntry:{}", self.name)
}
}
impl fmt::Display for VmmMapUserEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapUserEntry:[{}]", self.user)
}
}
impl fmt::Display for VmmMapVirtualMachineEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmMapVirtualMachineEntry:[{}]", self.name)
}
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPfnEntry {
dwPfn : u32,
tpExtended : u32,
dwPfnPte : [u32; 5],
va : u64,
vaPte : u64,
OriginalPte : u64,
u3 : u32,
u4 : u64,
_FutureUse : [u32; 6],
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPfnMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
cMap : u32,
pMap : CPfnEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CNetMapEntry {
dwPID : u32,
dwState : u32,
_FutureUse3 : [u16; 3],
AF : u16,
src_fValid : bool,
src__Reserved : u16,
src_port : u16,
src_pbAddr : [u8; 16],
src_uszText : *const c_char,
dst_fValid : bool,
dst__Reserved : u16,
dst_port : u16,
dst_pbAddr : [u8; 16],
dst_uszText : *const c_char,
vaObj : u64,
ftTime : u64,
dwPoolTag : u32,
_FutureUse4 : u32,
uszText : *const c_char,
_FutureUse2 : [u32; 4],
}
#[repr(C)]
#[allow(non_snake_case)]
struct CNetMap {
dwVersion : u32,
_Reserved1 : u32,
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CNetMapEntry,
}
#[repr(C)]
struct CMemoryMapEntry {
pa : u64,
cb : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CMemoryMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
cMap : u32,
_Reserved2 : u32,
pMap : CMemoryMapEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKDeviceEntry {
va : u64,
iDepth : u32,
dwDeviceType : u32,
uszDeviceType : *const c_char,
vaDriverObject : u64,
vaAttachedDevice : u64,
vaFileSystemDevice : u64,
uszVolumeInfo : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKDeviceMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CKDeviceEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKDriverEntry {
va : u64,
vaDriverStart : u64,
cbDriverSize : u64,
vaDeviceObject : u64,
uszName : *const c_char,
uszPath : *const c_char,
uszServiceKeyName : *const c_char,
MajorFunction : [u64; 28],
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKDriverMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CKDriverEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKObjectEntry {
va : u64,
vaParent : u64,
_Filler : u32,
cvaChild : u32,
pvaChild : *const u64,
uszName : *const c_char,
uszType : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CKObjectMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CKObjectEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPoolEntry {
va : u64,
dwTag : u32,
_ReservedZero : u8,
fAlloc : u8,
tpPool : u8,
tpSS : u8,
cb : u32,
_Filler : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPoolMap {
dwVersion : u32,
_Reserved1 : [u32; 6],
cbTotal : u32,
piTag2Map : usize, pTag : usize, cTag : u32,
cMap : u32,
pMap : CPoolEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CServiceEntry {
vaObj : u64,
dwOrdinal : u32,
dwStartType : u32,
dwServiceType : u32,
dwCurrentState : u32,
dwControlsAccepted : u32,
dwWin32ExitCode : u32,
dwServiceSpecificExitCode : u32,
dwCheckPoint : u32,
wWaitHint : u32,
uszServiceName : *const c_char,
uszDisplayName : *const c_char,
uszPath : *const c_char,
uszUserTp : *const c_char,
uszUserAcct : *const c_char,
uszImagePath : *const c_char,
dwPID : u32,
_FutureUse1 : u32,
_FutureUse2 : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CServiceMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CServiceEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CUserEntry {
_FutureUse1 : [u32; 2],
uszText : *const c_char,
vaRegHive : u64,
uszSID : *const c_char,
_FutureUse2 : [u32; 2],
}
#[repr(C)]
#[allow(non_snake_case)]
struct CUserMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CUserEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CRegHive {
magic : u64,
wVersion : u16,
wSize : u16,
_FutureReserved1 : [u8; 0x34],
vaCMHIVE : u64,
vaHBASE_BLOCK : u64,
cbLength : u32,
uszName : [i8; 128],
uszNameShort : [i8; 32 + 1],
uszHiveRootPath : [i8; 260],
_FutureReserved : [u64; 0x10],
}
impl fmt::Display for VmmVfsEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_directory {
write!(f, "VmmVfsEntry:D:'{}'", self.name,)
} else {
write!(f, "VmmVfsEntry:F:'{}':0x{:x}", self.name, self.size)
}
}
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
struct CVMMDLL_VFS_FILELIST2 {
dwVersion : u32,
pfnAddFile : extern "C" fn(h : &mut Vec<VmmVfsEntry>, uszName : *const c_char, cb : u64, pExInfo : usize),
pfnAddDirectory : extern "C" fn(h : &mut Vec<VmmVfsEntry>, uszName : *const c_char, pExInfo : usize),
h : *mut Vec<VmmVfsEntry>,
}
extern "C" fn vfs_list_addfile_cb(h : &mut Vec<VmmVfsEntry>, name : *const c_char, cb : u64, _p_ex_info : usize) {
unsafe {
if name.is_null() { return; }
if let Ok(name) = CStr::from_ptr(name).to_str() {
let e = VmmVfsEntry {
name : name.to_string(),
is_directory : false,
size : cb,
};
h.push(e);
}
}
}
extern "C" fn vfs_list_adddirectory_cb(h : &mut Vec<VmmVfsEntry>, name : *const c_char, _p_ex_info : usize) {
unsafe {
if name.is_null() { return; }
if let Ok(name) = CStr::from_ptr(name).to_str() {
let e = VmmVfsEntry {
name : name.to_string(),
is_directory : true,
size : 0,
};
h.push(e);
}
}
}
unsafe fn cstr_to_string(sz : *const c_char) -> String {
return if sz.is_null() {
String::from("")
} else {
String::from(CStr::from_ptr(sz).to_str().unwrap_or(""))
};
}
unsafe fn cstr_to_string_lossy(sz : *const c_char) -> String {
return if sz.is_null() {
String::from("")
} else {
String::from_utf8_lossy(CStr::from_ptr(sz).to_bytes()).to_string()
};
}
macro_rules! impl_map_get {
($native:expr, $structs:ident, $version_const:expr, |$ne:ident| $convert:block) => {{
unsafe {
if (*$structs).dwVersion != $version_const {
($native.VMMDLL_MemFree)($structs as usize);
return Err(anyhow!("bad version [{} != {}].", (*$structs).dwVersion, $version_const));
}
let mut result = Vec::new();
if (*$structs).cMap == 0 {
($native.VMMDLL_MemFree)($structs as usize);
return Ok(result);
}
let cMap: usize = (*$structs).cMap.try_into()?;
let pMap = std::slice::from_raw_parts(&(*$structs).pMap, cMap);
for i in 0..cMap {
let $ne = &pMap[i];
result.push($convert);
}
($native.VMMDLL_MemFree)($structs as usize);
return Ok(result);
}
}};
}
#[allow(non_snake_case)]
impl Vmm<'_> {
fn impl_get_leechcore(&self) -> ResultEx<LeechCore> {
let lc_handle = self.get_config(CONFIG_OPT_CORE_LEECHCORE_HANDLE)?;
let lc_lib_path = self.path_lc.as_str();
let device_config_string = format!("existing://0x{:x}", lc_handle);
return LeechCore::new(lc_lib_path, device_config_string.as_str(), 0);
}
fn impl_log(&self, log_mid : u32, log_level : &VmmLogLevel, log_message : &str) {
let c_loglevel : u32 = match log_level {
VmmLogLevel::_1Critical => 1,
VmmLogLevel::_2Warning => 2,
VmmLogLevel::_3Info => 3,
VmmLogLevel::_4Verbose => 4,
VmmLogLevel::_5Debug => 5,
VmmLogLevel::_6Trace => 6,
VmmLogLevel::_7None => 7,
};
let sz_log_fmt = CString::new("%s").unwrap();
let sz_log_message = CString::new(log_message).unwrap();
let _r = (self.native.VMMDLL_Log)(self.native.h, log_mid, c_loglevel, sz_log_fmt.as_ptr(), sz_log_message.as_ptr());
}
fn impl_get_config(&self, config_id : u64) -> ResultEx<u64> {
let mut v = 0;
let f = (self.native.VMMDLL_ConfigGet)(self.native.h, config_id, &mut v);
return if f { Ok(v) } else { Err(anyhow!("VMMDLL_ConfigGet: fail")) };
}
fn impl_set_config(&self, config_id : u64, config_value : u64) -> ResultEx<()> {
let f = (self.native.VMMDLL_ConfigSet)(self.native.h, config_id, config_value);
return if f { Ok(()) } else { Err(anyhow!("VMMDLL_ConfigSet: fail")) };
}
fn impl_process_from_pid(&self, pid : u32) -> ResultEx<VmmProcess> {
let process_list = self.process_list()?;
let process = VmmProcess {
vmm : &self,
pid : pid,
};
if process_list.contains(&process) {
return Ok(process);
}
return Err(anyhow!("VMMDLL_PidGetFromName: fail. PID '{pid}' does not exist."));
}
fn impl_process_from_name(&self, process_name : &str) -> ResultEx<VmmProcess> {
let mut pid = 0;
let sz_process_name = CString::new(process_name)?;
let r = (self.native.VMMDLL_PidGetFromName)(self.native.h, sz_process_name.as_ptr(), &mut pid);
if !r {
return Err(anyhow!("VMMDLL_PidGetFromName: fail. Process '{process_name}' does not exist."));
}
return Ok(VmmProcess {
vmm : &self,
pid : pid,
});
}
fn impl_process_list(&self) -> ResultEx<Vec<VmmProcess>> {
let mut cpids : usize = 0;
let r = (self.native.VMMDLL_PidList)(self.native.h, std::ptr::null_mut(), &mut cpids);
if !r || cpids > 0x00100000 {
return Err(anyhow!("VMMDLL_PidList: fail."));
}
let mut pids = vec![0u32; cpids];
let r = (self.native.VMMDLL_PidList)(self.native.h, pids.as_mut_ptr(), &mut cpids);
if !r || cpids > 0x00100000 {
return Err(anyhow!("VMMDLL_PidList: fail."));
}
let mut proclist = Vec::new();
for i in 0..cpids {
let proc = VmmProcess {
vmm : self,
pid : *pids.get(i).unwrap(),
};
proclist.push(proc);
}
return Ok(proclist);
}
fn impl_map_pfn(&self, pfns : &Vec<u32>, is_extended : bool) -> ResultEx<Vec<VmmMapPfnEntry>> {
let mut structs = std::ptr::null_mut();
let flags = if is_extended { 1 } else { 0 };
let r = (self.native.VMMDLL_Map_GetPfnEx)(self.native.h, pfns.as_ptr(), u32::try_from(pfns.len())?, &mut structs, flags);
if !r {
return Err(anyhow!("VMMDLL_Map_GetPfnEx: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_PFN_VERSION, |ne| {
VmmMapPfnEntry {
pfn : ne.dwPfn,
location : VmmMapPfnType::from((ne.u3 >> 16) & 7),
is_prototype : if ne.u4 & 0x0200000000000000 > 0 { true } else { false },
color : u32::try_from(ne.u4 >> 58)?,
is_extended : is_extended,
tp_ex : VmmMapPfnTypeExtended::from(ne.tpExtended),
pid : ne.dwPfnPte[0],
ptes : [0, ne.dwPfnPte[1], ne.dwPfnPte[2], ne.dwPfnPte[3], ne.dwPfnPte[4]],
va : ne.va,
va_pte : ne.vaPte,
pte_original : ne.OriginalPte,
}
})
}
fn impl_map_memory(&self) -> ResultEx<Vec<VmmMapMemoryEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetPhysMem)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetPhysMem: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_PHYSMEM_VERSION, |ne| {
VmmMapMemoryEntry {
pa : ne.pa,
cb : ne.cb,
}
})
}
fn impl_map_net(&self) -> ResultEx<Vec<VmmMapNetEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetNetU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetNetU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_NET_VERSION, |ne| {
VmmMapNetEntry {
pid : ne.dwPID,
state : ne.dwState,
address_family : ne.AF,
src_is_valid : ne.src_fValid,
src_port : ne.src_port,
src_addr_raw : ne.src_pbAddr,
src_str : cstr_to_string(ne.src_uszText),
dst_is_valid : ne.dst_fValid,
dst_port : ne.dst_port,
dst_addr_raw : ne.dst_pbAddr,
dst_str : cstr_to_string(ne.dst_uszText),
va_object : ne.vaObj,
filetime : ne.ftTime,
pool_tag : ne.dwPoolTag,
desc : cstr_to_string(ne.uszText),
}
})
}
fn impl_map_kdevice(&self) -> ResultEx<Vec<VmmMapKDeviceEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetKDeviceU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetKDeviceU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_KDEVICE_VERSION, |ne| {
VmmMapKDeviceEntry {
va : ne.va,
depth : ne.iDepth,
device_type : ne.dwDeviceType,
device_type_name : cstr_to_string(ne.uszDeviceType),
va_driver_object : ne.vaDriverObject,
va_attached_device : ne.vaAttachedDevice,
va_file_system_device : ne.vaFileSystemDevice,
volume_info : cstr_to_string(ne.uszVolumeInfo),
}
})
}
fn impl_map_kdriver(&self) -> ResultEx<Vec<VmmMapKDriverEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetKDriverU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetKDriverU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_KDRIVER_VERSION, |ne| {
VmmMapKDriverEntry {
va : ne.va,
va_driver_start : ne.vaDriverStart,
cb_driver_size : ne.cbDriverSize,
va_device_object : ne.vaDeviceObject,
name : cstr_to_string(ne.uszName),
path : cstr_to_string(ne.uszPath),
service_key_name : cstr_to_string(ne.uszServiceKeyName),
major_function : ne.MajorFunction,
}
})
}
fn impl_map_kobject(&self) -> ResultEx<Vec<VmmMapKObjectEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetKObjectU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetKObjectU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_KOBJECT_VERSION, |ne| {
let mut child_vec = Vec::new();
let child_count = ne.cvaChild as usize;
let child_ptr = std::slice::from_raw_parts(ne.pvaChild, ne.cvaChild as usize);
for j in 0..child_count {
child_vec.push(child_ptr[j]);
}
VmmMapKObjectEntry {
va : ne.va,
va_parent : ne.vaParent,
children : child_vec,
name : cstr_to_string(ne.uszName),
object_type : cstr_to_string(ne.uszType),
}
})
}
fn impl_map_pool(&self, is_bigpool_only : bool) -> ResultEx<Vec<VmmMapPoolEntry>> {
let mut structs = std::ptr::null_mut();
let flags = if is_bigpool_only { 1 } else { 0 };
let r = (self.native.VMMDLL_Map_GetPool)(self.native.h, &mut structs, flags);
if !r {
return Err(anyhow!("VMMDLL_Map_GetPool: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_POOL_VERSION, |ne| {
VmmMapPoolEntry {
va : ne.va,
cb : ne.cb,
tag : ne.dwTag,
is_alloc : ne.fAlloc != 0,
tp_pool : ne.tpPool,
tp_subsegment : ne.tpSS,
}
})
}
fn impl_map_service(&self) -> ResultEx<Vec<VmmMapServiceEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetServicesU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetServicesU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_SERVICE_VERSION, |ne| {
VmmMapServiceEntry {
ordinal : ne.dwOrdinal,
va_object : ne.vaObj,
pid : ne.dwPID,
start_type : ne.dwStartType,
service_type : ne.dwServiceType,
current_state : ne.dwCurrentState,
controls_accepted : ne.dwControlsAccepted,
win32_exit_code : ne.dwWin32ExitCode,
service_specific_exit_code : ne.dwServiceSpecificExitCode,
check_point : ne.dwCheckPoint,
wait_hint : ne.wWaitHint,
name : cstr_to_string(ne.uszServiceName),
name_display : cstr_to_string(ne.uszDisplayName),
path : cstr_to_string(ne.uszPath),
user_type : cstr_to_string(ne.uszUserTp),
user_account : cstr_to_string(ne.uszUserAcct),
image_path : cstr_to_string(ne.uszImagePath),
}
})
}
fn impl_map_user(&self) -> ResultEx<Vec<VmmMapUserEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetUsersU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetUsersU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_USER_VERSION, |ne| {
VmmMapUserEntry {
user : cstr_to_string(ne.uszText),
sid : cstr_to_string(ne.uszSID),
va_reg_hive : ne.vaRegHive,
}
})
}
fn impl_map_virtual_machine(&self) -> ResultEx<Vec<VmmMapVirtualMachineEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.native.VMMDLL_Map_GetVMU)(self.native.h, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetVMU: fail."));
}
impl_map_get!(self.native, structs, VMMDLL_MAP_VM_VERSION, |ne| {
VmmMapVirtualMachineEntry {
h_vmm : self.native.h,
h_vm : ne.hVM,
name : cstr_to_string(ne.uszName),
gpa_max : ne.gpaMax,
tp_vm : ne.tp,
is_active : ne.fActive,
is_readonly : ne.fReadOnly,
is_physicalonly : ne.fPhysicalOnly,
partition_id : ne.dwPartitionID,
guest_os_version_build : ne.dwVersionBuild,
guest_tp_system : ne.tpSystem,
parent_mount_id : ne.dwParentVmmMountID,
vmmem_pid : ne.dwVmMemPID,
}
})
}
fn impl_mem_read(&self, pid : u32, va : u64, size : usize, flags : u64) -> ResultEx<Vec<u8>> {
let cb = u32::try_from(size)?;
let mut cb_read = 0;
let mut pb_result = vec![0u8; size];
let r = (self.native.VMMDLL_MemReadEx)(self.native.h, pid, va, pb_result.as_mut_ptr(), cb, &mut cb_read, flags);
if !r {
return Err(anyhow!("VMMDLL_MemReadEx: fail."));
}
return Ok(pb_result);
}
fn impl_mem_read_into(&self, pid : u32, va : u64, flags : u64, data : &mut [u8]) -> ResultEx<usize> {
let cb = u32::try_from(data.len())?;
let mut cb_read = 0;
let r = (self.native.VMMDLL_MemReadEx)(self.native.h, pid, va, data.as_mut_ptr(), cb, &mut cb_read, flags);
if !r {
return Err(anyhow!("VMMDLL_MemReadEx: fail."));
}
return Ok(cb_read as usize);
}
fn impl_mem_read_as<T>(&self, pid : u32, va : u64, flags : u64) -> ResultEx<T> {
unsafe {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let mut cb_read = 0;
let mut result : T = std::mem::zeroed();
let r = (self.native.VMMDLL_MemReadEx)(self.native.h, pid, va, &mut result as *mut _ as *mut u8, cb, &mut cb_read, flags);
if !r {
return Err(anyhow!("VMMDLL_MemReadEx: fail."));
}
return Ok(result);
}
}
fn impl_mem_scatter(&self, pid : u32, flags : u64) -> ResultEx<VmmScatterMemory> {
let flags = u32::try_from(flags)?;
let r = (self.native.VMMDLL_Scatter_Initialize)(self.native.h, pid, flags);
if r == 0 {
return Err(anyhow!("VMMDLL_Scatter_Initialize: fail."));
}
return Ok(VmmScatterMemory {
vmm : &self,
hs : r,
pid,
flags,
is_scatter_ex : false,
});
}
fn impl_mem_virt2phys(&self, pid : u32, va : u64) -> ResultEx<u64> {
let mut pa : u64 = 0;
let r = (self.native.VMMDLL_MemVirt2Phys)(self.native.h, pid, va, &mut pa);
if !r {
return Err(anyhow!("VMMDLL_MemVirt2Phys: fail."));
}
return Ok(pa);
}
fn impl_mem_write(&self, pid : u32, va : u64, data : &[u8]) -> ResultEx<()> {
let cb = u32::try_from(data.len())?;
let pb = data.as_ptr();
let r = (self.native.VMMDLL_MemWrite)(self.native.h, pid, va, pb, cb);
if !r {
return Err(anyhow!("VMMDLL_MemWrite: fail."));
}
return Ok(());
}
fn impl_mem_write_as<T>(&self, pid : u32, va : u64, data : &T) -> ResultEx<()> {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let r = (self.native.VMMDLL_MemWrite)(self.native.h, pid, va, data as *const _ as *const u8, cb);
if !r {
return Err(anyhow!("VMMDLL_MemWrite: fail."));
}
return Ok(());
}
fn impl_vfs_list(&self, path : &str) -> ResultEx<Vec<VmmVfsEntry>> {
let c_path = CString::new(str::replace(path, "/", "\\"))?;
let mut vec_result : Vec<VmmVfsEntry> = Vec::new();
let ptr_result : *mut Vec<VmmVfsEntry> = &mut vec_result;
let mut filelist2 = CVMMDLL_VFS_FILELIST2 {
dwVersion : VMMDLL_VFS_FILELIST_VERSION,
pfnAddFile : vfs_list_addfile_cb,
pfnAddDirectory : vfs_list_adddirectory_cb,
h : ptr_result,
};
let r = (self.native.VMMDLL_VfsListU)(self.native.h, c_path.as_ptr(), &mut filelist2);
if !r {
return Err(anyhow!("VMMDLL_VfsListU: fail."));
}
return Ok(vec_result);
}
fn impl_vfs_read(&self, filename : &str, size : u32, offset : u64) -> ResultEx<Vec<u8>> {
let c_filename = CString::new(str::replace(filename, "/", "\\"))?;
let mut cb_read = 0u32;
let mut data = vec![0u8; size as usize];
let ntstatus = (self.native.VMMDLL_VfsReadU)(self.native.h, c_filename.as_ptr(), data.as_mut_ptr(), size, &mut cb_read, offset);
if ntstatus != 0 && ntstatus != 0xC0000011 {
return Err(anyhow!("VMMDLL_VfsReadU: fail."));
}
if cb_read < size {
data.resize(cb_read as usize, 0);
}
return Ok(data);
}
fn impl_vfs_write(&self, filename : &str, data : &[u8], offset : u64) {
if data.len() < u32::MAX as usize {
let c_filename = CString::new(str::replace(filename, "/", "\\")).unwrap();
let mut cb_write = 0u32;
let _ntstatus = (self.native.VMMDLL_VfsWriteU)(self.native.h, c_filename.as_ptr(), data.as_ptr(), data.len() as u32, &mut cb_write, offset);
}
}
fn impl_reg_hive_list(&self) -> ResultEx<Vec<VmmRegHive>> {
unsafe {
let mut cHives = 0;
let r = (self.native.VMMDLL_WinReg_HiveList)(self.native.h, std::ptr::null_mut(), 0, &mut cHives);
if !r {
return Err(anyhow!("VMMDLL_WinReg_HiveList: fail."));
}
if cHives == 0 {
return Ok(Vec::new());
}
let size = std::mem::size_of::<CRegHive>();
let mut bytes = vec![0u8; size * cHives as usize];
let ptr = bytes.as_mut_ptr() as *mut CRegHive;
let r = (self.native.VMMDLL_WinReg_HiveList)(self.native.h, ptr, cHives, &mut cHives);
if !r {
return Err(anyhow!("VMMDLL_WinReg_HiveList: fail."));
}
if cHives == 0 {
return Ok(Vec::new());
}
let mut result = Vec::new();
let pMap = std::slice::from_raw_parts(ptr, cHives as usize);
for i in 0..cHives as usize {
let ne = &pMap[i];
if (ne.magic != VMMDLL_REGISTRY_HIVE_INFORMATION_MAGIC) || (ne.wVersion != VMMDLL_REGISTRY_HIVE_INFORMATION_VERSION) {
return Err(anyhow!("Hive Bad Version."));
}
let e = VmmRegHive {
vmm : &self,
va : ne.vaCMHIVE,
va_baseblock : ne.vaHBASE_BLOCK,
size : ne.cbLength,
name : cstr_to_string_lossy(ne.uszName.as_ptr() as *const c_char),
name_short : cstr_to_string_lossy(ne.uszNameShort.as_ptr() as *const c_char),
path : cstr_to_string_lossy(ne.uszHiveRootPath.as_ptr() as *const c_char),
};
result.push(e);
}
return Ok(result);
}
}
fn impl_reg_pathsplit(path : &str) -> ResultEx<(&str, &str)> {
let path = path.trim_end_matches('\\');
if let Some(split) = path.rsplit_once('\\') {
if (split.0.len() > 0) && (split.1.len() > 0) {
return Ok(split);
}
}
return Err(anyhow!("[err]"));
}
fn impl_reg_key(&self, path : &str) -> ResultEx<VmmRegKey> {
let mut ftLastWrite = 0;
let mut cch = 0;
let c_path = CString::new(path)?;
let r = (self.native.VMMDLL_WinReg_EnumKeyExU)(self.native.h, c_path.as_ptr(), u32::MAX, std::ptr::null_mut(), &mut cch, &mut ftLastWrite);
if !r {
return Err(anyhow!("VMMDLL_WinReg_EnumKeyExU: fail."));
}
let pathname = Vmm::impl_reg_pathsplit(path)?;
let result = VmmRegKey {
vmm : &self,
name : String::from(pathname.1),
path : String::from(path),
ft_last_write : ftLastWrite,
};
return Ok(result);
}
fn impl_reg_value(&self, path : &str) -> ResultEx<VmmRegValue> {
let mut raw_value = None;
let mut raw_type = 0;
let mut v = [0u8; 64];
let mut raw_size = v.len() as u32;
let c_path = CString::new(path)?;
let r = (self.native.VMMDLL_WinReg_QueryValueExU)(self.native.h, c_path.as_ptr(), &mut raw_type, v.as_mut_ptr(), &mut raw_size);
if !r {
return Err(anyhow!("VMMDLL_WinReg_QueryValueExU: fail."));
}
if raw_size < v.len() as u32 {
raw_value = Some(v[0..raw_size as usize].to_vec());
} else {
let r = (self.native.VMMDLL_WinReg_QueryValueExU)(self.native.h, c_path.as_ptr(), std::ptr::null_mut(), std::ptr::null_mut(), &mut raw_size);
if !r {
return Err(anyhow!("VMMDLL_WinReg_QueryValueExU: fail."));
}
}
let pathname = Vmm::impl_reg_pathsplit(path)?;
let result = VmmRegValue {
vmm : &self,
name : String::from(pathname.1),
path : String::from(path),
raw_type,
raw_size,
raw_value,
};
return Ok(result);
}
}
impl fmt::Display for VmmKernel<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmKernel")
}
}
impl fmt::Display for VmmPdb<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmPdb:{}", self.module)
}
}
impl VmmPdb<'_> {
fn impl_symbol_name_from_address(&self, va_or_offset : u64) -> ResultEx<(String, u32)> {
let c_module = CString::new(self.module.as_str())?;
let mut c_symbol_name = [0 as c_char; MAX_PATH];
let mut result_symbol_displacement = 0;
let r = (self.vmm.native.VMMDLL_PdbSymbolName)(self.vmm.native.h, c_module.as_ptr(), va_or_offset, c_symbol_name.as_mut_ptr(), &mut result_symbol_displacement);
if !r {
return Err(anyhow!("VMMDLL_PdbSymbolName: fail."));
}
let string_symbol_name = unsafe { cstr_to_string_lossy(c_symbol_name.as_ptr()) };
return Ok((string_symbol_name, result_symbol_displacement));
}
fn impl_symbol_address_from_name(&self, symbol_name : &str) -> ResultEx<u64> {
let c_module = CString::new(self.module.as_str())?;
let c_symbol_name = CString::new(symbol_name)?;
let mut result = 0;
let r = (self.vmm.native.VMMDLL_PdbSymbolAddress)(self.vmm.native.h, c_module.as_ptr(), c_symbol_name.as_ptr(), &mut result);
if !r {
return Err(anyhow!("VMMDLL_PdbSymbolAddress: fail."));
}
return Ok(result);
}
fn impl_type_size(&self, type_name : &str) -> ResultEx<u32> {
let c_module = CString::new(self.module.as_str())?;
let c_type_name = CString::new(type_name)?;
let mut result = 0;
let r = (self.vmm.native.VMMDLL_PdbTypeSize)(self.vmm.native.h, c_module.as_ptr(), c_type_name.as_ptr(), &mut result);
if !r {
return Err(anyhow!("VMMDLL_PdbTypeSize: fail."));
}
return Ok(result);
}
fn impl_type_child_offset(&self, type_name : &str, type_child_name : &str) -> ResultEx<u32> {
let c_module = CString::new(self.module.as_str())?;
let c_type_name = CString::new(type_name)?;
let c_type_child_name = CString::new(type_child_name)?;
let mut result = 0;
let r = (self.vmm.native.VMMDLL_PdbTypeChildOffset)(self.vmm.native.h, c_module.as_ptr(), c_type_name.as_ptr(), c_type_child_name.as_ptr(), &mut result);
if !r {
return Err(anyhow!("VMMDLL_PdbTypeChildOffset: fail."));
}
return Ok(result);
}
}
impl fmt::Display for VmmRegHive<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmRegHive:{:x}", self.va)
}
}
impl PartialEq for VmmRegHive<'_> {
fn eq(&self, other: &Self) -> bool {
self.va == other.va
}
}
impl fmt::Display for VmmRegKey<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmRegKey:{}", self.name)
}
}
impl PartialEq for VmmRegKey<'_> {
fn eq(&self, other: &Self) -> bool {
self.name.eq(&other.name)
}
}
impl fmt::Display for VmmRegValueType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmRegValueType::REG_NONE => "REG_NONE".to_string(),
VmmRegValueType::REG_SZ(r) => format!("REG_SZ({r})"),
VmmRegValueType::REG_EXPAND_SZ(_) => "REG_EXPAND_SZ".to_string(),
VmmRegValueType::REG_BINARY(_) => "REG_BINARY".to_string(),
VmmRegValueType::REG_DWORD(r) => format!("REG_DWORD(0x{:x})", r),
VmmRegValueType::REG_DWORD_BIG_ENDIAN(r) => format!("REG_DWORD_BIG_ENDIAN(0x{:x})", r),
VmmRegValueType::REG_LINK(r) => format!("REG_LINK({r})"),
VmmRegValueType::REG_MULTI_SZ(_) => "REG_MULTI_SZ".to_string(),
VmmRegValueType::REG_RESOURCE_LIST(_) => "REG_RESOURCE_LIST".to_string(),
VmmRegValueType::REG_FULL_RESOURCE_DESCRIPTOR(_) => "REG_FULL_RESOURCE_DESCRIPTOR".to_string(),
VmmRegValueType::REG_RESOURCE_REQUIREMENTS_LIST(_) => "REG_RESOURCE_REQUIREMENTS_LIST".to_string(),
VmmRegValueType::REG_QWORD(r) => format!("REG_QWORD(0x{:x})", r),
};
write!(f, "{v}")
}
}
impl fmt::Display for VmmRegValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmRegValue:{}", self.name)
}
}
impl PartialEq for VmmRegValue<'_> {
fn eq(&self, other: &Self) -> bool {
self.name.eq(&other.name)
}
}
impl VmmRegHive<'_> {
fn impl_reg_hive_read(&self, ra : u32, size : usize, flags : u64) -> ResultEx<Vec<u8>> {
let cb = u32::try_from(size)?;
let mut cb_read = 0;
let mut pb_result = vec![0u8; size];
let r = (self.vmm.native.VMMDLL_WinReg_HiveReadEx)(self.vmm.native.h, self.va, ra, pb_result.as_mut_ptr(), cb, &mut cb_read, flags);
if !r {
return Err(anyhow!("VMMDLL_WinReg_HiveReadEx: fail."));
}
return Ok(pb_result);
}
fn impl_reg_hive_write(&self, ra : u32, data : &[u8]) -> ResultEx<()> {
let cb = u32::try_from(data.len())?;
let pb = data.as_ptr();
let r = (self.vmm.native.VMMDLL_WinReg_HiveWrite)(self.vmm.native.h, self.va, ra, pb, cb);
if !r {
return Err(anyhow!("VMMDLL_WinReg_HiveWrite: fail."));
}
return Ok(());
}
}
impl VmmRegKey<'_> {
fn impl_parent(&self) -> ResultEx<VmmRegKey> {
let pathfile = Vmm::impl_reg_pathsplit(self.path.as_str())?;
let result = self.vmm.impl_reg_key(pathfile.0)?;
return Ok(result);
}
#[allow(unused_assignments)]
fn impl_subkeys(&self) -> ResultEx<Vec<VmmRegKey>> {
unsafe {
let mut ft_last_write = 0;
let mut cch = 0;
let mut i = 0;
let mut data = [0; MAX_PATH+1];
let c_path = CString::new(self.path.as_str())?;
let mut result = Vec::new();
loop {
cch = data.len() as u32 - 1;
let r = (self.vmm.native.VMMDLL_WinReg_EnumKeyExU)(self.vmm.native.h, c_path.as_ptr(), i, data.as_mut_ptr(), &mut cch, &mut ft_last_write);
if !r {
break;
}
let name = cstr_to_string_lossy(data.as_ptr());
let path = format!("{}\\{}", self.path, name);
let e = VmmRegKey {
vmm : self.vmm,
name,
path,
ft_last_write,
};
result.push(e);
i += 1;
}
return Ok(result);
}
}
fn impl_values(&self) -> ResultEx<Vec<VmmRegValue>> {
return Err(anyhow!("Not implemented"));
}
}
impl VmmRegValue<'_> {
fn impl_parent(&self) -> ResultEx<VmmRegKey> {
let pathfile = Vmm::impl_reg_pathsplit(self.path.as_str())?;
let result = self.vmm.impl_reg_key(pathfile.0)?;
return Ok(result);
}
fn impl_raw_value(&self) -> ResultEx<Vec<u8>> {
if self.raw_value.is_some() {
return Ok(self.raw_value.as_ref().unwrap().clone());
}
if self.raw_size > 0x01000000 {
return Err(anyhow!("VmmRegKey size too large (>16MB)."));
}
let mut raw_value = vec![0; self.raw_size as usize];
let c_path = CString::new(self.path.clone())?;
let mut raw_size = self.raw_size;
let r = (self.vmm.native.VMMDLL_WinReg_QueryValueExU)(self.vmm.native.h, c_path.as_ptr(), std::ptr::null_mut(), raw_value.as_mut_ptr(), &mut raw_size);
if !r {
return Err(anyhow!("VMMDLL_WinReg_QueryValueExU: fail."));
}
return Ok(raw_value);
}
fn impl_value(&self) -> ResultEx<VmmRegValueType> {
const REG_NONE : u32 = 0;
const REG_SZ : u32 = 1;
const REG_EXPAND_SZ : u32 = 2;
const REG_BINARY : u32 = 3;
const REG_DWORD : u32 = 4;
const REG_DWORD_BIG_ENDIAN : u32 = 5;
const REG_LINK : u32 = 6;
const REG_MULTI_SZ : u32 = 7;
const REG_RESOURCE_LIST : u32 = 8;
const REG_FULL_RESOURCE_DESCRIPTOR : u32 = 9;
const REG_RESOURCE_REQUIREMENTS_LIST: u32 = 10;
const REG_QWORD : u32 = 11;
if self.raw_type == REG_NONE {
return Ok(VmmRegValueType::REG_NONE);
}
if self.raw_type > REG_QWORD {
return Err(anyhow!("Unknown registry value type."));
}
let raw_value = self.raw_value()?;
match self.raw_type {
REG_BINARY => return Ok(VmmRegValueType::REG_BINARY(raw_value)),
REG_RESOURCE_LIST => return Ok(VmmRegValueType::REG_RESOURCE_LIST(raw_value)),
REG_FULL_RESOURCE_DESCRIPTOR => return Ok(VmmRegValueType::REG_FULL_RESOURCE_DESCRIPTOR(raw_value)),
REG_RESOURCE_REQUIREMENTS_LIST => return Ok(VmmRegValueType::REG_RESOURCE_REQUIREMENTS_LIST(raw_value)),
_ => (),
};
if self.raw_type == REG_DWORD {
let v : [u8; 4] = raw_value.as_slice().try_into()?;
return Ok(VmmRegValueType::REG_DWORD(u32::from_le_bytes(v)));
}
if self.raw_type == REG_DWORD_BIG_ENDIAN {
let v : [u8; 4] = raw_value.as_slice().try_into()?;
return Ok(VmmRegValueType::REG_DWORD_BIG_ENDIAN(u32::from_be_bytes(v)));
}
if self.raw_type == REG_QWORD {
let v : [u8; 8] = raw_value.as_slice().try_into()?;
return Ok(VmmRegValueType::REG_QWORD(u64::from_le_bytes(v)));
}
if raw_value.len() % 2 == 1 {
return Err(anyhow!("Invalid size"));
}
let mut raw_chars = vec![0u16; raw_value.len() / 2];
unsafe {
std::ptr::copy_nonoverlapping(raw_value.as_ptr(), raw_chars.as_mut_ptr() as *mut u8, raw_value.len());
}
if self.raw_type == REG_MULTI_SZ {
let mut result_vec = Vec::new();
for raw_string in raw_chars.split(|v| *v == 0) {
if raw_string.len() > 0 {
result_vec.push(String::from_utf16_lossy(raw_string));
}
}
return Ok(VmmRegValueType::REG_MULTI_SZ(result_vec));
}
let mut result_string = "".to_string();
if let Some(raw_string) = raw_chars.split(|v| *v == 0).next() {
result_string = String::from_utf16_lossy(raw_string);
}
match self.raw_type {
REG_SZ => return Ok(VmmRegValueType::REG_SZ(result_string)),
REG_EXPAND_SZ => return Ok(VmmRegValueType::REG_EXPAND_SZ(result_string)),
REG_LINK => return Ok(VmmRegValueType::REG_LINK(result_string)),
_ => return Err(anyhow!("[err]")),
};
}
}
impl fmt::Display for VmmProcess<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcess:{}", self.pid & 0x7fffffff)
}
}
impl PartialEq for VmmProcess<'_> {
fn eq(&self, other: &Self) -> bool {
self.pid == other.pid
}
}
impl fmt::Display for VmmProcessInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessInfo:{}:{}", self.pid & 0x7fffffff, self.name)
}
}
impl fmt::Display for VmmProcessMapEatEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapEatEntry:{:x}:{}", self.va_function, self.function)
}
}
impl fmt::Display for VmmProcessMapHandleEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapHandleEntry:{}:{:x}:{}:[{}]", self.pid & 0x7fffffff, self.handle_id, self.tp, self.info)
}
}
impl From<u32> for VmmProcessMapHeapType {
fn from(v : u32) -> Self {
return match v {
1 => VmmProcessMapHeapType::NtHeap,
2 => VmmProcessMapHeapType::SegmentHeap,
_ => VmmProcessMapHeapType::NA,
};
}
}
impl fmt::Display for VmmProcessMapHeapType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmProcessMapHeapType::NA => "NA",
VmmProcessMapHeapType::NtHeap => "NtHeap",
VmmProcessMapHeapType::SegmentHeap => "SegmentHeap",
};
write!(f, "{v}")
}
}
impl fmt::Display for VmmProcessMapHeapEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapHeapAllocEntry:{}:{}:{}", self.pid & 0x7fffffff, self.number, self.tp)
}
}
impl From<u32> for VmmProcessMapHeapAllocType {
fn from(v : u32) -> Self {
return match v {
1 => VmmProcessMapHeapAllocType::NtHeap,
2 => VmmProcessMapHeapAllocType::NtLFH,
3 => VmmProcessMapHeapAllocType::NtLarge,
4 => VmmProcessMapHeapAllocType::NtNA,
5 => VmmProcessMapHeapAllocType::SegVS,
6 => VmmProcessMapHeapAllocType::SegLFH,
7 => VmmProcessMapHeapAllocType::SegLarge,
8 => VmmProcessMapHeapAllocType::SegNA,
_ => VmmProcessMapHeapAllocType::NA,
};
}
}
impl fmt::Display for VmmProcessMapHeapAllocType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmProcessMapHeapAllocType::NA => "NA",
VmmProcessMapHeapAllocType::NtHeap => "NtHeap",
VmmProcessMapHeapAllocType::NtLFH => "NtLFH",
VmmProcessMapHeapAllocType::NtLarge => "NtLarge",
VmmProcessMapHeapAllocType::NtNA => "NtNA",
VmmProcessMapHeapAllocType::SegVS => "SegVS",
VmmProcessMapHeapAllocType::SegLFH => "SegLFH",
VmmProcessMapHeapAllocType::SegLarge => "SegLarge",
VmmProcessMapHeapAllocType::SegNA => "SegNA",
};
write!(f, "{v}")
}
}
impl fmt::Display for VmmProcessMapHeapAllocEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapHeapAllocEntry:{}:{}:{:x}", self.pid & 0x7fffffff, self.tp, self.va)
}
}
impl fmt::Display for VmmProcessMapIatEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapIatEntry:{:x}:{}", self.va_function, self.function)
}
}
impl fmt::Display for VmmProcessMapPteEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapPteEntry:{}:{:x}->{:x}", self.pid & 0x7fffffff, self.va_base, self.va_base + self.page_count * 0x1000 - 1)
}
}
impl fmt::Display for VmmProcessMapModuleEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapModuleEntry:{}:{:x}:[{}]", self.pid & 0x7fffffff, self.va_base, self.name)
}
}
impl fmt::Display for VmmProcessMapModuleDebugEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapModuleDebugEntry:[{}]", self.pdb_filename)
}
}
impl fmt::Display for VmmProcessMapModuleVersionEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapModuleVersionEntry")
}
}
impl fmt::Display for VmmProcessMapThreadEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapThreadEntry:{}:{:x}", self.pid & 0x7fffffff, self.thread_id)
}
}
impl fmt::Display for VmmProcessMapThreadCallstackEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapThreadEntry:{}:{}:{:02x}:{:016x}:{:016x}:[{}!{}+{:x}]", self.pid & 0x7fffffff, self.tid, self.i, self.va_rsp, self.va_ret_addr, self.module, self.function, self.displacement)
}
}
impl fmt::Display for VmmProcessMapUnloadedModuleEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapUnloadedModuleEntry:{}:{:x}:[{}]", self.pid & 0x7fffffff, self.va_base, self.name)
}
}
impl fmt::Display for VmmProcessMapVadEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapVadEntry:{}:{:x}->{}", self.pid & 0x7fffffff, self.va_start, self.va_end)
}
}
impl From<u32> for VmmProcessMapVadExType {
fn from(v : u32) -> Self {
return match v {
1 => VmmProcessMapVadExType::Hardware,
2 => VmmProcessMapVadExType::Transition,
3 => VmmProcessMapVadExType::Prototype,
4 => VmmProcessMapVadExType::DemandZero,
5 => VmmProcessMapVadExType::Compressed,
6 => VmmProcessMapVadExType::Pagefile,
7 => VmmProcessMapVadExType::File,
_ => VmmProcessMapVadExType::NA,
};
}
}
impl fmt::Display for VmmProcessMapVadExType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmProcessMapVadExType::NA => "NA",
VmmProcessMapVadExType::Hardware => "Hardware",
VmmProcessMapVadExType::Transition => "Transition",
VmmProcessMapVadExType::Prototype => "Prototype",
VmmProcessMapVadExType::DemandZero => "DemandZero",
VmmProcessMapVadExType::Compressed => "Compressed",
VmmProcessMapVadExType::Pagefile => "Pagefile",
VmmProcessMapVadExType::File => "File",
};
write!(f, "{v}")
}
}
impl fmt::Display for VmmProcessMapVadExEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapVadExEntry:{}:{:x}:{}", self.pid & 0x7fffffff, self.va, self.tp)
}
}
impl fmt::Display for VmmProcessMapDirectoryEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessMapDirectoryEntry:{}:{}:{:x}:{:x}", self.pid & 0x7fffffff, self.name, self.virtual_address, self.size)
}
}
impl fmt::Display for VmmProcessSectionEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmProcessSectionEntry:{}:[{}]:{:x}:{:x}", self.pid & 0x7fffffff, self.name, self.virtual_address, self.misc_virtual_size)
}
}
impl From<u32> for VmmProcessMapModuleType {
fn from(v : u32) -> Self {
return match v {
1 => VmmProcessMapModuleType::Data,
2 => VmmProcessMapModuleType::NotLinked,
3 => VmmProcessMapModuleType::Injected,
_ => VmmProcessMapModuleType::Normal,
};
}
}
impl fmt::Display for VmmProcessMapModuleType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = match self {
VmmProcessMapModuleType::Data => "Data",
VmmProcessMapModuleType::NotLinked => "NotLinked",
VmmProcessMapModuleType::Injected => "Injected",
VmmProcessMapModuleType::Normal => "Normal",
};
write!(f, "{v}")
}
}
#[repr(C)]
#[allow(non_snake_case)]
struct CProcessInformation {
magic : u64,
wVersion : u16,
wSize : u16,
tpMemoryModel : u32,
tpSystem : u32,
fUserOnly : bool,
dwPID : u32,
dwPPID : u32,
dwState : u32,
szName : [c_char; 16],
szNameLong : [c_char; 64],
paDTB : u64,
paDTB_UserOpt : u64,
vaEPROCESS : u64,
vaPEB : u64,
_Reserved1 : u64,
fWow64 : bool,
vaPEB32 : u32,
dwSessionId : u32,
qwLUID : u64,
szSID : [c_char; 260],
IntegrityLevel : u32,
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Clone, Default)]
struct CIMAGE_SECTION_HEADER {
Name : [u8; 8],
Misc_VirtualAddress : u32,
VirtualAddress : u32,
SizeOfRawData : u32,
PointerToRawData : u32,
PointerToRelocations : u32,
PointerToLinenumbers : u32,
NumberOfRelocations : u16,
NumberOfLinenumbers : u16,
Characteristics : u32,
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Clone, Default)]
struct CIMAGE_DATA_DIRECTORY {
VirtualAddress : u32,
Size : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CEatEntry {
vaFunction : u64,
dwOrdinal : u32,
oFunctionsArray : u32,
oNamesArray : u32,
_FutureUse1 : u32,
uszFunction : *const c_char,
uszForwardedFunction : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CEatMap {
dwVersion : u32,
dwOrdinalBase : u32,
cNumberOfNames : u32,
cNumberOfFunctions : u32,
cNumberOfForwardedFunctions : u32,
_Reserved1 : [u32; 3],
vaModuleBase : u64,
vaAddressOfFunctions : u64,
vaAddressOfNames : u64,
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CEatEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHandleEntry {
vaObject : u64,
dwHandle : u32,
dwGrantedAccess_Tp : u32,
qwHandleCount : u64,
qwPointerCount : u64,
vaObjectCreateInfo : u64,
vaSecurityDescriptor : u64,
uszText : *const c_char,
_FutureUse2 : u32,
dwPID : u32,
dwPoolTag : u32,
_FutureUse : [u32; 7],
uszType : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHandleMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CHandleEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHeapEntry {
va : u64,
tp : u32,
f32 : bool,
iHeap : u32,
dwHeapNum : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHeapMap {
dwVersion : u32,
_Reserved1 : [u32; 7],
pSegments : usize,
cSegments : u32,
cMap : u32,
pMap : CHeapEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHeapAllocEntry {
va : u64,
cb : u32,
tp : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CHeapAllocMap {
dwVersion : u32,
_Reserved1 : [u32; 7],
_Reserved2 : [usize; 2],
cMap : u32,
pMap : CHeapAllocEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CIatEntry {
vaFunction : u64,
uszFunction : *const c_char,
_FutureUse1 : u32,
_FutureUse2 : u32,
uszModule : *const c_char,
thunk_f32 : bool,
thunk_wHint : u16,
thunk__Reserved1 : u16,
thunk_rvaFirstThunk : u32,
thunk_rvaOriginalFirstThunk : u32,
thunk_rvaNameModule : u32,
thunk_rvaNameFunction : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CIatMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
vaModuleBase : u64,
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CIatEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CDebugInfo {
dwAge : u32,
_Reserved : u32,
Guid : [u8; 16],
uszGuid : *const c_char,
uszPdbFilename : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVersionInfo {
uszCompanyName : *const c_char,
uszFileDescription : *const c_char,
uszFileVersion : *const c_char,
uszInternalName : *const c_char,
uszLegalCopyright : *const c_char,
uszOriginalFilename : *const c_char,
uszProductName : *const c_char,
uszProductVersion : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CModuleEntry {
vaBase : u64,
vaEntry : u64,
cbImageSize : u32,
fWoW64 : bool,
uszText : *const c_char,
_Reserved3 : u32,
_Reserved4 : u32,
uszFullName : *const c_char,
tp : u32,
cbFileSizeRaw : u32,
cSection : u32,
cEAT : u32,
cIAT : u32,
_Reserved2 : u32,
_Reserved1 : [u64; 3],
pExDebugInfo : *const CDebugInfo,
pExVersionInfo : *const CVersionInfo,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CModuleMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CModuleEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPteEntry {
vaBase : u64,
cPages : u64,
fPage : u64,
fWoW64 : bool,
_FutureUse1 : u32,
uszText : *const c_char,
_Reserved1 : u32,
cSoftware : u32,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CPteMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CPteEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CThreadEntry {
dwTID : u32,
dwPID : u32,
dwExitStatus : u32,
bState : u8,
bRunning : u8,
bPriority : u8,
bBasePriority : u8,
vaETHREAD : u64,
vaTeb : u64,
ftCreateTime : u64,
ftExitTime : u64,
vaStartAddress : u64,
vaStackBaseUser : u64,
vaStackLimitUser : u64,
vaStackBaseKernel : u64,
vaStackLimitKernel : u64,
vaTrapFrame : u64,
vaRIP : u64,
vaRSP : u64,
qwAffinity : u64,
dwUserTime : u32,
dwKernelTime : u32,
bSuspendCount : u8,
bWaitReason : u8,
_FutureUse1 : [u8; 2],
_FutureUse2 : [u32; 11],
vaImpersonationToken : u64,
vaWin32StartAddress : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CThreadMap {
dwVersion : u32,
_Reserved1 : [u32; 8],
cMap : u32,
pMap : CThreadEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CThreadCallstackEntry {
i : u32,
fRegPresent : bool,
vaRetAddr : u64,
vaRSP : u64,
vaBaseSP : u64,
_FutureUse1 : u32,
cbDisplacement : i32,
uszModule : *const c_char,
uszFunction : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CThreadCallstackMap {
dwVersion : u32,
_Reserved1 : [u32; 6],
dwPID : u32,
dwTID : u32,
cbText : u32,
uszText : *const c_char,
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CThreadCallstackEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CUnloadedModuleEntry {
vaBase : u64,
cbImageSize : u32,
fWoW64 : bool,
uszText : *const c_char,
_FutureUse1 : u32,
dwCheckSum : u32,
dwTimeDateStamp : u32,
_Reserved1 : u32,
ftUnload : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CUnloadedModuleMap {
dwVersion : u32,
_Reserved1 : [u32; 5],
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CUnloadedModuleEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVadEntry {
vaStart : u64,
vaEnd : u64,
vaVad : u64,
u0 : u32,
u1 : u32,
u2 : u32,
cbPrototypePte : u32,
vaPrototypePte : u64,
vaSubsection : u64,
uszText : *const c_char,
_FutureUse1 : u32,
_Reserved1 : u32,
vaFileObject : u64,
cVadExPages : u32,
cVadExPagesBase : u32,
_Reserved2 : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVadMap {
dwVersion : u32,
_Reserved1 : [u32; 4],
cPage : u32,
pbMultiText : *const c_char,
cbMultiText : u32,
cMap : u32,
pMap : CVadEntry,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVadExEntry {
tp : u32,
iPML : u8,
pteFlags : u8,
_Reserved2 : u16,
va : u64,
pa : u64,
pte : u64,
proto__Reserved1 : u32,
proto_tp : u32,
proto_pa : u64,
proto_va : u64,
vaVadBase : u64,
}
#[repr(C)]
#[allow(non_snake_case)]
struct CVadExMap {
dwVersion : u32,
_Reserved1 : [u32; 4],
cMap : u32,
pMap : CVadExEntry,
}
#[allow(non_snake_case)]
impl VmmProcess<'_> {
fn impl_info(&self) -> ResultEx<VmmProcessInfo> {
let mut cb_pi = std::mem::size_of::<CProcessInformation>();
let mut pi = CProcessInformation {
magic : VMMDLL_PROCESS_INFORMATION_MAGIC,
wVersion : VMMDLL_PROCESS_INFORMATION_VERSION,
wSize : u16::try_from(cb_pi)?,
tpMemoryModel : 0,
tpSystem : 0,
fUserOnly : false,
dwPID : 0,
dwPPID : 0,
dwState : 0,
szName : [0; 16],
szNameLong : [0; 64],
paDTB : 0,
paDTB_UserOpt : 0,
vaEPROCESS : 0,
vaPEB : 0,
_Reserved1 : 0,
fWow64 : false,
vaPEB32 : 0,
dwSessionId : 0,
qwLUID : 0,
szSID : [0; 260],
IntegrityLevel : 0,
};
let raw_pi = &mut pi as *mut CProcessInformation;
let r = (self.vmm.native.VMMDLL_ProcessGetInformation)(self.vmm.native.h, self.pid, raw_pi, &mut cb_pi);
if !r {
return Err(anyhow!("VMMDLL_ProcessGetInformation: fail."));
}
let result = VmmProcessInfo {
tp_system : VmmSystemType::from(pi.tpSystem),
tp_memorymodel : VmmMemoryModelType::from(pi.tpMemoryModel),
is_user_mode : pi.fUserOnly,
pid : pi.dwPID,
ppid : pi.dwPPID,
state : pi.dwState,
name : unsafe { cstr_to_string_lossy(&pi.szName as *const c_char) },
name_long : unsafe { cstr_to_string_lossy(&pi.szNameLong as *const c_char) },
pa_dtb : pi.paDTB,
pa_dtb_user : pi.paDTB_UserOpt,
va_eprocess : pi.vaEPROCESS,
va_peb : pi.vaPEB,
is_wow64 : pi.fWow64,
va_peb32 : pi.vaPEB32,
session_id : pi.dwSessionId,
luid : pi.qwLUID,
sid : unsafe { cstr_to_string_lossy(&pi.szSID as *const c_char) },
integrity_level : VmmIntegrityLevelType::from(pi.IntegrityLevel),
};
return Ok(result);
}
fn impl_get_information_string(&self, option : u32) -> ResultEx<String> {
let r = (self.vmm.native.VMMDLL_ProcessGetInformationString)(self.vmm.native.h, self.pid, option);
if r.is_null() {
return Err(anyhow!("VMMDLL_ProcessGetInformationString: fail."));
}
let result = unsafe { cstr_to_string_lossy(r) };
(self.vmm.native.VMMDLL_MemFree)(r as usize);
return Ok(result);
}
fn impl_get_module_base(&self, module_name : &str) -> ResultEx<u64> {
let sz_module_name = CString::new(module_name)?;
let r = (self.vmm.native.VMMDLL_ProcessGetModuleBaseU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr());
if r == 0 {
return Err(anyhow!("VMMDLL_ProcessGetModuleBaseU: fail."));
}
return Ok(r);
}
fn impl_get_proc_address(&self, module_name : &str, function_name : &str) -> ResultEx<u64> {
let sz_module_name = CString::new(module_name)?;
let sz_function_name = CString::new(function_name)?;
let r = (self.vmm.native.VMMDLL_ProcessGetProcAddressU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), sz_function_name.as_ptr());
if r == 0 {
return Err(anyhow!("VMMDLL_ProcessGetProcAddressU: fail."));
}
return Ok(r);
}
fn impl_pdb_from_module_name(&self, module_name : &str) -> ResultEx<VmmPdb> {
let va_module_base = self.get_module_base(module_name)?;
return self.impl_pdb_from_module_address(va_module_base);
}
fn impl_pdb_from_module_address(&self, va_module_base : u64) -> ResultEx<VmmPdb> {
let mut szModuleName = [0i8; MAX_PATH + 1];
let r = (self.vmm.native.VMMDLL_PdbLoad)(self.vmm.native.h, self.pid, va_module_base, szModuleName.as_mut_ptr() as *mut c_char);
if !r {
return Err(anyhow!("VMMDLL_PdbLoad: fail."));
}
let module = unsafe { cstr_to_string_lossy(szModuleName.as_ptr() as *const c_char) };
let pdb = VmmPdb {
vmm : self.vmm,
module,
};
return Ok(pdb);
}
fn impl_map_handle(&self) -> ResultEx<Vec<VmmProcessMapHandleEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetHandleU)(self.vmm.native.h, self.pid, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetHandleU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_HANDLE_VERSION, |ne| {
VmmProcessMapHandleEntry {
pid : self.pid,
va_object : ne.vaObject,
handle_id : ne.dwHandle,
granted_access : ne.dwGrantedAccess_Tp & 0x00ffffff,
type_index : (ne.dwGrantedAccess_Tp >> 24) & 0xff,
handle_count : ne.qwHandleCount,
pointer_count : ne.qwPointerCount,
va_object_create_info : ne.vaObjectCreateInfo,
va_security_descriptor : ne.vaSecurityDescriptor,
handle_pid : ne.dwPID,
pool_tag : ne.dwPoolTag,
info : cstr_to_string(ne.uszText),
tp : cstr_to_string(ne.uszType),
}
})
}
fn impl_map_heap(&self) -> ResultEx<Vec<VmmProcessMapHeapEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetHeap)(self.vmm.native.h, self.pid, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetHeap: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_HEAP_VERSION, |ne| {
VmmProcessMapHeapEntry {
pid : self.pid,
tp : VmmProcessMapHeapType::from(ne.tp),
is_32 : ne.f32,
index : ne.iHeap,
number : ne.dwHeapNum,
}
})
}
fn impl_map_heapalloc(&self, heap_number_or_address : u64) -> ResultEx<Vec<VmmProcessMapHeapAllocEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetHeapAlloc)(self.vmm.native.h, self.pid, heap_number_or_address, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetHeapAlloc: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_HEAPALLOC_VERSION, |ne| {
VmmProcessMapHeapAllocEntry {
pid : self.pid,
va : ne.va,
size : ne.cb,
tp : VmmProcessMapHeapAllocType::from(ne.tp),
}
})
}
fn impl_map_module(&self, is_info_debug : bool, is_info_version : bool) -> ResultEx<Vec<VmmProcessMapModuleEntry>> {
let mut structs = std::ptr::null_mut();
let flags = 0 + if is_info_debug { 1 } else { 0 } + if is_info_version { 2 } else { 0 };
let r = (self.vmm.native.VMMDLL_Map_GetModuleU)(self.vmm.native.h, self.pid, &mut structs, flags);
if !r {
return Err(anyhow!("VMMDLL_Map_GetModuleU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_MODULE_VERSION, |ne| {
let mut debug_info = None;
if !ne.pExDebugInfo.is_null() {
let nei = &*ne.pExDebugInfo;
debug_info = Some(VmmProcessMapModuleDebugEntry {
pid : self.pid,
age : nei.dwAge,
raw_guid : nei.Guid,
guid : cstr_to_string(nei.uszGuid),
pdb_filename : cstr_to_string(nei.uszPdbFilename),
});
}
let mut version_info = None;
if !ne.pExVersionInfo.is_null() {
let nei = &*ne.pExVersionInfo;
version_info = Some(VmmProcessMapModuleVersionEntry {
pid : self.pid,
company_name : cstr_to_string(nei.uszCompanyName),
file_description : cstr_to_string(nei.uszFileDescription),
file_version : cstr_to_string(nei.uszFileVersion),
internal_name : cstr_to_string(nei.uszInternalName),
legal_copyright : cstr_to_string(nei.uszLegalCopyright),
original_file_name : cstr_to_string(nei.uszOriginalFilename),
product_name : cstr_to_string(nei.uszProductName),
product_version : cstr_to_string(nei.uszProductVersion),
});
}
VmmProcessMapModuleEntry {
pid : self.pid,
va_base : ne.vaBase,
va_entry : ne.vaEntry,
image_size : ne.cbImageSize,
is_wow64 : ne.fWoW64,
tp : VmmProcessMapModuleType::from(ne.tp),
name : cstr_to_string(ne.uszText),
full_name : cstr_to_string(ne.uszFullName),
file_size_raw : ne.cbFileSizeRaw,
section_count : ne.cSection,
eat_count : ne.cEAT,
iat_count : ne.cIAT,
debug_info : debug_info,
version_info : version_info,
}
})
}
fn impl_map_module_eat(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapEatEntry>> {
let mut structs = std::ptr::null_mut();
let sz_module_name = CString::new(module_name)?;
let r = (self.vmm.native.VMMDLL_Map_GetEATU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetEATU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_EAT_VERSION, |ne| {
VmmProcessMapEatEntry {
pid : self.pid,
va_function : ne.vaFunction,
ordinal : ne.dwOrdinal,
function : cstr_to_string(ne.uszFunction),
forwarded_function : cstr_to_string(ne.uszForwardedFunction),
}
})
}
fn impl_map_module_iat(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapIatEntry>> {
let mut structs = std::ptr::null_mut();
let sz_module_name = CString::new(module_name)?;
let r = (self.vmm.native.VMMDLL_Map_GetIATU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetIATU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_IAT_VERSION, |ne| {
VmmProcessMapIatEntry {
pid : self.pid,
va_function : ne.vaFunction,
function : cstr_to_string(ne.uszFunction),
module : cstr_to_string(ne.uszModule),
}
})
}
fn impl_map_pte(&self, is_identify_modules : bool) -> ResultEx<Vec<VmmProcessMapPteEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetPteU)(self.vmm.native.h, self.pid, is_identify_modules, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetPteU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_PTE_VERSION, |ne| {
VmmProcessMapPteEntry {
pid : self.pid,
va_base : ne.vaBase,
page_count : ne.cPages,
page_software_count : ne.cSoftware,
is_r : true,
is_w : (ne.fPage & 0x0000000000000002) != 0,
is_x : (ne.fPage & 0x8000000000000000) == 0,
is_s : (ne.fPage & 0x0000000000000004) == 0,
is_wow64 : ne.fWoW64,
info : cstr_to_string(ne.uszText),
}
})
}
fn impl_map_thread(&self) -> ResultEx<Vec<VmmProcessMapThreadEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetThread)(self.vmm.native.h, self.pid, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetThread: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_THREAD_VERSION, |ne| {
VmmProcessMapThreadEntry {
pid : self.pid,
thread_id : ne.dwTID,
thread_pid : ne.dwPID,
exit_status : ne.dwExitStatus,
state : ne.bState,
running : ne.bRunning,
priority : ne.bPriority,
priority_base : ne.bBasePriority,
va_ethread : ne.vaETHREAD,
va_teb : ne.vaTeb,
ft_create_time : ne.ftCreateTime,
ft_exit_time : ne.ftExitTime,
va_start_address : ne.vaStartAddress,
va_win32_start_address : ne.vaWin32StartAddress,
va_stack_user_base : ne.vaStackBaseUser,
va_stack_user_limit : ne.vaStackLimitUser,
va_stack_kernel_base : ne.vaStackBaseKernel,
va_stack_kernel_limit : ne.vaStackLimitKernel,
va_trap_frame : ne.vaTrapFrame,
va_impersonation_token : ne.vaImpersonationToken,
va_rip : ne.vaRIP,
va_rsp : ne.vaRSP,
affinity : ne.qwAffinity,
user_time : ne.dwUserTime,
kernel_time : ne.dwKernelTime,
suspend_count : ne.bSuspendCount,
wait_reason : ne.bWaitReason
}
})
}
fn impl_map_thread_callstack(&self, tid : u32, flags : u32) -> ResultEx<Vec<VmmProcessMapThreadCallstackEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetThreadCallstackU)(self.vmm.native.h, self.pid, tid, flags, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetThreadCallstackU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_THREAD_CALLSTACK_VERSION, |ne| {
VmmProcessMapThreadCallstackEntry {
pid : self.pid,
tid : tid,
i : ne.i,
is_reg_present : ne.fRegPresent,
va_ret_addr : ne.vaRetAddr,
va_rsp : ne.vaRSP,
va_base_sp : ne.vaBaseSP,
displacement : ne.cbDisplacement,
module : cstr_to_string(ne.uszModule),
function : cstr_to_string(ne.uszFunction),
}
})
}
fn impl_map_unloaded_module(&self) -> ResultEx<Vec<VmmProcessMapUnloadedModuleEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetUnloadedModuleU)(self.vmm.native.h, self.pid, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetUnloadedModuleU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_UNLOADEDMODULE_VERSION, |ne| {
VmmProcessMapUnloadedModuleEntry {
pid : self.pid,
va_base : ne.vaBase,
image_size : ne.cbImageSize,
is_wow64 : ne.fWoW64,
name : cstr_to_string(ne.uszText),
checksum : ne.dwCheckSum,
timedatestamp : ne.dwTimeDateStamp,
ft_unload : ne.ftUnload,
}
})
}
fn impl_map_vad(&self, is_identify_modules : bool) -> ResultEx<Vec<VmmProcessMapVadEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetVadU)(self.vmm.native.h, self.pid, is_identify_modules, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetVadU: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_VAD_VERSION, |ne| {
VmmProcessMapVadEntry {
pid : self.pid,
va_start : ne.vaStart,
va_end : ne.vaEnd,
va_vad : ne.vaVad,
u0 : ne.u0,
u1 : ne.u1,
u2 : ne.u2,
commit_charge : ne.u1 & 0x7fffffff,
is_mem_commit : (ne.u1 & 0x80000000) != 0,
cb_prototype_pte : ne.cbPrototypePte,
va_prototype_pte : ne.vaPrototypePte,
va_subsection : ne.vaSubsection,
va_file_object : ne.vaFileObject,
info : cstr_to_string(ne.uszText),
vadex_page_base : ne.cVadExPagesBase,
vadex_page_count : ne.cVadExPages,
}
})
}
fn impl_map_vadex(&self, offset_pages : u32, count_pages : u32) -> ResultEx<Vec<VmmProcessMapVadExEntry>> {
let mut structs = std::ptr::null_mut();
let r = (self.vmm.native.VMMDLL_Map_GetVadEx)(self.vmm.native.h, self.pid, offset_pages, count_pages, &mut structs);
if !r {
return Err(anyhow!("VMMDLL_Map_GetVadEx: fail."));
}
impl_map_get!(self.vmm.native, structs, VMMDLL_MAP_VADEX_VERSION, |ne| {
VmmProcessMapVadExEntry {
pid : self.pid,
tp : VmmProcessMapVadExType::from(ne.tp),
i_pml : ne.iPML,
va : ne.va,
pa : ne.pa,
pte : ne.pte,
pte_flags : ne.pteFlags,
proto_tp : VmmProcessMapVadExType::from(ne.proto_tp),
proto_pa : ne.proto_pa,
proto_pte : ne.proto_va,
va_vad_base : ne.vaVadBase,
}
})
}
fn impl_map_module_data_directory(&self, module_name : &str) -> ResultEx<Vec<VmmProcessMapDirectoryEntry>> {
let sz_module_name = CString::new(module_name)?;
let mut data_directories = vec![CIMAGE_DATA_DIRECTORY::default(); 16];
let r = (self.vmm.native.VMMDLL_ProcessGetDirectoriesU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), data_directories.as_mut_ptr());
if !r {
return Err(anyhow!("VMMDLL_ProcessGetDirectoriesU: fail."));
}
let mut result = Vec::new();
for i in 0..16 {
let src : &CIMAGE_DATA_DIRECTORY = data_directories.get(i).unwrap();
let dst = VmmProcessMapDirectoryEntry {
pid : self.pid,
name : DIRECTORY_NAMES[i],
virtual_address : src.VirtualAddress,
size : src.Size,
};
result.push(dst);
}
return Ok(result);
}
fn impl_map_module_section(&self, module_name : &str) -> ResultEx<Vec<VmmProcessSectionEntry>> {
let sz_module_name = CString::new(module_name)?;
let mut section_count = 0u32;
let r = (self.vmm.native.VMMDLL_ProcessGetSectionsU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), std::ptr::null_mut(), 0, &mut section_count);
if !r {
return Err(anyhow!("VMMDLL_ProcessGetSectionsU: fail."));
}
let mut sections = vec![CIMAGE_SECTION_HEADER::default(); section_count.try_into()?];
let mut result = Vec::new();
if section_count == 0 {
return Ok(result);
}
let r = (self.vmm.native.VMMDLL_ProcessGetSectionsU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), sections.as_mut_ptr(), section_count, &mut section_count);
if !r {
return Err(anyhow!("VMMDLL_ProcessGetSectionsU: fail."));
}
for i in 0..(section_count as usize) {
let src : &CIMAGE_SECTION_HEADER = sections.get(i).unwrap();
let dst = VmmProcessSectionEntry {
pid : self.pid,
index : i as u32,
name : std::str::from_utf8(&src.Name).unwrap_or_default().to_string(),
name_raw : src.Name,
misc_virtual_size : src.Misc_VirtualAddress,
virtual_address : src.VirtualAddress,
size_of_raw_data : src.SizeOfRawData,
pointer_to_raw_data : src.PointerToRawData,
pointer_to_relocations : src.PointerToRelocations,
pointer_to_linenumbers : src.PointerToLinenumbers,
number_of_relocations : src.NumberOfRelocations,
number_of_linenumbers : src.NumberOfLinenumbers,
characteristics : src.Characteristics,
};
result.push(dst);
}
return Ok(result);
}
}
impl fmt::Display for VmmScatterMemory<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.pid == u32::MAX { write!(f, "VmmScatterMemory:physical") } else { write!(f, "VmmScatterMemory:virtual:{}", self.pid & 0x7fffffff) }
}
}
impl Drop for VmmScatterMemory<'_> {
fn drop(&mut self) {
if self.is_scatter_ex {
let _r = self.impl_execute();
}
(self.vmm.native.VMMDLL_Scatter_CloseHandle)(self.hs);
}
}
impl <'a> VmmScatterMemory<'a> {
fn impl_prepare_ex(&mut self, data_to_read : &'a mut (u64, Vec<u8>, u32)) -> ResultEx<()> {
if data_to_read.2 != 0 {
return Err(anyhow!("data_to_read.2 not set to zero"));
}
let cb = u32::try_from(data_to_read.1.len())?;
let r = (self.vmm.native.VMMDLL_Scatter_PrepareEx)(self.hs, data_to_read.0, cb, data_to_read.1.as_mut_ptr(), &mut data_to_read.2);
if !r {
return Err(anyhow!("VMMDLL_Scatter_PrepareEx: fail."));
}
self.is_scatter_ex = true;
return Ok(());
}
fn impl_prepare_ex_as<T>(&mut self, data_to_read : &'a mut (u64, T, u32)) -> ResultEx<()> {
if data_to_read.2 != 0 {
return Err(anyhow!("data_to_read.2 not set to zero"));
}
let cb = u32::try_from(std::mem::size_of::<T>())?;
let r = (self.vmm.native.VMMDLL_Scatter_PrepareEx)(self.hs, data_to_read.0, cb, &mut data_to_read.1 as *mut _ as *mut u8, &mut data_to_read.2);
if !r {
return Err(anyhow!("VMMDLL_Scatter_PrepareEx: fail."));
}
self.is_scatter_ex = true;
return Ok(());
}
}
impl VmmScatterMemory<'_> {
fn impl_prepare(&self, va : u64, size : usize) -> ResultEx<()> {
let cb = u32::try_from(size)?;
let r = (self.vmm.native.VMMDLL_Scatter_Prepare)(self.hs, va, cb);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Prepare: fail."));
}
return Ok(());
}
fn impl_prepare_write(&self, va : u64, data : &[u8]) -> ResultEx<()> {
let cb = u32::try_from(data.len())?;
let pb = data.as_ptr();
let r = (self.vmm.native.VMMDLL_Scatter_PrepareWrite)(self.hs, va, pb, cb);
if !r {
return Err(anyhow!("VMMDLL_Scatter_PrepareWrite: fail."));
}
return Ok(());
}
fn impl_prepare_write_as<T>(&self, va : u64, data : &T) -> ResultEx<()> {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let r = (self.vmm.native.VMMDLL_Scatter_PrepareWrite)(self.hs, va, data as *const _ as *const u8, cb);
if !r {
return Err(anyhow!("VMMDLL_Scatter_PrepareWrite: fail."));
}
return Ok(());
}
fn impl_execute(&self) -> ResultEx<()> {
let r = (self.vmm.native.VMMDLL_Scatter_Execute)(self.hs);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Execute: fail."));
}
return Ok(());
}
fn impl_read(&self, va : u64, size : usize) -> ResultEx<Vec<u8>> {
let cb = u32::try_from(size)?;
let mut cb_read = 0;
let mut pb_result = vec![0u8; size];
let r = (self.vmm.native.VMMDLL_Scatter_Read)(self.hs, va, cb, pb_result.as_mut_ptr(), &mut cb_read);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Read: fail."));
}
return Ok(pb_result);
}
fn impl_read_as<T>(&self, va : u64) -> ResultEx<T> {
unsafe {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let mut cb_read = 0;
let mut result : T = std::mem::zeroed();
let r = (self.vmm.native.VMMDLL_Scatter_Read)(self.hs, va, cb, &mut result as *mut _ as *mut u8, &mut cb_read);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Read: fail."));
}
return Ok(result);
}
}
fn impl_read_into(&self, va : u64, data : &mut [u8]) -> ResultEx<usize> {
let cb = u32::try_from(data.len())?;
let mut cb_read = 0;
let r = (self.vmm.native.VMMDLL_Scatter_Read)(self.hs, va, cb, data.as_mut_ptr(), &mut cb_read);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Read: fail."));
}
return Ok(cb_read as usize);
}
fn impl_clear(&self) -> ResultEx<()> {
let r = (self.vmm.native.VMMDLL_Scatter_Clear)(self.hs, self.pid, self.flags);
if !r {
return Err(anyhow!("VMMDLL_Scatter_Clear: fail."));
}
return Ok(());
}
}
impl fmt::Display for VmmSearch<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmSearch")
}
}
impl fmt::Display for VmmSearchResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmSearchResult")
}
}
const CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY_MAX : usize = 0x00100000;
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug, Default)]
struct CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY {
cbAlign : u32,
cb : u32,
pb : [u8; 32],
pbSkipMask : [u8; 32],
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug, Default)]
pub(crate) struct CVMMDLL_MEM_SEARCH_CONTEXT {
dwVersion : u32,
_Filler : [u32; 2],
fAbortRequested : u32,
cMaxResult : u32,
cSearch : u32,
pSearch : usize,
vaMin : u64,
vaMax : u64,
vaCurrent : u64,
_Filler2 : u32,
cResult : u32,
cbReadTotal : u64,
pvUserPtrOpt : usize,
pfnResultOptCB : usize,
ReadFlags : u64,
fForcePTE : u32,
fForceVAD : u32,
pfnFilterOptCB : usize,
}
impl Drop for VmmSearch<'_> {
fn drop(&mut self) {
if self.is_started && !self.is_completed {
self.impl_abort();
let _r = self.impl_result();
}
}
}
impl VmmSearch<'_> {
fn impl_result(&mut self) -> VmmSearchResult {
if self.is_started == false {
self.impl_start();
}
if self.is_completed == false {
self.is_completed = true;
if let Some(thread) = self.thread.take() {
if let Ok(thread_result) = thread.join() {
self.is_completed_success = thread_result;
}
}
}
return self.impl_poll();
}
fn impl_abort(&mut self) {
if self.is_started && !self.is_completed {
self.native_search.fAbortRequested = 1;
}
}
fn impl_start(&mut self) {
if self.is_started == false {
self.is_started = true;
self.native_search.cSearch = self.search_terms.len() as u32;
self.native_search.pSearch = self.search_terms.as_ptr() as usize;
self.native_search.pvUserPtrOpt = std::ptr::addr_of!(self.result) as usize;
let pid = self.pid;
let native_h = self.vmm.native.h;
let pfn = self.vmm.native.VMMDLL_MemSearch;
let ptr = &mut self.native_search as *mut CVMMDLL_MEM_SEARCH_CONTEXT;
let ptr_wrap = ptr as usize;
let thread_handle = std::thread::spawn(move || {
let ptr = ptr_wrap as *mut CVMMDLL_MEM_SEARCH_CONTEXT;
(pfn)(native_h, pid, ptr, std::ptr::null_mut(), std::ptr::null_mut())
});
self.thread = Some(thread_handle);
}
}
fn impl_poll(&mut self) -> VmmSearchResult {
if self.is_started && !self.is_completed && self.thread.as_ref().unwrap().is_finished() {
return self.impl_result();
}
let result_vec = if self.is_completed_success { self.result.clone() } else { Vec::new() };
return VmmSearchResult {
is_started : self.is_started,
is_completed : self.is_completed,
is_completed_success : self.is_completed_success,
addr_min : self.native_search.vaMin,
addr_max : self.native_search.vaMax,
addr_current : self.native_search.vaCurrent,
total_read_bytes : self.native_search.cbReadTotal,
total_results : self.native_search.cResult,
result : result_vec,
}
}
fn impl_new<'a>(vmm : &'a Vmm<'a>, pid : u32, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmSearch<'a>> {
let num_results_max = std::cmp::min(0x10000, num_results_max);
let addr_min = addr_min & 0xfffffffffffff000;
let addr_max = addr_max & 0xfffffffffffff000;
if addr_max != 0 && addr_max <= addr_min {
return Err(anyhow!("search max address must be larger than min address"));
}
let result_vec = Vec::new();
let mut native_search = CVMMDLL_MEM_SEARCH_CONTEXT::default();
native_search.dwVersion = VMMDLL_MEM_SEARCH_VERSION;
native_search.vaMin = addr_min;
native_search.vaMax = addr_max;
native_search.ReadFlags = flags;
native_search.cMaxResult = num_results_max;
native_search.pfnResultOptCB = VmmSearch::impl_search_cb as usize;
native_search.pvUserPtrOpt = std::ptr::addr_of!(result_vec) as usize;
return Ok(VmmSearch {
vmm,
pid,
is_started : false,
is_completed : false,
is_completed_success : false,
native_search,
search_terms : Vec::new(),
thread : None,
result : result_vec,
});
}
fn impl_add_search(&mut self, search_bytes : &[u8], search_skipmask : Option<&[u8]>, byte_align : u32) -> ResultEx<u32> {
if self.is_started || self.is_completed {
return Err(anyhow!("Search cannot add terms to an already started/completed search."));
}
if self.search_terms.len() >= CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY_MAX {
return Err(anyhow!("Search max terms ({}) reached.", CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY_MAX));
}
if (search_bytes.len() == 0) || (search_bytes.len() > 32) {
return Err(anyhow!("Search invalid length: search_bytes."));
}
if byte_align > 0 {
if ((byte_align & (byte_align - 1)) != 0) || (byte_align > 0x1000) {
return Err(anyhow!("Search bad byte_align."));
}
}
if let Some(search_skipmask) = search_skipmask {
if search_skipmask.len() > search_bytes.len() {
return Err(anyhow!("Search invalid length: search_skipmask."));
}
}
let mut term = CVMMDLL_MEM_SEARCH_CONTEXT_SEARCHENTRY::default();
term.cbAlign = byte_align;
term.cb = search_bytes.len() as u32;
term.pb[0..search_bytes.len()].copy_from_slice(search_bytes);
if let Some(search_skipmask) = search_skipmask {
term.pbSkipMask[0..search_skipmask.len()].copy_from_slice(search_skipmask);
}
let result_index = self.search_terms.len() as u32;
self.search_terms.push(term);
return Ok(result_index);
}
extern "C" fn impl_search_cb(ctx : usize, va : u64, i_search : u32) -> bool {
unsafe {
let ctx = ctx as *const CVMMDLL_MEM_SEARCH_CONTEXT;
let ptr_result_vec = (*ctx).pvUserPtrOpt as *mut Vec<(u64, u32)>;
(*ptr_result_vec).push((va, i_search));
return true;
}
}
}
impl fmt::Display for VmmYara<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmYara")
}
}
impl fmt::Display for VmmYaraResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmYaraResult")
}
}
impl fmt::Display for VmmYaraMatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmYaraMatch:[{}]:{}", self.rule, self.match_strings.len())
}
}
impl fmt::Display for VmmYaraMatchString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmYaraMatchString:[{}]:{}", self.match_string, self.addresses.len())
}
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug)]
struct CVMMDLL_VMMYARA_RULE_MATCH_META {
szIdentifier : *const c_char,
szString : *const c_char,
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug)]
struct CVMMDLL_VMMYARA_RULE_MATCH_STRINGS {
szString : *const c_char,
cMatch : u32,
cbMatchOffset : [usize; VMMYARA_RULE_MATCH_OFFSET_MAX],
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug)]
struct CVMMDLL_VMMYARA_RULE_MATCH {
dwVersion : u32,
flags : u32,
szRuleIdentifier : *const c_char,
cTags : u32,
szTags : [*const c_char; VMMYARA_RULE_MATCH_TAG_MAX],
cMeta : u32,
meta : [CVMMDLL_VMMYARA_RULE_MATCH_META; VMMYARA_RULE_MATCH_META_MAX],
cStrings : u32,
strings : [CVMMDLL_VMMYARA_RULE_MATCH_STRINGS; VMMYARA_RULE_MATCH_STRING_MAX],
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
#[derive(Debug)]
pub(crate) struct CVMMDLL_YARA_CONFIG {
dwVersion : u32,
_Filler : [u32; 2],
fAbortRequested : u32,
cMaxResult : u32,
cRules : u32,
pszRules : *const *const c_char,
vaMin : u64,
vaMax : u64,
vaCurrent : u64,
_Filler2 : u32,
cResult : u32,
cbReadTotal : u64,
pvUserPtrOpt : usize,
pfnScanMemoryCB : usize,
ReadFlags : u64,
fForcePTE : u32,
fForceVAD : u32,
pfnFilterOptCB : usize,
pvUserPtrOpt2 : usize,
_Reserved : u64,
}
impl Drop for VmmYara<'_> {
fn drop(&mut self) {
if self.is_started && !self.is_completed {
self.impl_abort();
let _r = self.impl_result();
}
}
}
impl VmmYara<'_> {
fn impl_result(&mut self) -> VmmYaraResult {
if self.is_started == false {
self.impl_start();
}
if self.is_completed == false {
self.is_completed = true;
if let Some(thread) = self.thread.take() {
if let Ok(thread_result) = thread.join() {
self.is_completed_success = thread_result;
}
}
}
return self.impl_poll();
}
fn impl_abort(&mut self) {
if self.is_started && !self.is_completed {
self.native.fAbortRequested = 1;
}
}
fn impl_start(&mut self) {
if self.is_started == false {
self.is_started = true;
self.native.pvUserPtrOpt2 = std::ptr::addr_of!(self.result) as usize;
self.native.pvUserPtrOpt = std::ptr::addr_of!(self.native) as usize;
let pid = self.pid;
let native_h = self.vmm.native.h;
let pfn = self.vmm.native.VMMDLL_YaraSearch;
let ptr = &mut self.native as *mut CVMMDLL_YARA_CONFIG;
let ptr_wrap = ptr as usize;
let thread_handle = std::thread::spawn(move || {
let ptr = ptr_wrap as *mut CVMMDLL_YARA_CONFIG;
(pfn)(native_h, pid, ptr, std::ptr::null_mut(), std::ptr::null_mut())
});
self.thread = Some(thread_handle);
}
}
fn impl_poll(&mut self) -> VmmYaraResult {
if self.is_started && !self.is_completed && self.thread.as_ref().unwrap().is_finished() {
return self.impl_result();
}
let result_vec = if self.is_completed_success { self.result.clone() } else { Vec::new() };
return VmmYaraResult {
is_completed : self.is_completed,
is_completed_success : self.is_completed_success,
addr_min : self.native.vaMin,
addr_max : self.native.vaMax,
addr_current : self.native.vaCurrent,
total_read_bytes : self.native.cbReadTotal,
total_results : self.result.len() as u32,
result : result_vec,
}
}
fn impl_new<'a>(vmm : &'a Vmm<'a>, rules : Vec<&str>, pid : u32, addr_min : u64, addr_max : u64, num_results_max : u32, flags : u64) -> ResultEx<VmmYara<'a>> {
let num_results_max = std::cmp::min(0x10000, num_results_max);
let addr_min = addr_min & 0xfffffffffffff000;
let addr_max = addr_max & 0xfffffffffffff000;
if addr_max != 0 && addr_max <= addr_min {
return Err(anyhow!("search max address must be larger than min address"));
}
let native_args_rules = rules.iter().map(|arg| CString::new(*arg).unwrap()).collect::<Vec<CString>>();
let native_argv_rules: Vec<*const c_char> = native_args_rules.iter().map(|s| s.as_ptr()).collect();
let native = CVMMDLL_YARA_CONFIG {
dwVersion : VMMDLL_YARA_CONFIG_VERSION,
_Filler : [0; 2],
fAbortRequested : 0,
cMaxResult : num_results_max,
cRules : native_args_rules.len() as u32,
pszRules : native_argv_rules.as_ptr(),
vaMin : addr_min,
vaMax : addr_max,
vaCurrent : 0,
_Filler2 : 0,
cResult : 0,
cbReadTotal : 0,
pvUserPtrOpt : 0,
pfnScanMemoryCB : VmmYara::impl_yara_cb as usize,
ReadFlags : flags,
fForcePTE : 0,
fForceVAD : 0,
pfnFilterOptCB : 0,
pvUserPtrOpt2 : 0,
_Reserved : 0,
};
let yara = VmmYara {
vmm,
pid,
is_started : false,
is_completed : false,
is_completed_success : false,
native,
_native_args_rules : native_args_rules,
_native_argv_rules : native_argv_rules,
thread : None,
result : Vec::new(),
};
return Ok(yara);
}
extern "C" fn impl_yara_cb(ctx : *const CVMMDLL_YARA_CONFIG, yrm : *const CVMMDLL_VMMYARA_RULE_MATCH, _pb_buffer : *const u8, _cb_buffer : usize) -> bool {
unsafe {
if (*ctx).dwVersion != VMMDLL_YARA_CONFIG_VERSION {
return false;
}
if (*yrm).dwVersion != VMMYARA_RULE_MATCH_VERSION {
return false;
}
let addr = (*ctx).vaCurrent;
let rule = cstr_to_string((*yrm).szRuleIdentifier);
let mut tags = Vec::new();
let ctags = std::cmp::min((*yrm).cTags as usize, VMMYARA_RULE_MATCH_TAG_MAX);
for i in 0..ctags {
let tag = cstr_to_string((*yrm).szTags[i]);
tags.push(tag);
}
let mut meta = Vec::new();
let cmeta = std::cmp::min((*yrm).cMeta as usize, VMMYARA_RULE_MATCH_META_MAX);
for i in 0..cmeta {
let key = cstr_to_string((*yrm).meta[i].szIdentifier);
let value = cstr_to_string((*yrm).meta[i].szString);
meta.push((key, value));
}
let mut match_strings = Vec::new();
let cmatch_strings = std::cmp::min((*yrm).cStrings as usize, VMMYARA_RULE_MATCH_STRING_MAX);
for i in 0..cmatch_strings {
let match_string = cstr_to_string((*yrm).strings[i].szString);
let cmatch = std::cmp::min((*yrm).strings[i].cMatch as usize, VMMYARA_RULE_MATCH_OFFSET_MAX);
let mut addresses = Vec::new();
for j in 0..cmatch {
let offset = (*yrm).strings[i].cbMatchOffset[j] as u64;
addresses.push(addr + offset);
}
let match_string = VmmYaraMatchString {
match_string,
addresses,
};
match_strings.push(match_string);
}
let yara_match = VmmYaraMatch {
addr,
rule,
tags,
meta,
match_strings,
};
let ptr_result_vec = (*ctx).pvUserPtrOpt2 as *mut Vec<VmmYaraMatch>;
(*ptr_result_vec).push(yara_match);
return true;
}
}
}
impl<T> fmt::Display for VmmPluginContext<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmPluginContext")
}
}
impl fmt::Display for VmmPluginFileList<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmPluginFileList")
}
}
impl<T> fmt::Display for VmmPluginInitializationContext<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmPluginInitializationContext")
}
}
impl fmt::Display for VmmPluginInitializationInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VmmPluginInitializationInfo")
}
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
struct CVMMDLL_PLUGIN_CONTEXT<'a, T> {
magic : u64,
wVersion : u16,
wSize : u16,
pid : u32,
pProcess : usize,
uszModule : *const c_char,
uszPath : *const c_char,
pvReserved1 : usize,
ctxM : *const VmmPluginContext<'a, T>,
MID : u32,
}
#[repr(C)]
#[allow(non_snake_case, non_camel_case_types)]
struct CVMMDLL_PLUGIN_REGINFO<T> {
magic : u64,
wVersion : u16,
wSize : u16,
tpMemoryModel : u32,
tpSystem : u32,
hDLL : usize,
pfnPluginManager_Register : extern "C" fn(H : usize, pPluginRegInfo : *mut CVMMDLL_PLUGIN_REGINFO<T>) -> bool,
uszPathVmmDLL : *const c_char,
_Reserved : [u32; 30],
Py_fPythonStandalone : bool,
Py__Reserved : u32,
Py_hReservedDllPython3 : usize,
Py_hReservedDllPython3X : usize,
reg_info_ctxM : usize,
reg_info_uszPathName : [u8; 128],
reg_info_fRootModule : u32, reg_info_fProcessModule : u32, reg_info_fRootModuleHidden : u32, reg_info_fProcessModuleHidden : u32, reg_info_sTimelineNameShort : [u8; 6],
reg_info__Reserved : [u8; 2],
reg_info_uszTimelineFile : [u8; 32],
reg_info__Reserved2 : [u8; 32],
reg_fn_pfnList : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>, pFileList : usize) -> bool,
reg_fn_pfnRead : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>, pb : *mut u8, cb : u32, pcbRead : *mut u32, cbOffset : u64) -> u32,
reg_fn_pfnWrite : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>, pb : *const u8, cb : u32, pcbWrite : *mut u32, cbOffset : u64) -> u32,
reg_fn_pfnNotify : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>, fEvent : u32, pvEvent : usize, cbEvent : usize),
reg_fn_pfnClose : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>),
reg_fn_pfnVisibleModule : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT<T>) -> bool,
reg_fn_pvReserved : [usize; 10],
reg_fnfc_pfnInitialize : usize,
reg_fnfc_pfnFinalize : usize,
reg_fnfc_pfnTimeline : usize,
reg_fnfc_pfnIngestPhysmem : usize,
reg_fnfc_pfnIngestVirtmem : usize,
reg_fnfc_pfnIngestFinalize : usize,
reg_fnfc_pfnFindEvil : usize,
reg_fnfc_pvReserved : [usize; 7],
reg_fnfc_pfnLogCSV : usize,
reg_fnfc_pfnLogJSON : usize,
sysinfo_f32 : u32,
sysinfo_dwVersionMajor : u32,
sysinfo_dwVersionMinor : u32,
sysinfo_dwVersionBuild : u32,
sysinfo__Reserved : [u32; 32],
}
fn impl_new_plugin_initialization<T>(native_h : usize, native_reginfo : usize) -> ResultEx<(VmmPluginInitializationInfo, VmmPluginInitializationContext<T>)> {
unsafe {
let reginfo = native_reginfo as *mut CVMMDLL_PLUGIN_REGINFO<T>;
if (*reginfo).magic != VMMDLL_PLUGIN_REGINFO_MAGIC || (*reginfo).wVersion != VMMDLL_PLUGIN_REGINFO_VERSION {
return Err(anyhow!("Bad reginfo magic/version."));
}
let info = VmmPluginInitializationInfo {
tp_system : VmmSystemType::from((*reginfo).tpSystem),
tp_memorymodel : VmmMemoryModelType::from((*reginfo).tpMemoryModel),
version_major : (*reginfo).sysinfo_dwVersionMajor,
version_minor : (*reginfo).sysinfo_dwVersionMinor,
version_build : (*reginfo).sysinfo_dwVersionBuild,
};
let ctx = VmmPluginInitializationContext {
h_vmm : native_h,
h_reginfo : native_reginfo,
ctx : None,
path_name : String::from(""),
is_root_module : false,
is_root_module_hidden : false,
is_process_module : false,
is_process_module_hidden : false,
fn_list : None,
fn_read : None,
fn_write : None,
fn_notify : None,
fn_visible : None,
};
return Ok((info, ctx));
}
}
impl<T> VmmPluginInitializationContext<T> {
fn impl_register(self) -> ResultEx<()> {
unsafe {
let reginfo = self.h_reginfo as *mut CVMMDLL_PLUGIN_REGINFO<T>;
if (*reginfo).magic != VMMDLL_PLUGIN_REGINFO_MAGIC || (*reginfo).wVersion != VMMDLL_PLUGIN_REGINFO_VERSION {
return Err(anyhow!("Bad reginfo magic/version."));
}
if self.ctx.is_none() {
return Err(anyhow!("User context ctx is missing. User context cannot be None."));
}
let pathname_str = str::replace(&self.path_name, "/", "\\");
let pathname_cstring = CString::new(pathname_str)?;
let pathname_bytes = pathname_cstring.to_bytes_with_nul();
if pathname_bytes.len() > (*reginfo).reg_info_uszPathName.len() {
return Err(anyhow!("Plugin path/name too long."));
}
let pathname_len = std::cmp::min(pathname_bytes.len(), (*reginfo).reg_info_uszPathName.len());
let c_path_vmm = CStr::from_ptr((*reginfo).uszPathVmmDLL);
let vmm = impl_new(c_path_vmm.to_str()?, None, self.h_vmm, &Vec::new())?;
let ctx_user = self.ctx.unwrap();
let ctx_rust = VmmPluginContext {
vmm : vmm,
ctxlock : std::sync::RwLock::new(ctx_user),
fn_list : self.fn_list,
fn_read : self.fn_read,
fn_write : self.fn_write,
fn_notify : self.fn_notify,
fn_visible : self.fn_visible,
};
let ctx_rust_box = Box::new(ctx_rust);
let ctx_native = Box::into_raw(ctx_rust_box);
for i in 0..pathname_len {
(*reginfo).reg_info_uszPathName[i] = pathname_bytes[i];
}
(*reginfo).reg_info_ctxM = ctx_native as usize;
(*reginfo).reg_info_fProcessModule = if self.is_process_module { 1 } else { 0 };
(*reginfo).reg_info_fProcessModuleHidden = if self.is_process_module_hidden { 1 } else { 0 };
(*reginfo).reg_info_fRootModule = if self.is_root_module { 1 } else { 0 };
(*reginfo).reg_info_fRootModuleHidden = if self.is_root_module_hidden { 1 } else { 0 };
(*reginfo).reg_fn_pfnClose = impl_plugin_close_cb;
if self.fn_list.is_some() {
(*reginfo).reg_fn_pfnList = impl_plugin_list_cb;
}
if self.fn_read.is_some() {
(*reginfo).reg_fn_pfnRead = impl_plugin_read_cb;
}
if self.fn_write.is_some() {
(*reginfo).reg_fn_pfnWrite = impl_plugin_write_cb;
}
if self.fn_visible.is_some() {
(*reginfo).reg_fn_pfnVisibleModule = impl_plugin_visible_cb;
}
if self.fn_notify.is_some() {
(*reginfo).reg_fn_pfnNotify = impl_plugin_notify_cb;
}
let r = ((*reginfo).pfnPluginManager_Register)(self.h_vmm, reginfo);
if !r {
return Err(anyhow!("Failed registering plugin."));
}
return Ok(());
}
}
}
impl VmmPluginFileList<'_> {
fn impl_add_file(&self, name : &str, size : u64) {
let sz_name = CString::new(name).unwrap();
(self.vmm.native.VMMDLL_VfsList_AddFile)(self.h_file_list, sz_name.as_ptr(), size, 0);
}
fn impl_add_directory(&self, name : &str) {
let sz_name = CString::new(name).unwrap();
(self.vmm.native.VMMDLL_VfsList_AddDirectory)(self.h_file_list, sz_name.as_ptr(), 0);
}
}
extern "C" fn impl_plugin_close_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>) {
unsafe {
drop(Box::from_raw((*ctxp).ctxM as *mut VmmPluginContext<T>));
}
}
extern "C" fn impl_plugin_list_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>, h_pfilelist : usize) -> bool {
unsafe {
let ctx = &*(*ctxp).ctxM;
if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) {
return true;
}
let callback = ctx.fn_list.unwrap();
let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None };
let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/");
let path = path_string.as_str();
if path == "[err]" {
return true;
}
let filelist = VmmPluginFileList {
vmm : &ctx.vmm,
h_file_list : h_pfilelist,
};
let _r = (callback)(ctx, process, path, &filelist);
return true;
}
}
extern "C" fn impl_plugin_read_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>, pb : *mut u8, cb : u32, pcb_read : *mut u32, cb_offset : u64) -> u32 {
unsafe {
*pcb_read = 0;
let ctx = &*(*ctxp).ctxM;
if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) {
return VMMDLL_STATUS_FILE_INVALID;
}
let callback = ctx.fn_read.unwrap();
let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None };
let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/");
let path = path_string.as_str();
if path == "[err]" {
return VMMDLL_STATUS_FILE_INVALID;
}
let r = match (callback)(ctx, process, path, cb, cb_offset) {
Err(_) => return VMMDLL_STATUS_FILE_INVALID,
Ok(r) => r,
};
if r.len() == 0 {
return VMMDLL_STATUS_END_OF_FILE;
}
if r.len() > u32::MAX as usize {
return VMMDLL_STATUS_FILE_INVALID;
}
*pcb_read = r.len() as u32;
std::ptr::copy_nonoverlapping(r.as_ptr(), pb, r.len());
return VMMDLL_STATUS_SUCCESS;
}
}
extern "C" fn impl_plugin_write_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>, pb : *const u8, cb : u32, pcb_write : *mut u32, cb_offset : u64) -> u32 {
unsafe {
*pcb_write = 0;
let ctx = &*(*ctxp).ctxM;
if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) {
return VMMDLL_STATUS_FILE_INVALID;
}
let callback = ctx.fn_write.unwrap();
let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None };
let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/");
let path = path_string.as_str();
if path == "[err]" {
return VMMDLL_STATUS_FILE_INVALID;
}
let size = cb as usize;
let mut data = vec![0u8; size];
std::ptr::copy_nonoverlapping(pb, data.as_mut_ptr(), size);
if (callback)(ctx, process, path, data, cb_offset).is_err() {
return VMMDLL_STATUS_FILE_INVALID;
};
*pcb_write = cb;
return VMMDLL_STATUS_SUCCESS;
}
}
extern "C" fn impl_plugin_visible_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>) -> bool {
unsafe {
let ctx = &*(*ctxp).ctxM;
if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) {
return false;
}
let callback = ctx.fn_visible.unwrap();
let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None };
let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/");
let path = path_string.as_str();
if path == "[err]" {
return false;
}
return (callback)(ctx, process).unwrap_or(false);
}
}
extern "C" fn impl_plugin_notify_cb<T>(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT<T>, f_event : u32, _pv_event : usize, _cb_event : usize) {
unsafe {
let ctx = &*(*ctxp).ctxM;
if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) {
return;
}
let callback = ctx.fn_notify.unwrap();
let _r = (callback)(ctx, f_event);
}
}
#[allow(dead_code)]
#[allow(non_snake_case)]
#[derive(Debug)]
struct LcNative {
h : usize,
library_lc : libloading::Library,
config : CLC_CONFIG,
LcCreate : extern "C" fn(pLcCreateConfig : *mut CLC_CONFIG) -> usize,
LcClose : extern "C" fn(hLC : usize),
LcMemFree : extern "C" fn(pvMem : usize),
LcRead : extern "C" fn(hLC : usize, pa : u64, cb : u32, pb : *mut u8) -> bool,
LcWrite : extern "C" fn(hLC : usize, pa : u64, cb : u32, pb : *const u8) -> bool,
LcGetOption : extern "C" fn(hLC : usize, fOption : u64, pqwValue : *mut u64) -> bool,
LcSetOption : extern "C" fn(hLC : usize, fOption : u64, qwValue : u64) -> bool,
LcCommand : extern "C" fn(hLC : usize, fCommand : u64, cbDataIn : u32, pbDataIn : *const u8, ppbDataOut : *mut *mut u8, pcbDataOut : *mut u32) -> bool,
LcCommandPtr : extern "C" fn(hLC : usize, fCommand : u64, cbDataIn : u32, pbDataIn : usize, ppbDataOut : *mut usize, pcbDataOut : *mut u32) -> bool,
}
impl fmt::Display for LeechCore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LeechCore")
}
}
impl fmt::Display for LcBar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_valid {
write!(f, "LcBar:{}:[{:x}->{:x}]", self.bar_index, self.pa, self.pa + self.cb - 1)
} else {
write!(f, "LcBar:{}:inactive", self.bar_index)
}
}
}
impl fmt::Display for LcBarRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let tp = if self.is_write { "write" } else { "read" };
write!(f, "LcBarRequest:{}:{tp}:[{:x}:{:x}]", self.bar.bar_index, self.data_offset, self.data_size)
}
}
impl<T> fmt::Display for LcBarContext<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LcBarContext")
}
}
impl<T> fmt::Display for LcBarContextWrap<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LcBarContextWrap")
}
}
impl<T> fmt::Display for LcTlpContext<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LcTlpContext")
}
}
impl<T> fmt::Display for LcTlpContextWrap<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LcTlpContextWrap")
}
}
impl Drop for LeechCore {
fn drop(&mut self) {
(self.native.LcClose)(self.native.h);
}
}
impl Clone for LeechCore {
fn clone(&self) -> Self {
let lc_init_arg = format!("existing://0x{:x}", self.native.h);
let lc_clone = LeechCore::new(&self.path_lc, &lc_init_arg, 0).unwrap();
return lc_clone;
}
}
impl<T> Drop for LcBarContext<'_, T> {
fn drop(&mut self) {
let mut native_ctx : usize = 0;
let r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_BAR_CONTEXT_RD, 0, 0, &mut native_ctx, std::ptr::null_mut());
if r && self.native_ctx == native_ctx {
let _r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_BAR_FUNCTION_CALLBACK, 0, 0, std::ptr::null_mut(), std::ptr::null_mut());
let _r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_BAR_CONTEXT, 0, 0, std::ptr::null_mut(), std::ptr::null_mut());
}
}
}
impl<T> Drop for LcBarContextWrap<'_, T> {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.native));
}
}
}
impl<T> Drop for LcTlpContext<'_, T> {
fn drop(&mut self) {
let mut native_ctx : usize = 0;
let r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_TLP_CONTEXT_RD, 0, 0, &mut native_ctx, std::ptr::null_mut());
if r && self.native_ctx == native_ctx {
let _r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_TLP_FUNCTION_CALLBACK, 0, 0, std::ptr::null_mut(), std::ptr::null_mut());
let _r = (self.lc.native.LcCommandPtr)(self.lc.native.h, LeechCore::LC_CMD_FPGA_TLP_CONTEXT, 0, 0, std::ptr::null_mut(), std::ptr::null_mut());
}
}
}
impl<T> Drop for LcTlpContextWrap<'_, T> {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.native));
}
}
}
#[allow(non_snake_case)]
impl LeechCore {
#[allow(non_snake_case)]
fn impl_new(lc_lib_path : &str, device_config : &str, remote_config : &str, lc_config_printf_verbosity : u32, pa_max : u64) -> ResultEx<LeechCore> {
unsafe {
let path = std::path::Path::new(lc_lib_path).canonicalize()?;
let str_path_lc = path.to_str().unwrap_or("");
let library_lc : libloading::Library = libloading::Library::new(str_path_lc)
.with_context(|| format!("Failed to load leechcore library at: {}", str_path_lc))?;
let LcCreate : extern "C" fn(pLcCreateConfig : *mut CLC_CONFIG) -> usize = *library_lc.get(b"LcCreate")?;
let LcClose = *library_lc.get(b"LcClose")?;
let LcMemFree = *library_lc.get(b"LcMemFree")?;
let LcRead = *library_lc.get(b"LcRead")?;
let LcWrite = *library_lc.get(b"LcWrite")?;
let LcGetOption = *library_lc.get(b"LcGetOption")?;
let LcSetOption = *library_lc.get(b"LcSetOption")?;
let LcCommand = *library_lc.get(b"LcCommand")?;
let LcCommandPtr = *library_lc.get(b"LcCommand")?;
let device_config_bytes = &*(device_config.as_bytes() as *const [u8] as *const [c_char]);
let mut device_sz: [c_char; 260] = [0; 260];
device_sz[..device_config_bytes.len().min(260-1)].copy_from_slice(device_config_bytes);
let remote_config_bytes = &*(remote_config.as_bytes() as *const [u8] as *const [c_char]);
let mut remote_sz: [c_char; 260] = [0; 260];
remote_sz[..remote_config_bytes.len().min(260-1)].copy_from_slice(remote_config_bytes);
let mut config = CLC_CONFIG {
dwVersion : LeechCore::LC_CONFIG_VERSION,
dwPrintfVerbosity : lc_config_printf_verbosity,
szDevice : device_sz,
szRemote : remote_sz,
pfn_printf_opt : 0,
paMax : pa_max,
fVolatile : 0,
fWritable : 0,
fRemote : 0,
fRemoteDisableCompress : 0,
szDeviceName : [0; 260],
};
let h: usize;
h = (LcCreate)(&mut config);
if h == 0 {
return Err(anyhow!("LcCreate: fail"));
}
let native = LcNative {
h,
library_lc,
config,
LcCreate,
LcClose,
LcMemFree,
LcRead,
LcWrite,
LcGetOption,
LcSetOption,
LcCommand,
LcCommandPtr,
};
let lc = LeechCore {
path_lc : str_path_lc.to_string(),
native,
};
return Ok(lc);
}
}
fn impl_get_option(&self, config_id : u64) -> ResultEx<u64> {
let mut v = 0;
let f = (self.native.LcGetOption)(self.native.h, config_id, &mut v);
return if f { Ok(v) } else { Err(anyhow!("LcGetOption: fail")) };
}
fn impl_set_option(&self, config_id : u64, config_value : u64) -> ResultEx<()> {
let f = (self.native.LcSetOption)(self.native.h, config_id, config_value);
return if f { Ok(()) } else { Err(anyhow!("LcSetOption: fail")) };
}
fn impl_mem_read(&self, pa : u64, size : usize) -> ResultEx<Vec<u8>> {
let cb = u32::try_from(size)?;
let mut pb_result: Vec<u8> = vec![0u8; size];
let r = (self.native.LcRead)(self.native.h, pa, cb, pb_result.as_mut_ptr());
if !r {
return Err(anyhow!("LcRead: fail."));
}
return Ok(pb_result);
}
fn impl_mem_read_as<T>(&self, pa : u64) -> ResultEx<T> {
unsafe {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let mut result : T = std::mem::zeroed();
let r = (self.native.LcRead)(self.native.h, pa, cb, &mut result as *mut _ as *mut u8);
if !r {
return Err(anyhow!("LcRead: fail."));
}
return Ok(result);
}
}
fn impl_mem_write(&self, va : u64, data : &Vec<u8>) -> ResultEx<()> {
let cb = u32::try_from(data.len())?;
let pb = data.as_ptr();
let r = (self.native.LcWrite)(self.native.h, va, cb, pb);
if !r {
return Err(anyhow!("LcWrite: fail."));
}
return Ok(());
}
fn impl_mem_write_as<T>(&self, va : u64, data : &T) -> ResultEx<()> {
let cb = u32::try_from(std::mem::size_of::<T>())?;
let r = (self.native.LcWrite)(self.native.h, va, cb, data as *const _ as *const u8);
if !r {
return Err(anyhow!("LcWrite: fail."));
}
return Ok(());
}
fn impl_command(&self, command_id : u64, data : Option<&Vec<u8>>) -> ResultEx<Option<Vec<u8>>> {
unsafe {
let mut pb_out : *mut u8 = std::ptr::null_mut();
let mut cb_out : u32 = 0;
let cb_in;
let pb_in;
match data {
Some(data) => {
cb_in = u32::try_from(data.len())?;
pb_in = data.as_ptr();
},
None => {
cb_in = 0;
pb_in = std::ptr::null();
},
}
let r = (self.native.LcCommand)(self.native.h, command_id, cb_in, pb_in, &mut pb_out, &mut cb_out);
if !r {
return Err(anyhow!("LcCommand: fail."));
}
if pb_out.is_null() {
return Ok(None);
}
let mut pb_result: Vec<u8> = vec![0u8; cb_out as usize];
std::ptr::copy_nonoverlapping(pb_out, pb_result.as_mut_ptr(), cb_out as usize);
(self.native.LcMemFree)(pb_out as usize);
return Ok(Some(pb_result));
}
}
fn impl_get_memmap(&self) -> ResultEx<String> {
let memmap_vec = self.command(LeechCore::LC_CMD_MEMMAP_GET, None)?;
match memmap_vec {
Some(memmap_vec) => {
let memmap_str = String::from_utf8(memmap_vec)?;
return Ok(memmap_str);
},
None => {
return Err(anyhow!("Failed to get memmap."));
},
}
}
fn impl_set_memmap(&self, str_memmap : &str) -> ResultEx<()> {
let memmap_vec = str_memmap.as_bytes().to_vec();
self.command(LeechCore::LC_CMD_MEMMAP_SET, Some(&memmap_vec))?;
return Ok(());
}
fn impl_pcie_bar_info(&self) -> ResultEx<[LcBar; 6]> {
unsafe {
let mut cb_out = 0;
let mut pb_out = 0;
let r = (self.native.LcCommandPtr)(self.native.h, LeechCore::LC_CMD_FPGA_BAR_INFO, 0, 0, &mut pb_out, &mut cb_out);
if !r {
return Err(anyhow!("LcCommand: fail."));
}
if pb_out == 0 || cb_out as usize != 6 * std::mem::size_of::<CLC_BAR>() {
return Err(anyhow!("Failed to get PCIe BARs."));
}
let structs = pb_out as *const CLC_BAR;
let mut result : [LcBar; 6] = [LcBar::default(); 6];
let pMap = std::slice::from_raw_parts(structs, 6);
for i in 0..6 {
let ne = &pMap[i];
result[i] = LcBar {
bar_index : ne.iBar,
is_valid : ne.fValid != 0,
is_io : ne.fIO != 0,
is_64bit : ne.f64Bit != 0,
is_prefetchable : ne.fPrefetchable != 0,
pa : ne.pa,
cb : ne.cb,
};
}
return Ok(result);
}
}
fn impl_pcie_tlp_write(&self, tlp : &[u8]) -> ResultEx<()> {
if tlp.len() % 4 > 0 {
return Err(anyhow!("TLP length must be a multiple of 4."));
}
let r = (self.native.LcCommand)(self.native.h, LeechCore::LC_CMD_FPGA_TLP_WRITE_SINGLE, tlp.len() as u32, tlp.as_ptr(), std::ptr::null_mut(), std::ptr::null_mut());
if !r {
return Err(anyhow!("LcCommand: fail."));
}
return Ok(());
}
fn impl_pcie_bar_callback<T>(&self, ctx_user : T, fn_bar_callback : fn(ctx : &LcBarContext<T>, req : &LcBarRequest) -> ResultEx<()>) -> ResultEx<LcBarContextWrap<T>> {
unsafe {
let ctx = LcBarContext {
lc : self,
ctxlock : std::sync::RwLock::new(ctx_user),
fn_callback : fn_bar_callback,
native_ctx : 0,
};
let ctx_rust_box = Box::new(ctx);
let ctx_native = Box::into_raw(ctx_rust_box); (*ctx_native).native_ctx = ctx_native as usize;
let native_pfn = LeechCore::impl_pcie_bar_callback_external::<T> as usize;
let r = (self.native.LcCommandPtr)(self.native.h, LeechCore::LC_CMD_FPGA_BAR_CONTEXT, 0, ctx_native as usize, std::ptr::null_mut(), std::ptr::null_mut());
if !r {
return Err(anyhow!("LcCommand: fail."));
}
let r = (self.native.LcCommandPtr)(self.native.h, LeechCore::LC_CMD_FPGA_BAR_FUNCTION_CALLBACK, 0, native_pfn, std::ptr::null_mut(), std::ptr::null_mut());
if !r {
return Err(anyhow!("LcCommand: fail."));
}
let ctx_wrap = LcBarContextWrap {
ctx : &*ctx_native,
native : ctx_native,
};
return Ok(ctx_wrap);
}
}
fn impl_pcie_tlp_callback<T>(&self, ctx_user : T, fn_tlp_callback : fn(ctx : &LcTlpContext<T>, tlp : &[u8], tlp_str : &str) -> ResultEx<()>) -> ResultEx<LcTlpContextWrap<T>> {
unsafe {
let ctx = LcTlpContext {
lc : self,
ctxlock : std::sync::RwLock::new(ctx_user),
fn_callback : fn_tlp_callback,
native_ctx : 0,
};
let ctx_rust_box = Box::new(ctx);
let ctx_native = Box::into_raw(ctx_rust_box); (*ctx_native).native_ctx = ctx_native as usize;
let native_pfn = LeechCore::impl_pcie_tlp_callback_external::<T> as usize;
let r = (self.native.LcSetOption)(self.native.h, LeechCore::LC_OPT_FPGA_TLP_READ_CB_WITHINFO, 1);
if !r {
return Err(anyhow!("LcSetOption: fail."));
}
let r = (self.native.LcCommandPtr)(self.native.h, LeechCore::LC_CMD_FPGA_TLP_CONTEXT, 0, ctx_native as usize, std::ptr::null_mut(), std::ptr::null_mut());
if !r {
return Err(anyhow!("LcCommand: fail."));
}
let r = (self.native.LcCommandPtr)(self.native.h, LeechCore::LC_CMD_FPGA_TLP_FUNCTION_CALLBACK, 0, native_pfn, std::ptr::null_mut(), std::ptr::null_mut());
if !r {
return Err(anyhow!("LcCommand: fail."));
}
let ctx_wrap = LcTlpContextWrap {
ctx : &*ctx_native,
native : ctx_native,
};
return Ok(ctx_wrap);
}
}
extern "C" fn impl_pcie_tlp_callback_external<T>(native_ctx : *const LcTlpContext<T>, cbTlp : u32, pbTlp : *const u8, cbInfo : u32, szInfo : *const u8) {
unsafe {
let ctx : &LcTlpContext<T> = &*native_ctx;
let tlp = std::slice::from_raw_parts(pbTlp, cbTlp as usize);
let info = std::str::from_utf8_unchecked(std::slice::from_raw_parts(szInfo, cbInfo as usize));
let _r = (ctx.fn_callback)(ctx, tlp, info);
}
}
extern "C" fn impl_pcie_bar_callback_external<T>(native_bar_request : *mut LC_BAR_REQUEST) {
unsafe {
let req = &*native_bar_request;
let ctx = &*(req.ctx as *const LcBarContext<T>);
let ne = &*req.pBar;
let bar = LcBar {
bar_index : ne.iBar,
is_valid : ne.fValid != 0,
is_io : ne.fIO != 0,
is_64bit : ne.f64Bit != 0,
is_prefetchable : ne.fPrefetchable != 0,
pa : ne.pa,
cb : ne.cb,
};
let data_write : Option<Vec<u8>>;
if req.fWrite != 0 {
data_write = Some(std::slice::from_raw_parts(req.pbData.as_ptr(), req.cbData as usize).to_vec());
} else {
data_write = None;
}
let bar_request = LcBarRequest {
native : native_bar_request,
bar,
tag : req.bTag,
be_first : req.bFirstBE,
be_last : req.bLastBE,
is_64bit : req.f64 != 0,
is_read : req.fRead != 0,
is_write : req.fWrite != 0,
data_size : req.cbData,
data_offset : req.oData,
data_write,
};
let _r = (ctx.fn_callback)(ctx, &bar_request);
}
}
}
#[allow(non_snake_case)]
impl LcBarRequest {
fn impl_read_reply(&self, data_reply : &[u8], is_fail : bool) -> ResultEx<()> {
unsafe {
if !self.is_read {
return Err(anyhow!("LcBarRequest: only allowed to reply to read requests."));
}
if !is_fail && self.data_size != data_reply.len() as u32 {
return Err(anyhow!("LcBarRequest: reply data size mismatch."));
}
(*self.native).fReadReply = 1;
(*self.native).cbData = data_reply.len() as u32;
(*self.native).pbData[..data_reply.len()].copy_from_slice(data_reply);
return Ok(());
}
}
}
#[repr(C)]
#[derive(Clone, Debug)]
#[allow(non_snake_case, non_camel_case_types)]
struct CLC_CONFIG {
dwVersion : u32,
dwPrintfVerbosity : u32,
szDevice : [c_char; 260],
szRemote : [c_char; 260],
pfn_printf_opt : usize,
paMax : u64,
fVolatile : u32,
fWritable : u32,
fRemote : u32,
fRemoteDisableCompress : u32,
szDeviceName : [c_char; 260],
}
#[repr(C)]
#[derive(Clone, Debug)]
#[allow(non_snake_case, non_camel_case_types)]
struct CLC_BAR {
fValid : u32,
fIO : u32,
f64Bit : u32,
fPrefetchable : u32,
_Filler : [u32; 3],
iBar : u32,
pa : u64,
cb : u64,
}
#[repr(C)]
#[derive(Clone, Debug)]
#[allow(non_snake_case, non_camel_case_types)]
struct LC_BAR_REQUEST {
ctx : usize,
pBar : *const CLC_BAR,
bTag : u8,
bFirstBE : u8,
bLastBE : u8,
_Filler : u8,
f64 : u32,
fRead : u32,
fReadReply : u32,
fWrite : u32,
cbData : u32,
oData : u64,
pbData : [u8; 1024],
}