From ad2c73b791d6c1971635a5361395ea6bb6455ca0 Mon Sep 17 00:00:00 2001 From: Requiem Date: Sat, 8 Nov 2025 10:14:33 +0100 Subject: [PATCH 1/2] chore: made VM::HYPERVISOR_QUERY x64 only --- src/cli.cpp | 2 +- src/vmaware.hpp | 43 +++++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/cli.cpp b/src/cli.cpp index 3a56611e..e5eb845c 100755 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -809,7 +809,7 @@ static void general() { checker(VM::POWER_CAPABILITIES, "power capabilities"); checker(VM::QEMU_FW_CFG, "QEMU fw_cfg device"); checker(VM::VIRTUAL_PROCESSORS, "virtual processors"); - checker(VM::HYPERV_QUERY, "hypervisor query"); + checker(VM::HYPERVISOR_QUERY, "hypervisor query"); checker(VM::AMD_SEV, "AMD-SEV MSR"); checker(VM::VIRTUAL_REGISTRY, "registry emulation"); checker(VM::FIRMWARE, "firmware"); diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 34fbcd0e..34b34a6c 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -545,7 +545,7 @@ struct VM { DRIVERS, DEVICE_HANDLES, VIRTUAL_PROCESSORS, - HYPERV_QUERY, + HYPERVISOR_QUERY, AUDIO, DISPLAY, DLL, @@ -4175,7 +4175,7 @@ struct VM { debug("TIMER: Running inside a binary translation layer"); return false; } - u16 cycleThreshold = 1500; + u16 cycleThreshold = 1200; if (util::hyper_x() == HYPERV_ARTIFACT_VM) { cycleThreshold = 15000; // if we're running under Hyper-V, attempt to detect nested virtualization only } @@ -4215,18 +4215,18 @@ struct VM { const u64 t1 = __rdtsc(); u32 a, b, c, d; - cpu::cpuid(a, b, c, d, 0); + cpu::cpuid(a, b, c, d, 0); // sometimes not intercepted in compat mode under some hvs const u64 t2 = __rdtscp(&aux); return t2 - t1; }; - constexpr int N = 100; + constexpr u8 N = 100; auto sample_avg = [&]() -> u64 { u64 sum = 0; - for (int i = 0; i < N; ++i) { + for (u8 i = 0; i < N; ++i) { sum += cpuid(); } return (sum + N / 2) / N; @@ -7956,10 +7956,13 @@ struct VM { /** * @brief Check if a call to NtQuerySystemInformation with the 0x9f leaf fills a _SYSTEM_HYPERVISOR_DETAIL_INFORMATION structure - * @category Windows - * @implements VM::HYPERV_QUERY + * @category Windows, x86_64 + * @implements VM::HYPERVISOR_QUERY */ - [[nodiscard]] static bool hyperv_query() { + [[nodiscard]] static bool hypervisor_query() { + #if (x86_32) + return false; + #else if (util::hyper_x() == HYPERV_ARTIFACT_VM) { return false; } @@ -8006,7 +8009,7 @@ struct VM { return true; } } - + #endif return false; } @@ -8474,7 +8477,7 @@ struct VM { pNtFlushInstructionCache(hCurrentProcess, execMem, trampSize); - int hitCount = 0; + u8 hitCount = 0; CONTEXT origCtx{}; origCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS; @@ -8499,7 +8502,7 @@ struct VM { return false; } - auto vetExceptions = [&](u32 code, EXCEPTION_POINTERS* info) -> int { + auto vetExceptions = [&](u32 code, EXCEPTION_POINTERS* info) -> u8 { // if not single-step, hypervisor likely swatted our trap if (code != static_cast(0x80000004L)) { hypervisorCaught = true; @@ -9269,9 +9272,9 @@ struct VM { const u16 word = static_cast((edid[8] << 8) | edid[9]); char m[4] = { 0, 0, 0, 0 }; - const int c1 = (word >> 10) & 0x1F; - const int c2 = (word >> 5) & 0x1F; - const int c3 = (word >> 0) & 0x1F; + const u8 c1 = static_cast((word >> 10) & 0x1F); + const u8 c2 = static_cast((word >> 5) & 0x1F); + const u8 c3 = static_cast((word >> 0) & 0x1F); if (c1 >= 1 && c1 <= 26) m[0] = static_cast('A' + c1 - 1); else m[0] = '?'; if (c2 >= 1 && c2 <= 26) m[1] = static_cast('A' + c2 - 1); else m[1] = '?'; @@ -9546,7 +9549,7 @@ struct VM { const std::uintptr_t paddr = reinterpret_cast(amd_target_mem); // to avoid sign-extension, 32-bit compatible const u64 addr = static_cast(paddr); - for (int i = 0; i < 8; ++i) { + for (u8 i = 0; i < 8; ++i) { amd_bytes[2 + i] = static_cast((addr >> (i * 8)) & 0xFF); } bytes = amd_bytes; @@ -9571,8 +9574,8 @@ struct VM { pNtFlushInstructionCache(hCurrentProcess, exec_mem, codeSize); using CodeFunc = void(*)(); - using RunnerFn = int(*)(CodeFunc); - RunnerFn runner = +[](CodeFunc func) -> int { + using RunnerFn = u8(*)(CodeFunc); + RunnerFn runner = +[](CodeFunc func) -> u8 { __try { func(); return 0; @@ -9582,7 +9585,7 @@ struct VM { } }; - const int runner_rc = runner(reinterpret_cast(exec_mem)); + const u8 runner_rc = runner(reinterpret_cast(exec_mem)); // check if the target buffer was written to zero by CLZERO bool memory_all_zero = false; @@ -10793,7 +10796,7 @@ struct VM { case DEVICE_HANDLES: return "DEVICE_HANDLES"; case QEMU_FW_CFG: return "QEMU_FW_CFG"; case VIRTUAL_PROCESSORS: return "VIRTUAL_PROCESSORS"; - case HYPERV_QUERY: return "HYPERV_QUERY"; + case HYPERVISOR_QUERY: return "HYPERVISOR_QUERY"; case AMD_SEV: return "AMD_SEV"; case VIRTUAL_REGISTRY: return "VIRTUAL_REGISTRY"; case FIRMWARE: return "FIRMWARE"; @@ -11365,7 +11368,7 @@ std::pair VM::core::technique_list[] = { std::make_pair(VM::DEVICE_HANDLES, VM::core::technique(100, VM::device_handles)), std::make_pair(VM::VIRTUAL_PROCESSORS, VM::core::technique(100, VM::virtual_processors)), std::make_pair(VM::OBJECTS, VM::core::technique(100, VM::objects)), - std::make_pair(VM::HYPERV_QUERY, VM::core::technique(100, VM::hyperv_query)), + std::make_pair(VM::HYPERVISOR_QUERY, VM::core::technique(100, VM::hypervisor_query)), std::make_pair(VM::AUDIO, VM::core::technique(25, VM::audio)), std::make_pair(VM::DISPLAY, VM::core::technique(35, VM::display)), std::make_pair(VM::WINE, VM::core::technique(100, VM::wine)), From 5828385861e44f01f4187552e94d5c7be5d246c4 Mon Sep 17 00:00:00 2001 From: Requiem Date: Sun, 9 Nov 2025 04:47:29 +0100 Subject: [PATCH 2/2] feat: added VM::LBR --- docs/documentation.md | 181 +++++++++++++++++++------------------- src/cli.cpp | 1 + src/vmaware.hpp | 198 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 282 insertions(+), 98 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index ccbd5a8d..dee52bf4 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,96 +499,97 @@ 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#L1967) | -| `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#L1985) | -| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2059) | -| `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#L2090) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 55% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4169) | -| `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#L6319) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4478) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5328) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4359) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4383) | -| `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#L4408) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4426) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4441) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 65% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4584) | -| `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#L4625) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6615) | -| `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#L6343) | -| `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#L6646) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6685) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5339) | -| `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#L4635) | -| `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#L6745) | -| `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#L2118) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6376) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6409) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6506) | -| `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#L6563) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6843) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5366) | -| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6894) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6968) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7023) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4664) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5174) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4973) | -| `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#L4992) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7050) | -| `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#L7075) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7136) | -| `VM::INTEL_THREAD_MISMATCH` | Check for Intel CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2206) | -| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3256) | -| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3470) | -| `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#L7215) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7271) | -| `VM::HYPERV_HOSTNAME` | 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#L5470) | -| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | 🐧🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5491) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7327) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7362) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4680) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4116) | -| `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#L4145) | -| `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#L4761) | -| `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#L4790) | -| `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#L4818) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4866) | -| `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#L4896) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4950) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5026) | -| `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#L5048) | -| `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#L5129) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5159) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5191) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7379) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7477) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7696) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7795) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7833) | -| `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#L5219) | -| `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#L7929) | -| `VM::HYPERV_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#L7959) | -| `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#L4703) | -| `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#L8017) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5525) | -| `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#L5249) | -| `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#L8102) | -| `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#L5276) | -| `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#L5966) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8200) | -| `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#L8394) | -| `VM::UD` | Check if after executing an undefined instruction, a hypervisor misinterpret it as a system call | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8547) | -| `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#L8618) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8665) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8783) | -| `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#L6590) | -| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8876) | -| `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#L9045) | -| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9254) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9265) | -| `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#L9374) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9641) | +| `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#L1969) | +| `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#L1987) | +| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2061) | +| `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#L2092) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 55% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4171) | +| `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#L6321) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4480) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5330) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4361) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4385) | +| `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#L4410) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4428) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4443) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 65% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4586) | +| `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#L4627) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6617) | +| `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#L6345) | +| `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#L6648) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6687) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5341) | +| `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#L4637) | +| `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#L6747) | +| `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#L2120) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6378) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6411) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6508) | +| `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#L6565) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6845) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5368) | +| `VM::SGDT` | Check for sgdt instruction method | 🪟 | 50% | | | code documentation paper in /papers/www.offensivecomputing.net_vm.pdf (top-most byte signature) | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6896) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6970) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7025) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4666) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5176) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4975) | +| `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#L4994) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7052) | +| `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#L7077) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7138) | +| `VM::INTEL_THREAD_MISMATCH` | Check for Intel CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L2208) | +| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3258) | +| `VM::AMD_THREAD_MISMATCH` | Check for AMD CPU thread count database if it matches the system's thread count | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3472) | +| `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#L7217) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7273) | +| `VM::HYPERV_HOSTNAME` | 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#L5472) | +| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | 🐧🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5493) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7329) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7364) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4682) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4118) | +| `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#L4147) | +| `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#L4763) | +| `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#L4792) | +| `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#L4820) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4868) | +| `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#L4898) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4952) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5028) | +| `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#L5050) | +| `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#L5131) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5161) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5193) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7381) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7479) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7698) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7797) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7835) | +| `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#L5221) | +| `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#L7931) | +| `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#L7961) | +| `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#L4705) | +| `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#L8022) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5527) | +| `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#L5251) | +| `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#L8107) | +| `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#L5278) | +| `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#L5968) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8205) | +| `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#L8399) | +| `VM::UD` | Check if after executing an undefined instruction, a hypervisor misinterpret it as a system call | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8552) | +| `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#L8623) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8670) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8788) | +| `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#L6592) | +| `VM::OBJECTS` | Check for any signs of VMs in Windows kernel object entities | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8880) | +| `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#L9049) | +| `VM::SMBIOS_INTEGRITY` | Check if SMBIOS is malformed/corrupted in a way that is typical for VMs | 🪟 | 60% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9258) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9269) | +| `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#L9378) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9643) | +| `VM::LBR` | Check if Last Branch Record MSRs are correctly virtualized | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9735) | diff --git a/src/cli.cpp b/src/cli.cpp index e5eb845c..3ed3106f 100755 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -829,6 +829,7 @@ static void general() { checker(VM::EDID, "EDID"); checker(VM::CPU_HEURISTIC, "CPU heuristics"); checker(VM::CLOCK, "system timers"); + checker(VM::LBR, "LBR"); // ADD NEW TECHNIQUE CHECKER HERE const auto t2 = std::chrono::high_resolution_clock::now(); diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 34b34a6c..98dcec68 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -52,14 +52,14 @@ * * * ============================== SECTIONS ================================== - * - enums for publicly accessible techniques => line 532 - * - struct for internal cpu operations => line 714 - * - struct for internal memoization => line 1146 - * - struct for internal utility functions => line 1276 - * - struct for internal core components => line 9739 - * - start of VM detection technique list => line 1962 - * - start of public VM detection functions => line 10232 - * - start of externally defined variables => line 11210 + * - enums for publicly accessible techniques => line 534 + * - struct for internal cpu operations => line 717 + * - struct for internal memoization => line 1149 + * - struct for internal utility functions => line 1279 + * - struct for internal core components => line 9920 + * - start of VM detection technique list => line 1964 + * - start of public VM detection functions => line 10413 + * - start of externally defined variables => line 11394 * * * ============================== EXAMPLE =================================== @@ -570,6 +570,7 @@ struct VM { EDID, CPU_HEURISTIC, CLOCK, + LBR, // Linux and Windows SIDT, @@ -9727,6 +9728,185 @@ struct VM { SetupDiDestroyDeviceInfoList(devs); return !found; } + + /** + * @brief Check if Last Branch Record MSRs are correctly virtualized + * @category Windows + * @implements VM::LBR + */ + [[nodiscard]] static bool lbr() { + #if (x86) + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { + "NtAllocateVirtualMemory", + "NtFreeVirtualMemory", + "NtFlushInstructionCache", + "RtlAddVectoredExceptionHandler", + "RtlRemoveVectoredExceptionHandler", + "NtCreateThreadEx", + "NtGetContextThread", + "NtSetContextThread", + "NtResumeThread", + "NtWaitForSingleObject", + "NtClose" + }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + using NtAllocateVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG); + using NtFreeVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG); + using NtFlushInstructionCache_t = NTSTATUS(__stdcall*)(HANDLE, PVOID, SIZE_T); + using RtlAddVectoredExceptionHandler_t = PVOID(__stdcall*)(ULONG, PVECTORED_EXCEPTION_HANDLER); + using RtlRemoveVectoredExceptionHandler_t = ULONG(__stdcall*)(PVOID); + using NtCreateThreadEx_t = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, PVOID, HANDLE, PVOID, PVOID, BOOLEAN, ULONG_PTR, SIZE_T, SIZE_T, PVOID); + using NtGetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); + using NtSetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); + using NtResumeThread_t = NTSTATUS(__stdcall*)(HANDLE, PULONG); + using NtWaitForSingleObject_t = NTSTATUS(__stdcall*)(HANDLE, BOOLEAN, PLARGE_INTEGER); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE); + + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[1]); + const auto pNtFlushInstructionCache = reinterpret_cast(funcs[2]); + const auto pRtlAddVectoredExceptionHandler = reinterpret_cast(funcs[3]); + const auto pRtlRemoveVectoredExceptionHandler = reinterpret_cast(funcs[4]); + const auto pNtCreateThreadEx = reinterpret_cast(funcs[5]); + const auto pNtGetContextThread = reinterpret_cast(funcs[6]); + const auto pNtSetContextThread = reinterpret_cast(funcs[7]); + const auto pNtResumeThread = reinterpret_cast(funcs[8]); + const auto pNtWaitForSingleObject = reinterpret_cast(funcs[9]); + const auto pNtClose = reinterpret_cast(funcs[10]); + + if (!pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtFlushInstructionCache || + !pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || + !pNtCreateThreadEx || !pNtGetContextThread || !pNtSetContextThread || + !pNtResumeThread || !pNtWaitForSingleObject || !pNtClose) { + return false; + } + + // ICEBP because the kernel interrupt handler that inserts the LastBranchFromIp into EXCEPTION_RECORD->ExceptionInformation[0] is the INT 01 handler + constexpr unsigned char codeBytes[] = { 0xE8,0x00,0x00,0x00,0x00, 0xF1, 0xC3 }; // CALL next ; ICEBP ; RET + const SIZE_T codeSize = sizeof(codeBytes); + const HANDLE hCurrentProcess = reinterpret_cast(-1LL); + + PVOID controlBase = nullptr; + SIZE_T controlSize = sizeof(PVOID); + NTSTATUS st = pNtAllocateVirtualMemory(hCurrentProcess, &controlBase, 0, &controlSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (st != 0 || !controlBase) return false; + *reinterpret_cast(controlBase) = nullptr; + + PVOID execBase = nullptr; + SIZE_T allocSize = codeSize; + st = pNtAllocateVirtualMemory(hCurrentProcess, &execBase, 0, &allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (st != 0 || !execBase) { + SIZE_T tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + unsigned char* dst = reinterpret_cast(execBase); + for (SIZE_T i = 0; i < codeSize; ++i) dst[i] = codeBytes[i]; + pNtFlushInstructionCache(hCurrentProcess, execBase, codeSize); + + // local static pointer to control slot so lambda can access a stable address + static PVOID g_control_slot = nullptr; + g_control_slot = controlBase; + + auto veh_lambda = [](PEXCEPTION_POINTERS ep) -> LONG { + if (!ep || !ep->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; + if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; + + ULONG_PTR info0 = 0; + if (ep->ExceptionRecord->NumberParameters > 0) info0 = ep->ExceptionRecord->ExceptionInformation[0]; + if (info0 && g_control_slot) { + PVOID expected = nullptr; + _InterlockedCompareExchangePointer(reinterpret_cast(g_control_slot), reinterpret_cast(info0), expected); + } + return EXCEPTION_CONTINUE_EXECUTION; + }; + + // Register VEH + const PVECTORED_EXCEPTION_HANDLER veh_fn = static_cast(veh_lambda); + const PVOID vehHandle = pRtlAddVectoredExceptionHandler(1, veh_fn); + if (!vehHandle) { + SIZE_T tmp = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); + tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + + // create suspended thread + HANDLE hThread = nullptr; + NTSTATUS ntres = pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, nullptr, hCurrentProcess, execBase, nullptr, TRUE, 0, 0, 0, nullptr); + if (ntres != 0 || !hThread) { + pRtlRemoveVectoredExceptionHandler(vehHandle); + SIZE_T tmp = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); + tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + + // set debug bits + TF on suspended thread + CONTEXT ctx; + ZeroMemory(&ctx, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS; + ntres = pNtGetContextThread(hThread, &ctx); + if (ntres != 0) { + pNtClose(hThread); + pRtlRemoveVectoredExceptionHandler(vehHandle); + SIZE_T tmp = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); + tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + ctx.Dr7 |= (1ull << 8) | (1ull << 9); // LBR only would be enough + ctx.EFlags |= 0x100; + ntres = pNtSetContextThread(hThread, &ctx); + if (ntres != 0) { + pNtClose(hThread); + pRtlRemoveVectoredExceptionHandler(vehHandle); + SIZE_T tmp = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); + tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + + // resume and wait + ULONG suspendCount = 0; + ntres = pNtResumeThread(hThread, &suspendCount); + if (ntres != 0) { + pNtClose(hThread); + pRtlRemoveVectoredExceptionHandler(vehHandle); + SIZE_T tmp = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmp, MEM_RELEASE); + tmp = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmp, MEM_RELEASE); + return false; + } + ntres = pNtWaitForSingleObject(hThread, FALSE, nullptr); + + // read slot (pointer-sized) so if null then no LBR observed + const PVOID slot_val = *reinterpret_cast(controlBase); + + // cleanup + pRtlRemoveVectoredExceptionHandler(vehHandle); + pNtClose(hThread); + SIZE_T tmpSize = allocSize; + pNtFreeVirtualMemory(hCurrentProcess, &execBase, &tmpSize, MEM_RELEASE); + tmpSize = controlSize; + pNtFreeVirtualMemory(hCurrentProcess, &controlBase, &tmpSize, MEM_RELEASE); + g_control_slot = nullptr; + + return (slot_val == nullptr); + #else + return false; + #endif + } // ADD NEW TECHNIQUE FUNCTION HERE #endif @@ -10817,6 +10997,7 @@ struct VM { case EDID: return "EDID"; case CPU_HEURISTIC: return "CPU_HEURISTIC"; case CLOCK: return "CLOCK"; + case LBR: return "LBR"; // END OF TECHNIQUE LIST case DEFAULT: return "setting flag, error"; case ALL: return "setting flag, error"; @@ -11355,6 +11536,7 @@ std::pair VM::core::technique_list[] = { std::make_pair(VM::CLOCK, VM::core::technique(100, VM::clock)), std::make_pair(VM::POWER_CAPABILITIES, VM::core::technique(45, VM::power_capabilities)), std::make_pair(VM::CPU_HEURISTIC, VM::core::technique(90, VM::cpu_heuristic)), + std::make_pair(VM::LBR, VM::core::technique(100, VM::lbr)), std::make_pair(VM::EDID, VM::core::technique(100, VM::edid)), std::make_pair(VM::BOOT_LOGO, VM::core::technique(100, VM::boot_logo)), std::make_pair(VM::GPU_CAPABILITIES, VM::core::technique(45, VM::gpu_capabilities)),