use super::strings;
pub fn is_virtual_machine() -> bool {
let mut signals: u32 = 0;
if check_cpuid_vendor() {
signals += 1;
}
if check_vm_processes() {
signals += 1;
}
if check_vm_files() {
signals += 1;
}
if check_mac_address() {
signals += 1;
}
signals >= 2
}
fn check_cpuid_vendor() -> bool {
#[cfg(target_arch = "x86_64")]
{
use std::arch::x86_64::__cpuid;
#[allow(unused_unsafe)]
let result = unsafe { __cpuid(0x4000_0000) };
let mut vendor = [0u8; 12];
vendor[0..4].copy_from_slice(&result.ebx.to_le_bytes());
vendor[4..8].copy_from_slice(&result.ecx.to_le_bytes());
vendor[8..12].copy_from_slice(&result.edx.to_le_bytes());
let vmware = strings::decode(&strings::CPUID_VMWARE);
let vbox = strings::decode(&strings::CPUID_VBOX);
let kvm = strings::decode(&strings::CPUID_KVM);
let hyperv = strings::decode(&strings::CPUID_HYPERV);
vendor == vmware.as_bytes()
|| vendor == vbox.as_bytes()
|| vendor[..9] == kvm.as_bytes()[..9]
|| vendor == hyperv.as_bytes()
}
#[cfg(not(target_arch = "x86_64"))]
{
false
}
}
fn check_vm_processes() -> bool {
#[cfg(target_os = "windows")]
{
use winapi::um::handleapi::CloseHandle;
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
TH32CS_SNAPPROCESS,
};
let vm_procs = [
strings::decode(&strings::VMTOOLSD),
strings::decode(&strings::VMWARETRAY),
strings::decode(&strings::VMWAREUSER),
strings::decode(&strings::VBOXSERVICE),
strings::decode(&strings::VBOXTRAY),
strings::decode(&strings::VMUSRVC),
strings::decode(&strings::VMSRVC),
];
unsafe {
let snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap == winapi::um::handleapi::INVALID_HANDLE_VALUE {
return false;
}
let mut pe = PROCESSENTRY32W {
dwSize: std::mem::size_of::<PROCESSENTRY32W>() as u32,
..std::mem::zeroed()
};
let mut found = false;
if Process32FirstW(snap, &mut pe) != 0 {
loop {
let name_raw: Vec<u16> = pe
.szExeFile
.iter()
.take_while(|&&c| c != 0)
.copied()
.collect();
let name = String::from_utf16_lossy(&name_raw).to_lowercase();
if vm_procs.iter().any(|p| name.starts_with(p.as_str())) {
found = true;
break;
}
if Process32NextW(snap, &mut pe) == 0 {
break;
}
}
}
CloseHandle(snap);
found
}
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
fn check_vm_files() -> bool {
#[cfg(target_os = "windows")]
{
let paths = [
strings::decode(&strings::VMMOUSE_SYS),
strings::decode(&strings::VMHGFS_SYS),
strings::decode(&strings::VBOXGUEST_SYS),
strings::decode(&strings::VBOXMOUSE_SYS),
];
paths
.iter()
.any(|p| std::path::Path::new(p.trim_end_matches('\0')).exists())
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
fn check_mac_address() -> bool {
#[cfg(target_os = "windows")]
{
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::iphlpapi::GetAdaptersInfo;
use winapi::um::iptypes::IP_ADAPTER_INFO;
let oui_vbox = strings::decode(&strings::MAC_VBOX);
let oui_vmware1 = strings::decode(&strings::MAC_VMWARE1);
let oui_vmware2 = strings::decode(&strings::MAC_VMWARE2);
let oui_qemu = strings::decode(&strings::MAC_QEMU);
unsafe {
let mut size: u32 = 0;
GetAdaptersInfo(std::ptr::null_mut(), &mut size);
if size == 0 {
return false;
}
let count = (size as usize) / std::mem::size_of::<IP_ADAPTER_INFO>() + 1;
let mut adapters: Vec<IP_ADAPTER_INFO> = Vec::with_capacity(count);
adapters.resize_with(count, || std::mem::zeroed());
if GetAdaptersInfo(adapters.as_mut_ptr(), &mut size) != ERROR_SUCCESS {
return false;
}
let mut ptr: *const IP_ADAPTER_INFO = adapters.as_ptr();
while !ptr.is_null() {
let a = &*ptr;
if a.AddressLength as usize >= 6 {
let m = &a.Address[..6];
let oui = format!("{:02X}{:02X}{:02X}", m[0], m[1], m[2]);
if oui == oui_vbox
|| oui == oui_vmware1
|| oui == oui_vmware2
|| oui == oui_qemu
{
return true;
}
}
ptr = a.Next;
}
}
false
}
#[cfg(not(target_os = "windows"))]
{
false
}
}