diff --git a/docs/documentation.md b/docs/documentation.md
index 35eb4698..bc93433b 100644
--- a/docs/documentation.md
+++ b/docs/documentation.md
@@ -510,90 +510,90 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
| Flag alias | Description | Supported platforms | Certainty | Admin? | 32-bit only? | Notes | Code implementation |
| ---------- | ----------- | ------------------- | --------- | ------ | ------------ | ----- | ------------------- |
-| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4284) |
-| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4302) |
-| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID ECX bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4376) |
-| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4402) |
-| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4588) |
-| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7228) |
-| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5285) |
-| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6137) |
-| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5166) |
-| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5190) |
-| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5215) |
-| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5233) |
-| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5248) |
-| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5391) |
-| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5432) |
-| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7528) |
-| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7252) |
-| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7559) |
-| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7669) |
-| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6148) |
-| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5442) |
-| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7729) |
-| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4430) |
-| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7288) |
-| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7321) |
-| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7418) |
-| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7459) |
-| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7838) |
+| `VM::VMID` | Check CPUID output of manufacturer ID for known VMs/hypervisors at leaf 0 and 0x40000000-0x40000100 | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4283) |
+| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4301) |
+| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID ECX bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4375) |
+| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4401) |
+| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4587) |
+| `VM::THREAD_COUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings, nowadays physical CPUs should have at least 4 threads for modern CPUs | 🐧🪟🍏 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7159) |
+| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5216) |
+| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6068) |
+| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5097) |
+| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5121) |
+| `VM::CTYPE` | Check if the chassis type is valid (it's very often invalid in VMs) | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5146) |
+| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5164) |
+| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5179) |
+| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5322) |
+| `VM::HWMON` | Check if /sys/class/hwmon/ directory is present. If not, likely a VM | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5363) |
+| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7459) |
+| `VM::HWMODEL` | Check if the sysctl for the hwmodel does not contain the "Mac" string | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7183) |
+| `VM::WINE` | Check if the function "wine_get_unix_file_name" is present and if the OS booted from a VHD container | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7490) |
+| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7600) |
+| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6079) |
+| `VM::LINUX_USER_HOST` | Check for default VM username and hostname for linux | 🐧 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5373) |
+| `VM::GAMARUE` | Check for Gamarue ransomware technique which compares VM-specific Window product IDs | 🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7660) |
+| `VM::BOCHS_CPU` | Check for various Bochs-related emulation oversights through CPU checks | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4429) |
+| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7219) |
+| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7252) |
+| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7349) |
+| `VM::MAC_SIP` | Check for the status of System Integrity Protection and hv_mm_present | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7390) |
+| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7769) |
| `VM::SYSTEM_REGISTERS` | | | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L1) |
-| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5471) |
-| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5983) |
-| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5780) |
-| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🐧 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5799) |
-| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7889) |
-| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7914) |
-| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7975) |
-| `VM::THREAD_MISMATCH` | Check if the system's thread count matches the expected thread count for the detected CPU model | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4510) |
-| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8061) |
-| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8117) |
-| `VM::AZURE` | Check for default Azure hostname format (Azure uses Hyper-V as their base VM brand) | 🐧🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6385) |
-| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8173) |
-| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8208) |
-| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5487) |
-| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4538) |
-| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4564) |
-| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5568) |
-| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5597) |
-| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5625) |
-| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5673) |
-| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5703) |
-| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5757) |
-| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5833) |
-| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5855) |
-| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5938) |
-| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5968) |
-| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6000) |
-| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8225) |
-| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8323) |
-| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8562) |
-| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8668) |
-| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8706) |
-| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6028) |
-| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8809) |
-| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8839) |
-| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5510) |
-| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8907) |
-| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6445) |
-| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6058) |
-| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9001) |
-| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6085) |
-| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6872) |
-| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9109) |
-| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9254) |
-| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9001) |
-| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9529) |
-| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9576) |
-| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9695) |
-| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7503) |
-| `VM::KERNEL_OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9796) |
-| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9986) |
-| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10534) |
-| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10545) |
-| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10801) |
-| `VM::CLOCK` | Check the presence of system timers | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L11269) |
+| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5402) |
+| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5914) |
+| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5711) |
+| `VM::VMWARE_DMESG` | Check for VMware-specific device name in dmesg output | 🐧 | 65% | Admin | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5730) |
+| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7820) |
+| `VM::VMWARE_BACKDOOR` | Check for official VMware io port backdoor technique | 🪟 | 100% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7845) |
+| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7906) |
+| `VM::THREAD_MISMATCH` | Check if the system's thread count matches the expected thread count for the detected CPU model | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4509) |
+| `VM::CUCKOO_DIR` | Check for cuckoo directory using crt and WIN API directory functions | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7992) |
+| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8048) |
+| `VM::AZURE` | Check for default Azure hostname format (Azure uses Hyper-V as their base VM brand) | 🐧🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6316) |
+| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8104) |
+| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8139) |
+| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5418) |
+| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4537) |
+| `VM::KGT_SIGNATURE` | Check for Intel KGT (Trusty branch) hypervisor signature in CPUID | 🐧🪟🍏 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4563) |
+| `VM::QEMU_VIRTUAL_DMI` | Check for presence of QEMU in the /sys/devices/virtual/dmi/id directory | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5499) |
+| `VM::QEMU_USB` | Check for presence of QEMU in the /sys/kernel/debug/usb/devices directory | 🐧 | 20% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5528) |
+| `VM::HYPERVISOR_DIR` | Check for presence of any files in /sys/hypervisor directory | 🐧 | 20% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5556) |
+| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5604) |
+| `VM::KMSG` | Check for any indications of hypervisors in the kernel message logs | 🐧 | 5% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5634) |
+| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5688) |
+| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5764) |
+| `VM::DMI_SCAN` | Check for string matches of VM brands in the linux DMI | 🐧 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5786) |
+| `VM::SMBIOS_VM_BIT` | Check for the VM bit in the SMBIOS data | 🐧 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5869) |
+| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5899) |
+| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5931) |
+| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8156) |
+| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8254) |
+| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8493) |
+| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8599) |
+| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8637) |
+| `VM::QEMU_FW_CFG` | Detect QEMU fw_cfg interface. This first checks the Device Tree for a fw-cfg node or hypervisor tag, then verifies the presence of the qemu_fw_cfg module and firmware directories in sysfs. | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5959) |
+| `VM::VIRTUAL_PROCESSORS` | Check if the number of virtual and logical processors are reported correctly by the system | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8740) |
+| `VM::HYPERVISOR_QUERY` | Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8770) |
+| `VM::AMD_SEV` | Check for AMD-SEV MSR running on the system | 🐧🍏 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5441) |
+| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8838) |
+| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6376) |
+| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5989) |
+| `VM::AUDIO` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8932) |
+| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | 🐧 | 75% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6016) |
+| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6803) |
+| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9040) |
+| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9185) |
+| `VM::UD` | Check if no waveform-audio output devices are present in the system | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8932) |
+| `VM::BLOCKSTEP` | Check if a hypervisor does not properly restore the interruptibility state after a VM-exit in compatibility mode | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9460) |
+| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9507) |
+| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9626) |
+| `VM::MAC_SYS` | Check for VM-strings in system profiler commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7434) |
+| `VM::KERNEL_OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9727) |
+| `VM::NVRAM` | Check for known NVRAM signatures that are present on virtual firmware | 🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9917) |
+| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10454) |
+| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10465) |
+| `VM::CPU_HEURISTIC` | Check whether the CPU is genuine and its reported instruction capabilities are not masked | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10721) |
+| `VM::CLOCK` | Check the presence of system timers | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L11189) |
diff --git a/src/vmaware.hpp b/src/vmaware.hpp
index 928f5670..a55a9609 100644
--- a/src/vmaware.hpp
+++ b/src/vmaware.hpp
@@ -54,14 +54,14 @@
*
*
* ============================== SECTIONS ==================================
- * - enums for publicly accessible techniques => line 546
- * - struct for internal cpu operations => line 718
- * - struct for internal memoization => line 3042
- * - struct for internal utility functions => line 3224
- * - struct for internal core components => line 11418
- * - start of VM detection technique list => line 4279
- * - start of public VM detection functions => line 11796
- * - start of externally defined variables => line 12816
+ * - enums for publicly accessible techniques => line 545
+ * - struct for internal cpu operations => line 717
+ * - struct for internal memoization => line 3041
+ * - struct for internal utility functions => line 3223
+ * - struct for internal core components => line 11338
+ * - start of VM detection technique list => line 4278
+ * - start of public VM detection functions => line 11716
+ * - start of externally defined variables => line 12736
*
*
* ============================== EXAMPLE ===================================
@@ -4601,8 +4601,8 @@ struct VM {
debug("TIMER: Running inside a binary translation layer");
return false;
}
- // will be used in cpuid measurements later
- u16 cycle_threshold = 800; // average latency of a VMX/SVM VMEXIT
+ // will be used in cpuid measurements
+ u16 cycle_threshold = 800; // average latency of a VMX/SVM VMEXIT alone
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
cycle_threshold = 3250; // if we're running under Hyper-V, make VMAware detect nested virtualization
}
@@ -4616,13 +4616,12 @@ struct VM {
return true;
}
- const u64 ITER_XOR = 50000000ULL;
- const size_t CPUID_ITER = 100; // per leaf
- const unsigned int leaves[] = {
+ constexpr u64 ITER_XOR = 100000000ULL;
+ constexpr size_t CPUID_ITER = 100; // per leaf
+ static constexpr unsigned int leaves[] = {
0xB, 0xD, 0x4, 0x1, 0x7, 0xA, 0x12, 0x5, 0x40000000u, 0x80000008u, 0x0
};
- const size_t n_leaves = sizeof(leaves) / sizeof(leaves[0]);
- const size_t samples_expected = n_leaves * CPUID_ITER;
+ constexpr size_t n_leaves = sizeof(leaves) / sizeof(leaves[0]);
unsigned hw = std::thread::hardware_concurrency();
if (hw == 0) hw = 1;
@@ -4634,11 +4633,9 @@ struct VM {
std::atomic t2_start(0), t2_end(0);
std::atomic t2_accum(0);
- std::vector samples;
- samples.resize(samples_expected);
- for (size_t i = 0; i < samples.size(); ++i) samples[i] = 0;
+ std::vector samples(100000, 0);
- auto rdtsc = []() -> u64 {
+ auto rdtsc = []() noexcept -> u64 {
#if (MSVC)
return static_cast(__rdtsc());
#else
@@ -4646,28 +4643,60 @@ struct VM {
#endif
};
- // best-effort affinity as a local lambda; on macOS it's a no-op
- auto try_set_affinity = [](std::thread& t, unsigned core) {
+ struct affinity_cookie {
+ bool valid{ false };
+ #if (WINDOWS)
+ HANDLE thread_handle{ nullptr };
+ DWORD_PTR prev_mask{ 0 };
+ #elif (LINUX)
+ pthread_t thread{ 0 };
+ cpu_set_t prev_mask{};
+ #endif
+ };
+
+ auto set_affinity = [](std::thread& t, unsigned core) -> affinity_cookie {
+ affinity_cookie cookie;
#if (WINDOWS)
HANDLE h = static_cast(t.native_handle());
DWORD_PTR mask = static_cast(1ULL) << core;
- (void)SetThreadAffinityMask(h, mask);
+ DWORD_PTR prev = SetThreadAffinityMask(h, mask);
+ if (prev != 0) {
+ cookie.valid = true;
+ cookie.thread_handle = h;
+ cookie.prev_mask = prev;
+ }
#elif (LINUX)
- cpu_set_t cp;
- CPU_ZERO(&cp);
- CPU_SET(core, &cp);
- (void)pthread_setaffinity_np(t.native_handle(), sizeof(cp), &cp);
+ pthread_t ph = t.native_handle();
+ cpu_set_t prev;
+ if (pthread_getaffinity_np(ph, sizeof(prev), &prev) == 0) {
+ cpu_set_t cp;
+ CPU_ZERO(&cp);
+ CPU_SET(core, &cp);
+ (void)pthread_setaffinity_np(ph, sizeof(cp), &cp);
+ cookie.valid = true;
+ cookie.thread = ph;
+ cookie.prev_mask = prev;
+ }
#else
(void)t; (void)core;
#endif
+ return cookie;
};
- #if (MSVC)
- thread_local u32 aux = 0;
+ auto restore_affinity = [](const affinity_cookie& cookie) {
+ if (!cookie.valid) return;
+ #if (WINDOWS)
+ (void)SetThreadAffinityMask(cookie.thread_handle, cookie.prev_mask);
+ #elif (LINUX)
+ (void)pthread_setaffinity_np(cookie.thread, sizeof(cookie.prev_mask), &cookie.prev_mask);
+ #else
+ (void)cookie;
#endif
+ };
- auto cpuid = [&](unsigned int leaf) noexcept -> u64 {
- #if (MSVC)
+ thread_local u32 aux = 0;
+ auto cpuid = [](unsigned int leaf) noexcept -> u64 {
+ #if (MSVC)
// make regs volatile so writes cannot be optimized out, if this isn't added and the code is compiled in release mode, cycles would be around 40 even under Hyper-V
volatile int regs[4]{};
@@ -4704,7 +4733,7 @@ struct VM {
volatile unsigned int a, b, c, d;
- // this differs from the code above because a, b, c and d are effectively "used"
+ // this differs from the code above because a, b, c and d are effectively used
// the compiler must honor the write to a volatile variable
asm volatile("cpuid"
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
@@ -4723,16 +4752,21 @@ struct VM {
#endif
};
- // calculate_latency (kept as provided, minimal adaptations)
auto calculate_latency = [&](const std::vector& samples_in) -> u64 {
if (samples_in.empty()) return 0;
const size_t N = samples_in.size();
if (N == 1) return samples_in[0];
+
+ // local sorted copy
std::vector s = samples_in;
- std::sort(s.begin(), s.end());
+ std::sort(s.begin(), s.end()); // ascending
+
+ // tiny-sample short-circuits
if (N <= 4) return s.front();
+ // median (and works for sorted input)
auto median_of_sorted = [](const std::vector& v, size_t lo, size_t hi) -> u64 {
+ // this is the median of v[lo..hi-1], requires 0 <= lo < hi
const size_t len = hi - lo;
if (len == 0) return 0;
const size_t mid = lo + (len / 2);
@@ -4740,6 +4774,7 @@ struct VM {
return (v[mid - 1] + v[mid]) / 2;
};
+ // the robust center: median M and MAD -> approximate sigma
const u64 M = median_of_sorted(s, 0, s.size());
std::vector absdev;
absdev.reserve(N);
@@ -4749,73 +4784,100 @@ struct VM {
}
std::sort(absdev.begin(), absdev.end());
const u64 MAD = median_of_sorted(absdev, 0, absdev.size());
- const long double kmad_to_sigma = 1.4826L;
+ // convert MAD to an approximate standard-deviation-like measure
+ const long double kmad_to_sigma = 1.4826L; // consistent for normal approx
const long double sigma = (MAD == 0) ? 1.0L : (static_cast(MAD) * kmad_to_sigma);
+ // find the densest small-valued cluster by sliding a fixed-count window
+ // this locates the most concentrated group of samples (likely it would be the true VMEXIT cluster)
+ // const size_t frac_win = (N * 8 + 99) / 100; // ceil(N * 0.08)
+ // const size_t win = std::min(N, std::max(MIN_WIN, frac_win));
const size_t MIN_WIN = 10;
- const size_t frac_win = static_cast(std::ceil(static_cast(N) * 0.08));
- size_t inner_win = frac_win;
- if (inner_win < MIN_WIN) inner_win = MIN_WIN;
- const size_t win = (N < inner_win) ? N : inner_win;
+ const size_t win = std::min(
+ N,
+ std::max(
+ MIN_WIN,
+ static_cast(std::ceil(static_cast(N) * 0.08))
+ )
+ );
size_t best_i = 0;
- u64 best_span = (s.back() - s.front()) + 1;
+ u64 best_span = (s.back() - s.front()) + 1; // large initial
for (size_t i = 0; i + win <= N; ++i) {
const u64 span = s[i + win - 1] - s[i];
- if (span < best_span) { best_span = span; best_i = i; }
+ if (span < best_span) {
+ best_span = span;
+ best_i = i;
+ }
}
+ // expand the initial window greedily while staying "tight"
+ // allow expansion while adding samples does not more than multiply the span by EXPAND_FACTOR
constexpr long double EXPAND_FACTOR = 1.5L;
size_t cluster_lo = best_i;
- size_t cluster_hi = best_i + win;
+ size_t cluster_hi = best_i + win; // exclusive
+ // expand left
while (cluster_lo > 0) {
const u64 new_span = s[cluster_hi - 1] - s[cluster_lo - 1];
if (static_cast(new_span) <= EXPAND_FACTOR * static_cast(best_span) ||
(s[cluster_hi - 1] <= (s[cluster_lo - 1] + static_cast(std::ceil(3.0L * sigma))))) {
--cluster_lo;
- if (new_span < best_span) best_span = new_span;
+ best_span = std::min(best_span, new_span);
}
else break;
}
+ // expand right
while (cluster_hi < N) {
const u64 new_span = s[cluster_hi] - s[cluster_lo];
if (static_cast(new_span) <= EXPAND_FACTOR * static_cast(best_span) ||
(s[cluster_hi] <= (s[cluster_lo] + static_cast(std::ceil(3.0L * sigma))))) {
++cluster_hi;
- if (new_span < best_span) best_span = new_span;
+ best_span = std::min(best_span, new_span);
}
else break;
}
const size_t cluster_size = (cluster_hi > cluster_lo) ? (cluster_hi - cluster_lo) : 0;
+
+ // cluster must be reasonably dense and cover a non-negligible portion of samples, so this is pure sanity checks
const double fraction_in_cluster = static_cast(cluster_size) / static_cast(N);
- size_t threshold = N / 50;
- if (threshold < 5) threshold = 5;
- const size_t MIN_CLUSTER = (threshold < N) ? threshold : N;
+ const size_t MIN_CLUSTER = std::min(static_cast(std::max(5, static_cast(N / 50))), N); // at least 2% or 5 elements
if (cluster_size < MIN_CLUSTER || fraction_in_cluster < 0.02) {
- size_t fallback_count = static_cast(std::floor(static_cast(N) * 0.10));
- if (fallback_count < 1) fallback_count = 1;
+ // low-percentile (10th) trimmed median
+ const size_t fallback_count = std::max(1, static_cast(std::floor(static_cast(N) * 0.10)));
+ // median of lowest fallback_count elements (if fallback_count==1 that's smallest)
if (fallback_count == 1) return s.front();
const size_t mid = fallback_count / 2;
if (fallback_count & 1) return s[mid];
return (s[mid - 1] + s[mid]) / 2;
}
+ // now we try to get a robust estimate inside the cluster, trimmed mean (10% trim) centered on cluster
const size_t trim_count = static_cast(std::floor(static_cast(cluster_size) * 0.10));
- size_t lo = cluster_lo + trim_count;
- size_t hi = cluster_hi - trim_count;
+ const size_t lo = cluster_lo + trim_count;
+ const size_t hi = cluster_hi - trim_count; // exclusive
if (hi <= lo) {
+ // degenerate -> median of cluster
return median_of_sorted(s, cluster_lo, cluster_hi);
}
+ // sum with long double to avoid overflow and better rounding
long double sum = 0.0L;
for (size_t i = lo; i < hi; ++i) sum += static_cast(s[i]);
const long double avg = sum / static_cast(hi - lo);
u64 result = static_cast(std::llround(avg));
+
+ // final sanity adjustments:
+ // if the computed result is suspiciously far from the global median (e.g., > +6*sigma)
+ // clamp toward the median to avoid choosing a high noisy cluster by mistake
const long double diff_from_med = static_cast(result) - static_cast(M);
if (diff_from_med > 0 && diff_from_med > (6.0L * sigma)) {
+ // clamp to median + 4*sigma (conservative)
result = static_cast(std::llround(static_cast(M) + 4.0L * sigma));
}
+
+ // also, if result is zero (shouldn't be) or extremely small, return a smallest observed sample
if (result == 0) result = s.front();
+
return result;
};
@@ -4825,12 +4887,29 @@ struct VM {
VMAWARE_UNUSED(tmp);
}
+ // if hypervisor downscales TSC globally, this will catch it
+ const u64 calib_start = rdtsc();
+ {
+ volatile u64 x = 0xDEADBEEFCAFEBABEULL;
+ for (u64 i = 0; i < ITER_XOR; ++i) {
+ x ^= i;
+ x = (x << 1) ^ (x >> 3);
+ }
+ VMAWARE_UNUSED(x);
+ }
+ const u64 calib_end = rdtsc();
+ const u64 calib_delta = (calib_end > calib_start) ? (calib_end - calib_start) : 0;
+
+ ready_count.store(0, std::memory_order_release);
+ state.store(0, std::memory_order_release);
+
// Thread 1: start near same cycle, do XOR work, set end
std::thread th1([&]() {
ready_count.fetch_add(1, std::memory_order_acq_rel);
- while (ready_count.load(std::memory_order_acquire) < 2) { /* spin */ }
+ while (ready_count.load(std::memory_order_acquire) < 2)
+ _mm_pause();
- u64 s = rdtsc();
+ const u64 s = rdtsc();
t1_start.store(s, std::memory_order_release);
state.store(1, std::memory_order_release);
@@ -4841,15 +4920,16 @@ struct VM {
}
VMAWARE_UNUSED(x);
- u64 e = rdtsc();
+ const u64 e = rdtsc();
t1_end.store(e, std::memory_order_release);
state.store(2, std::memory_order_release);
});
- // Thread 2: barrier, sample start, perform cpuid sampling and keep accumulating rdtsc deltas
+ // Thread 2: rdtsc and cpuid spammer, forces hypervisor to downscale TSC if patch is present; if interception disabled, caught by cpuid latency
std::thread th2([&]() {
ready_count.fetch_add(1, std::memory_order_acq_rel);
- while (ready_count.load(std::memory_order_acquire) < 2) { /* spin */ }
+ while (ready_count.load(std::memory_order_acquire) < 2)
+ _mm_pause();
u64 last = rdtsc();
t2_start.store(last, std::memory_order_release);
@@ -4858,55 +4938,56 @@ struct VM {
u64 acc = 0;
size_t idx = 0;
- // per-leaf sampling but do not stop entirely if thread1 is still running after completing planned samples
- for (size_t li = 0; li < n_leaves; ++li) {
- const unsigned int leaf = leaves[li];
- for (unsigned i = 0; i < CPUID_ITER; ++i) {
- // accumulate rdtsc delta up to now (this includes time since last sample and includes previous cpuid)
- u64 now = rdtsc();
- acc += (now >= last) ? (now - last) : (u64)((u64)0 - last + now);
- last = now;
-
- // run cpuid and store latency
- if (idx < samples.size()) samples[idx] = cpuid(leaf);
- ++idx;
-
- // if thread1 finished, capture a final rdtsc and exit sampling loops
- if (state.load(std::memory_order_acquire) == 2) {
- u64 final_now = rdtsc();
- acc += (final_now >= last) ? (final_now - last) : (u64)((u64)0 - last + final_now);
- last = final_now;
- t2_end.store(final_now, std::memory_order_release);
- t2_accum.store(acc, std::memory_order_release);
- return;
+ // for each leaf do CPUID_ITER samples, then repeat
+ while (state.load(std::memory_order_acquire) != 2) {
+ for (size_t li = 0; li < n_leaves; ++li) {
+ const unsigned int leaf = leaves[li];
+
+ for (unsigned i = 0; i < CPUID_ITER; ++i) {
+ // read rdtsc and accumulate delta
+ const u64 now = rdtsc();
+ acc += (now >= last) ? (now - last) : (u64)((u64)0 - last + now);
+ last = now;
+
+ // store latency if buffer has space
+ if (idx < samples.size()) samples[idx] = cpuid(leaf);
+ ++idx;
+
+ // if thread1 finished
+ if (state.load(std::memory_order_acquire) == 2) break;
}
- }
- }
- // If we reach here, we completed planned samples but thread1 might still be running, so continue spamming
- while (state.load(std::memory_order_acquire) != 2) {
- u64 now = rdtsc();
- acc += (now >= last) ? (now - last) : (u64)((u64)0 - last + now);
- last = now;
+ if (state.load(std::memory_order_acquire) == 2) break;
+ }
}
- // final sample after seeing finished
- u64 final_now = rdtsc();
+ // final rdtsc after detecting finish
+ const u64 final_now = rdtsc();
acc += (final_now >= last) ? (final_now - last) : (u64)((u64)0 - last + final_now);
last = final_now;
+
+ // publish results
t2_end.store(final_now, std::memory_order_release);
t2_accum.store(acc, std::memory_order_release);
});
- // Try to pin to different cores
+ // try to pin to different cores
+ affinity_cookie cookie1{};
+ affinity_cookie cookie2{};
if (hw >= 2) {
- try_set_affinity(th1, 0);
- try_set_affinity(th2, 1);
+ if (hw >= 2) {
+ cookie1 = set_affinity(th1, 0);
+ cookie2 = set_affinity(th2, 1);
+ }
}
th1.join();
th2.join();
+ restore_affinity(cookie1);
+ restore_affinity(cookie2);
+
+ // collect results
const u64 a = t1_start.load(std::memory_order_acquire);
const u64 b = t1_end.load(std::memory_order_acquire);
#ifdef __VMAWARE_DEBUG__
@@ -4919,14 +5000,14 @@ struct VM {
const u64 t2_delta = acc;
std::vector used;
- used.reserve(samples_expected);
- for (size_t i = 0; i < samples.size(); ++i)
- if (samples[i] != 0)
+ for (size_t i = 0; i < samples.size(); ++i)
+ if (samples[i] != 0)
used.push_back(samples[i]);
const u64 cpuid_latency = calculate_latency(used);
- debug("TIMER: thread1 cycles: start=", a, " end=", b, " delta=", t1_delta);
- debug("TIMER: thread2 cycles: start=", c, " end=", d, " acc=", t2_delta);
+ debug("TIMER: calibration cycles: start=", calib_start, " delta=", calib_delta);
+ debug("TIMER: thread1 cycles: start=", a, " delta=", t1_delta);
+ debug("TIMER: thread2 cycles: start=", c, " delta=", t2_delta);
debug("TIMER: vmexit latency: ", cpuid_latency);
if (cpuid_latency >= cycle_threshold) {
@@ -4938,16 +5019,20 @@ struct VM {
return true;
}
- if (t1_delta == 0) {
- return false;
+ if (t1_delta == 0 || calib_delta == 0) {
+ return true;
}
const double ratio = double(t2_delta) / double(t1_delta);
+ const double calibration_ratio = static_cast(t1_delta) / static_cast(calib_delta);
+
+ // if thread 1 was faster than thread 2, hypervisor downscaled TSC per-vCPU in either cpuid or rdtsc
if (ratio < 0.95 || ratio > 1.05) {
- debug("TIMER: VMAware detected an hypervisor offsetting TSC: ", ratio);
+ return true;
}
- else {
- debug("TIMER: Ratio: ", ratio);
+ // if calibration was much faster than thread 1, hypervisor downscaled TSC globally while thread 2 was spamming
+ if (calibration_ratio < 0.95) {
+ return true;
}
#if (WINDOWS)
@@ -4964,8 +5049,7 @@ struct VM {
ProcessorInformation = 11
};
- HMODULE hPowr = GetModuleHandleA("powrprof.dll");
- if (!hPowr) hPowr = LoadLibraryA("powrprof.dll");
+ const HMODULE hPowr = GetModuleHandleA("powrprof.dll");
if (!hPowr) return 0;
const char* names[] = { "CallNtPowerInformation" };