diff --git a/docs/documentation.md b/docs/documentation.md index 098e9f4f..e4e6b4e1 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -499,98 +499,98 @@ 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#L2187) | -| `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#L2205) | -| `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#L2279) | -| `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#L2300) | -| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 55% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4379) | -| `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#L6653) | -| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4683) | -| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5533) | -| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4564) | -| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4588) | -| `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#L4613) | -| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4631) | -| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4646) | -| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 65% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4789) | -| `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#L4830) | -| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6949) | -| `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#L6677) | -| `VM::VBOX_DEFAULT` | Check for default RAM and DISK sizes set by VirtualBox | 🐧🪟 | 25% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5569) | -| `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#L6980) | -| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7019) | -| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5544) | -| `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#L4840) | -| `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#L7098) | -| `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#L2328) | -| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6710) | -| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6743) | -| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6840) | -| `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#L6897) | -| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7151) | -| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5693) | -| `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#L7205) | -| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7279) | -| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7335) | -| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4869) | -| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5379) | -| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5178) | -| `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#L5197) | -| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7364) | -| `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#L7387) | -| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7451) | -| `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#L2416) | -| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3466) | -| `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#L3680) | -| `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#L7496) | -| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7514) | -| `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#L5803) | -| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | 🐧🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5824) | -| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7541) | -| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7576) | -| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4885) | -| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4326) | -| `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#L4355) | -| `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#L4966) | -| `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#L4995) | -| `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#L5023) | -| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5071) | -| `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#L5101) | -| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5155) | -| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5231) | -| `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#L5253) | -| `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#L5334) | -| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5364) | -| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5396) | -| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7593) | -| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7699) | -| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7869) | -| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7916) | -| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7954) | -| `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#L5424) | -| `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#L8007) | -| `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#L8037) | -| `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#L4908) | -| `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#L8096) | -| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5856) | -| `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#L5454) | -| `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#L8185) | -| `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#L5481) | -| `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#L6300) | -| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8226) | -| `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#L8420) | -| `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#L8525) | -| `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#L8591) | -| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8638) | -| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8739) | -| `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#L6924) | -| `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#L8832) | -| `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#L9023) | -| `VM::BOOT_MANAGER` | Check for boot managers typically found in VMs | 🪟 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9269) | -| `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#L9469) | -| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9480) | -| `VM::CPU_HEURISTIC` | Check if the CPU is capable of running certain instructions successfully | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9589) | -| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9784) | +| `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#L2192) | +| `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#L2210) | +| `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#L2284) | +| `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#L2305) | +| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 55% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4384) | +| `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#L6660) | +| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4693) | +| `VM::TEMPERATURE` | Check for device's temperature | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5543) | +| `VM::SYSTEMD` | Check result from systemd-detect-virt tool | 🐧 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4574) | +| `VM::CVENDOR` | Check if the chassis vendor is a VM vendor | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4598) | +| `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#L4623) | +| `VM::DOCKERENV` | Check if /.dockerenv or /.dockerinit file is present | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4641) | +| `VM::DMIDECODE` | Check if dmidecode output matches a VM brand | 🐧 | 55% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4656) | +| `VM::DMESG` | Check if dmesg output matches a VM brand | 🐧 | 65% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4799) | +| `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#L4840) | +| `VM::DLL` | Check for VM-specific DLLs | 🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6956) | +| `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#L6684) | +| `VM::VBOX_DEFAULT` | Check for default RAM and DISK sizes set by VirtualBox | 🐧🪟 | 25% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5579) | +| `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#L6987) | +| `VM::POWER_CAPABILITIES` | Check what power states are enabled | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7027) | +| `VM::PROCESSES` | Check for any VM processes that are active | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5554) | +| `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#L4850) | +| `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#L7087) | +| `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#L2333) | +| `VM::MAC_MEMSIZE` | Check if memory is too low for MacOS system | 🍏 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6717) | +| `VM::MAC_IOKIT` | Check MacOS' IO kit registry for VM-specific strings | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6750) | +| `VM::IOREG_GREP` | Check for VM-strings in ioreg commands for MacOS | 🍏 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L6847) | +| `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#L6904) | +| `VM::VPC_INVALID` | Check for official VPC method | 🪟 | 75% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7185) | +| `VM::SIDT` | Check for uncommon IDT virtual addresses | 🐧🪟 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5703) | +| `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#L7239) | +| `VM::SLDT` | Check for sldt instruction method | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7313) | +| `VM::SMSW` | Check for SMSW assembly instruction technique | 🪟 | 50% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7369) | +| `VM::VMWARE_IOMEM` | Check for VMware string in /proc/iomem | 🐧 | 65% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4879) | +| `VM::VMWARE_IOPORTS` | Check for VMware string in /proc/ioports | 🐧 | 70% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5389) | +| `VM::VMWARE_SCSI` | Check for VMware string in /proc/scsi/scsi | 🐧 | 40% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5188) | +| `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#L5207) | +| `VM::VMWARE_STR` | Check str assembly instruction method for VMware | 🪟 | 35% | | 32-bit | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7398) | +| `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#L7421) | +| `VM::MUTEX` | Check for mutex strings of VM brands | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7485) | +| `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#L2421) | +| `VM::XEON_THREAD_MISMATCH` | Same as above, but for Xeon Intel CPUs | 🐧🪟🍏 | 50% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L3471) | +| `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#L3685) | +| `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#L7564) | +| `VM::CUCKOO_PIPE` | Check for Cuckoo specific piping mechanism | 🪟 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7620) | +| `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#L5813) | +| `VM::GENERAL_HOSTNAME` | Check for commonly set hostnames by certain VM brands | 🐧🪟 | 10% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5834) | +| `VM::DISPLAY` | Check for display configurations commonly found in VMs | 🪟 | 35% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7676) | +| `VM::DEVICE_STRING` | Check if bogus device string would be accepted | 🪟 | 25% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7711) | +| `VM::BLUESTACKS_FOLDERS` | Check for the presence of BlueStacks-specific folders | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4895) | +| `VM::CPUID_SIGNATURE` | Check for signatures in leaf 0x40000001 in CPUID | 🐧🪟🍏 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L4331) | +| `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#L4360) | +| `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#L4976) | +| `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#L5005) | +| `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#L5033) | +| `VM::UML_CPU` | Check for the "UML" string in the CPU brand | 🐧 | 80% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5081) | +| `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#L5111) | +| `VM::VBOX_MODULE` | Check for a VBox kernel module | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5165) | +| `VM::SYSINFO_PROC` | Check for potential VM info in /proc/sysinfo | 🐧 | 15% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5241) | +| `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#L5263) | +| `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#L5344) | +| `VM::PODMAN_FILE` | Check for podman file in /run/ | 🐧 | 5% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5374) | +| `VM::WSL_PROC` | Check for WSL or microsoft indications in /proc/ subdirectories | 🐧 | 30% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5406) | +| `VM::DRIVERS` | Check for VM-specific names for drivers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7728) | +| `VM::DISK_SERIAL` | Check for serial numbers of virtual disks | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L7826) | +| `VM::IVSHMEM` | Check for IVSHMEM device presence | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8044) | +| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | 🪟 | 45% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8143) | +| `VM::DEVICE_HANDLES` | Check for vm-specific devices | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8181) | +| `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#L5434) | +| `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#L8277) | +| `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#L8307) | +| `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#L4918) | +| `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#L8365) | +| `VM::FIRMWARE` | Check for VM signatures on all firmware tables | 🐧🪟 | 100% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5866) | +| `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#L5464) | +| `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#L8450) | +| `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#L5491) | +| `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#L6307) | +| `VM::ACPI_SIGNATURE` | Check for VM-specific ACPI device signatures | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8548) | +| `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#L8742) | +| `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#L8895) | +| `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#L8960) | +| `VM::DBVM` | Check if Dark Byte's VM is present | 🪟 | 150% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9007) | +| `VM::BOOT_LOGO` | Check boot logo for known VM images | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9124) | +| `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#L6931) | +| `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#L9217) | +| `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#L9386) | +| `VM::BOOT_MANAGER` | Check for boot managers typically found in VMs | 🪟 | 50% | Admin | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9649) | +| `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#L9867) | +| `VM::EDID` | Check for non-standard EDID configurations | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9878) | +| `VM::CPU_HEURISTIC` | Check if the CPU is capable of running certain instructions successfully | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L9987) | +| `VM::CLOCK` | Check the presence of system timers | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L10222) | diff --git a/src/cli.cpp b/src/cli.cpp index 674e6482..1b6c346b 100755 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -541,13 +541,13 @@ static std::string vm_description(const std::string& vm_brand) { #else NTSTATUS status; - HMODULE hNtdll = GetModuleHandle(_T("ntdll.dll")); - if (!hNtdll) { + HMODULE ntdll = GetModuleHandleA("ntdll.dll"); + if (!ntdll) { return false; } using NtCreateFile_t = NTSTATUS( - NTAPI*)( + __stdcall*)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, @@ -560,17 +560,17 @@ static std::string vm_description(const std::string& vm_brand) { PVOID, ULONG ); - using NtClose_t = NTSTATUS(NTAPI*)(HANDLE); - using RtlInitUnicodeString_t = VOID(NTAPI*)(PUNICODE_STRING, PCWSTR); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE); + using RtlInitUnicodeString_t = VOID(__stdcall*)(PUNICODE_STRING, PCWSTR); #pragma warning(push) #pragma warning(disable:4191) auto pRtlInitUnicodeString = reinterpret_cast( - GetProcAddress(hNtdll, "RtlInitUnicodeString")); + GetProcAddress(ntdll, "RtlInitUnicodeString")); auto pNtCreateFile = reinterpret_cast( - GetProcAddress(hNtdll, "NtCreateFile")); + GetProcAddress(ntdll, "NtCreateFile")); auto pNtClose = reinterpret_cast( - GetProcAddress(hNtdll, "NtClose")); + GetProcAddress(ntdll, "NtClose")); #pragma warning(pop) if (!pRtlInitUnicodeString || !pNtCreateFile || !pNtClose) { diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 6924410f..b406cd4b 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -52,14 +52,14 @@ * * * ============================== SECTIONS ================================== - * - enums for publicly accessible techniques => line 537 - * - struct for internal cpu operations => line 721 - * - struct for internal memoization => line 1096 - * - struct for internal utility functions => line 1226 - * - struct for internal core components => line 9882 - * - start of VM detection technique list => line 2182 - * - start of public VM detection functions => line 10375 - * - start of externally defined variables => line 11357 + * - enums for publicly accessible techniques => line 532 + * - struct for internal cpu operations => line 716 + * - struct for internal memoization => line 1091 + * - struct for internal utility functions => line 1221 + * - struct for internal core components => line 10320 + * - start of VM detection technique list => line 2187 + * - start of public VM detection functions => line 10813 + * - start of externally defined variables => line 11795 * * * ============================== EXAMPLE =================================== @@ -368,21 +368,16 @@ #if (WINDOWS) #include #include - #include #include #include - #include #include #include - #include #include #include #include #pragma comment(lib, "setupapi.lib") - #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "powrprof.lib") - #pragma comment(lib, "tbs.lib") #pragma comment(lib, "Mincore.lib") #elif (LINUX) #if (x86) @@ -1254,7 +1249,6 @@ struct VM { #endif } - #if (LINUX) // fetch file data [[nodiscard]] static std::string read_file(const char* raw_path) { @@ -1595,14 +1589,14 @@ struct VM { return 0; } - auto result = util::sys_result("dmidecode --type 19 | grep 'Size' | grep '[[:digit:]]*'"); + const auto result = util::sys_result("dmidecode --type 19 | grep 'Size' | grep '[[:digit:]]*'"); if (!result) { debug("get_physical_ram_size: ", "invalid system result, returned 0"); return 0; } - bool is_mb = std::regex_search(*result, std::regex("MB")); - bool is_gb = std::regex_search(*result, std::regex("GB")); + const bool is_mb = std::regex_search(*result, std::regex("MB")); + const bool is_gb = std::regex_search(*result, std::regex("GB")); if (!(is_mb || is_gb)) { debug("get_physical_ram_size: ", "unit not found, returned 0"); return 0; @@ -1685,7 +1679,7 @@ struct VM { } // cmdline is argv0\0argv1\0..., so argv0 is bytes up to first NUL - auto it_nul = std::find(buf.begin(), buf.end(), '\0'); + const auto it_nul = std::find(buf.begin(), buf.end(), '\0'); if (it_nul == buf.begin()) { continue; } @@ -1736,9 +1730,11 @@ struct VM { [[nodiscard]] static bool is_running_under_translator() { #if (WINDOWS && _WIN32_WINNT >= _WIN32_WINNT_WIN10) + const HANDLE hProcess = GetCurrentProcess(); USHORT procMachine = 0, nativeMachine = 0; - auto pIsWow64Process2 = &IsWow64Process2; - if (pIsWow64Process2(GetCurrentProcess(), &procMachine, &nativeMachine)) { + const auto pIsWow64Process2 = &IsWow64Process2; + + if (pIsWow64Process2(hProcess, &procMachine, &nativeMachine)) { if (nativeMachine == IMAGE_FILE_MACHINE_ARM64 && (procMachine == IMAGE_FILE_MACHINE_AMD64 || procMachine == IMAGE_FILE_MACHINE_I386)) { debug("Translator detected x64/x86 process on ARM64"); @@ -1748,20 +1744,17 @@ struct VM { // only if we got MACHINE_UNKNOWN on process but native is ARM64 if (nativeMachine == IMAGE_FILE_MACHINE_ARM64) { - const HMODULE hKernel = GetModuleHandle(_T("kernel32.dll")); - if (!hKernel) return false; using PGetProcessInformation = BOOL(__stdcall*)(HANDLE, PROCESS_INFORMATION_CLASS, PVOID, DWORD); - const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); - if (ntdll == NULL) { + const HMODULE ntdll = util::get_ntdll(); + if (ntdll == nullptr) { return false; } - const char* functionNames[] = { "GetProcessInformation" }; - void* functions[1] = { nullptr }; - - util::GetFunctionAddress(ntdll, functionNames, functions, 1); + const char* names[] = { "GetProcessInformation" }; + void* funcs[1] = { nullptr }; + util::get_function_address(ntdll, names, funcs, 1); - PGetProcessInformation pGetProcInfo = reinterpret_cast(functions[0]); + PGetProcessInformation pGetProcInfo = reinterpret_cast(funcs[0]); if (pGetProcInfo) { struct PROCESS_MACHINE_INFORMATION { USHORT ProcessMachine; @@ -1769,7 +1762,7 @@ struct VM { DWORD MachineAttributes; } pmInfo = {}; // ProcessMachineTypeInfo == 9 per MS Q&A - if (pGetProcInfo(GetCurrentProcess(), (PROCESS_INFORMATION_CLASS)9, &pmInfo, sizeof(pmInfo))) { + if (pGetProcInfo(hProcess, (PROCESS_INFORMATION_CLASS)9, &pmInfo, sizeof(pmInfo))) { if (pmInfo.ProcessMachine == IMAGE_FILE_MACHINE_AMD64 || pmInfo.ProcessMachine == IMAGE_FILE_MACHINE_I386) { debug("Translator detected x64/x86 process on ARM64 by fallback"); return true; @@ -1938,19 +1931,18 @@ struct VM { {26100, 11} }; - const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); + const HMODULE ntdll = util::get_ntdll(); if (!ntdll) { return 0; } - typedef NTSTATUS(__stdcall* RtlGetVersionFunc)(PRTL_OSVERSIONINFOW); - + using RtlGetVersionFunc = NTSTATUS(__stdcall*)(PRTL_OSVERSIONINFOW); const char* names[] = { "RtlGetVersion" }; void* functions[1] = { nullptr }; - GetFunctionAddress(ntdll, names, functions, _countof(names)); + get_function_address(ntdll, names, functions, _countof(names)); - auto pRtlGetVersion = reinterpret_cast(functions[0]); + const auto pRtlGetVersion = reinterpret_cast(functions[0]); if (!pRtlGetVersion) { return 0; } @@ -1974,7 +1966,7 @@ struct VM { // retrieves the addresses of specified functions from a loaded module using the export directory, manual implementation of GetProcAddress - static void GetFunctionAddress(const HMODULE hModule, const char* names[], void** functions, size_t count) { + static void get_function_address(const HMODULE hModule, const char* names[], void** functions, size_t count) { using FuncMap = std::unordered_map; static std::unordered_map function_cache; @@ -2074,7 +2066,7 @@ struct VM { const std::string s_name(current_name); // check cache first - auto cache_it = module_cache.find(s_name); + const auto cache_it = module_cache.find(s_name); if (cache_it != module_cache.end()) { functions[i] = cache_it->second; continue; @@ -2083,15 +2075,15 @@ struct VM { // binary search over names (names array is typically sorted) DWORD lo = 0, hi = nameCount; while (lo < hi) { - DWORD mid = lo + (hi - lo) / 2; - DWORD midNameRva = nameRvas[mid]; + const DWORD mid = lo + (hi - lo) / 2; + const DWORD midNameRva = nameRvas[mid]; const char* midName = safe_cstr_from_rva(midNameRva); - if (!midName) { // corrupted string table + if (!midName) { // corrupted string table or something lo = hi; break; } - int cmp = strcmp(current_name, midName); + const int cmp = strcmp(current_name, midName); if (cmp > 0) { lo = mid + 1; } @@ -2103,9 +2095,9 @@ struct VM { if (lo < nameCount) { const char* candidateName = safe_cstr_from_rva(nameRvas[lo]); if (candidateName && strcmp(current_name, candidateName) == 0) { - WORD nameOrdinal = ordinals[lo]; + const WORD nameOrdinal = ordinals[lo]; if (static_cast(nameOrdinal) >= funcCount) continue; - DWORD funcRva = funcRvas[nameOrdinal]; + const DWORD funcRva = funcRvas[nameOrdinal]; if (!valid_range(static_cast(funcRva), 1)) continue; void* addr = reinterpret_cast(base + funcRva); functions[i] = addr; @@ -2116,6 +2108,19 @@ struct VM { } } + + [[nodiscard]] static HMODULE get_ntdll() { + static HMODULE cachedNtdll = nullptr; + if (cachedNtdll != nullptr) { + return cachedNtdll; + } + + const HMODULE h = GetModuleHandleA("ntdll.dll"); + if (h) cachedNtdll = h; + + return h; + } + using NtEnumerateSystemEnvironmentValuesEx_t = NTSTATUS(__stdcall*)(ULONG InformationClass, PVOID Buffer, PULONG BufferLength); struct EnumerateFirmwareResult { @@ -2132,24 +2137,24 @@ struct VM { EnumerateFirmwareResult res; - const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); - if (ntdll == NULL) { + const HMODULE ntdll = util::get_ntdll(); + if (ntdll == nullptr) { return res; } - const char* functionNames[] = { "NtEnumerateSystemEnvironmentValuesEx" }; + const char* names[] = { "NtEnumerateSystemEnvironmentValuesEx" }; void* functions[1] = { nullptr }; - GetFunctionAddress(ntdll, functionNames, functions, 1); + get_function_address(ntdll, names, functions, 1); NtEnumerateSystemEnvironmentValuesEx_t NtEnumerateSystemEnvironmentValuesEx = reinterpret_cast(functions[0]); - if (NtEnumerateSystemEnvironmentValuesEx == NULL) { + if (NtEnumerateSystemEnvironmentValuesEx == nullptr) { return res; } res.hasFunction = true; ULONG bufferLength = 0; - NTSTATUS status = NtEnumerateSystemEnvironmentValuesEx(1, NULL, &bufferLength); + NTSTATUS status = NtEnumerateSystemEnvironmentValuesEx(1, nullptr, &bufferLength); res.bufferLength = bufferLength; if (bufferLength == 0) { @@ -2378,7 +2383,7 @@ struct VM { } return false; - }; + }; auto is_k8 = [](const u32 eax) -> bool { const u32 family = (eax >> 8) & 0xF; @@ -2391,7 +2396,7 @@ struct VM { } return false; - }; + }; if (!(is_k7(eax) || is_k8(eax))) { return false; @@ -3441,7 +3446,7 @@ struct VM { auto isAsciiAlphaNum = [](char c)->bool { const unsigned char uc = static_cast(c); return (uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z'); - }; + }; const bool left_ok = (pos == 0) || !isAsciiAlphaNum(cpu_full_name[pos - 1]); const bool right_ok = (end == cpu_full_name.size()) || !isAsciiAlphaNum(cpu_full_name[end]); @@ -3655,7 +3660,7 @@ struct VM { auto isAsciiAlphaNum = [](char c)->bool { const unsigned char uc = static_cast(c); return (uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z'); - }; + }; const bool left_ok = (pos == 0) || !isAsciiAlphaNum(cpu_full_name[pos - 1]); const bool right_ok = (end == cpu_full_name.size()) || !isAsciiAlphaNum(cpu_full_name[end]); @@ -4300,7 +4305,7 @@ struct VM { auto isAsciiAlphaNum = [](char c)->bool { const unsigned char uc = static_cast(c); return (uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z'); - }; + }; const bool left_ok = (pos == 0) || !isAsciiAlphaNum(cpu_full_name[pos - 1]); const bool right_ok = (end == cpu_full_name.size()) || !isAsciiAlphaNum(cpu_full_name[end]); @@ -5649,7 +5654,7 @@ struct VM { c = static_cast(std::tolower(static_cast(c))); } - auto it = defaults.find(key); + const auto it = defaults.find(key); if (it != defaults.end()) { return (it->second.first == disk) && (static_cast(it->second.second) == ram); } @@ -5862,16 +5867,13 @@ struct VM { */ [[nodiscard]] static bool firmware() { #if (WINDOWS) - #pragma pack(push, 1) typedef struct { char Signature[4]; u32 Length; u8 Revision; // others not needed } ACPI_HEADER; - #pragma pack(pop) - #pragma pack(push,1) typedef struct _FADT { UINT32 Signature; UINT32 Length; @@ -5910,7 +5912,7 @@ struct VM { UINT16 P_Lvl2_Lat; UINT16 P_Lvl3_Lat; } FADT, * PFADT; - #pragma pack(pop) + // "WAET" is also present as a string inside the WAET table, so there's no need to check for its table signature constexpr std::array targets = { { "Parallels Software", "Parallels(R)", @@ -6469,7 +6471,7 @@ struct VM { // and for each instance, open it and call processHardwareID() auto enumInstances = [&](HKEY hDev, RootType rootType) { for (DWORD j = 0;; ++j) { - wchar_t instName[256]; + wchar_t instName[256]{}; DWORD cbInst = _countof(instName); LONG st2 = RegEnumKeyExW( hDev, @@ -6682,7 +6684,7 @@ struct VM { * @implements VM::HWMODEL */ [[nodiscard]] static bool hwmodel() { - auto result = util::sys_result("sysctl -n hw.model"); + const auto result = util::sys_result("sysctl -n hw.model"); std::smatch match; @@ -6991,6 +6993,8 @@ struct VM { __try { BOOL isNativeVhdBoot = 0; + // we dont call NtQuerySystemInformation with SystemPrefetchPathInformation | SystemHandleInformation + // the point is to check if this kernel32.dll function throws an exception IsNativeVhdBoot(&isNativeVhdBoot); UNUSED(isNativeVhdBoot); } @@ -7000,15 +7004,14 @@ struct VM { } #endif - const HMODULE k32 = GetModuleHandle(_T("kernel32.dll")); + const HMODULE k32 = GetModuleHandleA("kernel32.dll"); if (!k32) { return false; } const char* names[] = { "wine_get_unix_file_name" }; void* functions[1] = { nullptr }; - - util::GetFunctionAddress(k32, names, functions, _countof(names)); + util::get_function_address(k32, names, functions, _countof(names)); if (functions[0] != nullptr) { return core::add(brands::WINE); @@ -7024,13 +7027,13 @@ struct VM { * @implements VM::POWER_CAPABILITIES */ [[nodiscard]] static bool power_capabilities() { - const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); + const HMODULE ntdll = util::get_ntdll(); + const char* names[] = { "NtPowerInformation" }; - void* funcs[1] = { nullptr }; - util::GetFunctionAddress(ntdll, names, funcs, _countof(funcs)); + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - if (!funcs[0]) - return false; + if (!funcs[0]) return false; using NtPI_t = NTSTATUS(__stdcall*)(POWER_INFORMATION_LEVEL, PVOID, ULONG, @@ -7038,13 +7041,12 @@ struct VM { const auto NtPowerInformation = reinterpret_cast(funcs[0]); SYSTEM_POWER_CAPABILITIES caps = { 0 }; - NTSTATUS status = NtPowerInformation( + const NTSTATUS status = NtPowerInformation( SystemPowerCapabilities, nullptr, 0, &caps, sizeof(caps) ); - if (status != 0) - return false; + if (status != 0) return false; const bool s0_supported = caps.AoAc; const bool s1_supported = caps.SystemS1; @@ -7068,25 +7070,7 @@ struct VM { return true; } - DWORD aoac_override = 0; - DWORD aoac_size = sizeof(aoac_override); - const LONG reg_status = RegGetValueW( - HKEY_LOCAL_MACHINE, - L"SYSTEM\\CurrentControlSet\\Control\\Power", - L"PlatformAoAcOverride", - RRF_RT_REG_DWORD, - nullptr, - &aoac_override, - &aoac_size - ); - - const bool aoac_override_present = (reg_status == ERROR_SUCCESS); - if (aoac_override_present) { - debug("POWER_CAPABILITIES: PlatformAoAcOverride present (HKLM\\SYSTEM\\CurrentControlSet\\Control\\Power\\PlatformAoAcOverride). Value=%u", - static_cast(aoac_override)); - return false; - } - + // could check for HKLM\\SYSTEM\\CurrentControlSet\\Control\\Power\\PlatformAoAcOverride const bool no_sleep_states = !s0_supported && !s1_supported && !s2_supported && !s3_supported; if (no_sleep_states) { debug("POWER_CAPABILITIES: Detected !(S0||S1||S2||S3) pattern"); // can sometimes false flag baremetal devices @@ -7103,26 +7087,71 @@ struct VM { * @implements VM::GAMARUE */ [[nodiscard]] static bool gamarue() { - HKEY hKey; - char buffer[64] = { 0 }; - DWORD dwSize = sizeof(buffer); - LONG lRes; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "NtOpenKey", "NtQueryValueKey", "RtlInitUnicodeString", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtOpenKey = reinterpret_cast(funcs[0]); + const auto pNtQueryValueKey = reinterpret_cast(funcs[1]); + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[2]); + const auto pNtClose = reinterpret_cast(funcs[3]); + + if (!pNtOpenKey || !pNtQueryValueKey || !pRtlInitUnicodeString || !pNtClose) return false; + + UNICODE_STRING uKeyName; + pRtlInitUnicodeString(&uKeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion"); + + OBJECT_ATTRIBUTES objAttr; + ZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uKeyName; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + HANDLE hKey = nullptr; + constexpr ACCESS_MASK KEY_QUERY_ONLY = 0x0001; // KEY_QUERY_VALUE + NTSTATUS st = pNtOpenKey(&hKey, KEY_QUERY_ONLY, &objAttr); + if (!NT_SUCCESS(st) || !hKey) { + return false; + } - lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, - _T("Software\\Microsoft\\Windows\\CurrentVersion"), - 0, - KEY_QUERY_VALUE, - &hKey); + UNICODE_STRING uValueName; + pRtlInitUnicodeString(&uValueName, L"ProductId"); - if (lRes != 0L) return false; + // Buffer for KEY_VALUE_PARTIAL_INFORMATION + BYTE buffer[128]{}; + ULONG resultLength = 0; + constexpr ULONG KeyValuePartialInformation = 2; - lRes = RegQueryValueEx(hKey, _T("ProductId"), - nullptr, nullptr, - reinterpret_cast(buffer), &dwSize); + st = pNtQueryValueKey(hKey, &uValueName, KeyValuePartialInformation, buffer, sizeof(buffer), &resultLength); - RegCloseKey(hKey); + pNtClose(hKey); - if (lRes != 0L) return false; + if (!NT_SUCCESS(st)) { + return false; + } + + struct KEY_VALUE_PARTIAL_INFORMATION_LOCAL { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + BYTE Data[1]; + }; + + if (resultLength < offsetof(KEY_VALUE_PARTIAL_INFORMATION_LOCAL, Data) + 1) { + return false; + } + + const auto* kv = reinterpret_cast(buffer); + const ULONG dataLen = kv->DataLength; + if (dataLen == 0 || dataLen >= sizeof(buffer)) return false; + + char productId[64] = { 0 }; + const size_t copyLen = (dataLen < (sizeof(productId) - 1)) ? dataLen : (sizeof(productId) - 1); + memcpy(productId, kv->Data, copyLen); + productId[copyLen] = '\0'; struct TargetPattern { const char* product_id; @@ -7130,17 +7159,17 @@ struct VM { }; constexpr TargetPattern targets[] = { - {"55274-640-2673064-23950", "JOEBOX"}, - {"76487-644-3177037-23510", "CWSANDBOX"}, - {"76487-337-8429955-22614", "ANUBIS"} + {"55274-640-2673064-23950", brands::JOEBOX}, + {"76487-644-3177037-23510", brands::CWSANDBOX}, + {"76487-337-8429955-22614", brands::ANUBIS} }; constexpr size_t target_len = 21; - if (strlen(buffer) != target_len) return false; + if (strlen(productId) != target_len) return false; for (const auto& target : targets) { - if (memcmp(buffer, target.product_id, target_len) == 0) { + if (memcmp(productId, target.product_id, target_len) == 0) { debug("GAMARUE: Detected ", target.product_id); return core::add(target.brand); } @@ -7166,7 +7195,7 @@ struct VM { ctx->Eip += 4; // skip past the "call VPC" opcodes return static_cast(EXCEPTION_CONTINUE_EXECUTION); // we can safely resume execution since we skipped faulty instruction - }; + }; __try { __asm { @@ -7456,39 +7485,73 @@ struct VM { * @implements VM::MUTEX */ [[nodiscard]] static bool mutex() { - auto is_mutex_present = [](const char* lpMutexName) -> bool { - if (lpMutexName == 0) { - return false; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + using RtlInitUnicodeString_t = void(__stdcall*)(PUNICODE_STRING DestinationString, PCWSTR SourceString); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE Handle); + using NtOpenMutant_t = NTSTATUS(__stdcall*)(PHANDLE MutantHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); + + const char* names[] = { "NtOpenMutant", "RtlInitUnicodeString", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtOpenMutant = reinterpret_cast(funcs[0]); + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); + + if (!pNtOpenMutant || !pRtlInitUnicodeString || !pNtClose) { + return false; + } + + auto try_mutex_name = [&](const wchar_t* baseName) -> bool { + std::wstring full = L"\\BaseNamedObjects\\"; + full += baseName; + + UNICODE_STRING uName; + pRtlInitUnicodeString(&uName, full.c_str()); + + OBJECT_ATTRIBUTES objAttr; + ZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uName; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + HANDLE hMutant = nullptr; + NTSTATUS st = pNtOpenMutant(&hMutant, MUTANT_QUERY_STATE, &objAttr); + if (NT_SUCCESS(st)) { + if (hMutant) pNtClose(hMutant); + return true; } - SetLastError(0); - const HANDLE hObject = CreateMutexA(0, 0, lpMutexName); - const DWORD dwError = GetLastError(); + // some contexts expose it without the prefix + pRtlInitUnicodeString(&uName, baseName); + ZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uName; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; - if (hObject) CloseHandle(hObject); + hMutant = nullptr; + st = pNtOpenMutant(&hMutant, MUTANT_QUERY_STATE, &objAttr); + if (NT_SUCCESS(st)) { + if (hMutant) pNtClose(hMutant); + return true; + } - return (dwError == ERROR_ALREADY_EXISTS); + return false; }; - if ( - is_mutex_present("Sandboxie_SingleInstanceMutex_Control") || - is_mutex_present("SBIE_BOXED_ServiceInitComplete_Mutex1") - ) { + if (try_mutex_name(L"Sandboxie_SingleInstanceMutex_Control") || + try_mutex_name(L"SBIE_BOXED_ServiceInitComplete_Mutex1")) { debug("MUTEX: Detected Sandboxie"); return core::add(brands::SANDBOXIE); } - if (is_mutex_present("MicrosoftVirtualPC7UserServiceMakeSureWe'reTheOnlyOneMutex")) { + if (try_mutex_name(L"MicrosoftVirtualPC7UserServiceMakeSureWe'reTheOnlyOneMutex")) { debug("MUTEX: Detected VPC"); return core::add(brands::VPC); } - /* - if (is_mutex_present("Frz_State")) { // DeepFreeze - debug("MUTEX: Detected DeepFreeze"); - return true; - } - */ return false; } @@ -7501,9 +7564,47 @@ struct VM { * @implements VM::CUCKOO_DIR */ [[nodiscard]] static bool cuckoo_dir() { - const DWORD attrs = GetFileAttributes(_T("C:\\Cuckoo")); + using NtOpenFile_t = NTSTATUS(__stdcall*)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, ULONG OpenOptions); + using RtlInitUnicodeString_t = void(__stdcall*)(PUNICODE_STRING DestinationString, PCWSTR SourceString); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE Handle); + + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "NtOpenFile", "RtlInitUnicodeString", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtOpenFile = reinterpret_cast(funcs[0]); + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); + + if (!pNtOpenFile || !pRtlInitUnicodeString || !pNtClose) { + return false; + } + + const wchar_t* nativePath = L"\\??\\C:\\Cuckoo"; + UNICODE_STRING uPath; + pRtlInitUnicodeString(&uPath, nativePath); + + OBJECT_ATTRIBUTES objAttr; + ZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPath; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; - if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { + IO_STATUS_BLOCK iosb; + HANDLE hFile = nullptr; + + constexpr ACCESS_MASK desiredAccess = FILE_READ_ATTRIBUTES; + constexpr ULONG shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + constexpr ULONG openOptions = FILE_OPEN | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE; + + const NTSTATUS st = pNtOpenFile(&hFile, desiredAccess, &objAttr, &iosb, shareAccess, openOptions); + if (NT_SUCCESS(st)) { + if (hFile) pNtClose(hFile); return core::add(brands::CUCKOO); } @@ -7519,21 +7620,50 @@ struct VM { * @implements VM::CUCKOO_PIPE */ [[nodiscard]] static bool cuckoo_pipe() { - const HANDLE hPipe = CreateFile( - TEXT("\\\\.\\pipe\\cuckoo"), - GENERIC_READ, - 0, - nullptr, - OPEN_EXISTING, - 0, - nullptr - ); - - if (hPipe != INVALID_HANDLE_VALUE) { - CloseHandle(hPipe); + using NtOpenFile_t = NTSTATUS(__stdcall*)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, ULONG OpenOptions); + using RtlInitUnicodeString_t = void(__stdcall*)(PUNICODE_STRING DestinationString, PCWSTR SourceString); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE Handle); + + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "NtOpenFile", "RtlInitUnicodeString", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtOpenFile = reinterpret_cast(funcs[0]); + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); + + if (!pNtOpenFile || !pRtlInitUnicodeString || !pNtClose) { + return false; + } + + const wchar_t* pipePath = L"\\??\\pipe\\cuckoo"; + UNICODE_STRING uPipe; + pRtlInitUnicodeString(&uPipe, pipePath); + + OBJECT_ATTRIBUTES objAttr; + ZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPipe; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + IO_STATUS_BLOCK iosb; + HANDLE hPipe = nullptr; + + constexpr ACCESS_MASK desiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES; + constexpr ULONG shareAccess = 0; + constexpr ULONG openOptions = FILE_OPEN | FILE_SYNCHRONOUS_IO_NONALERT; + + const NTSTATUS st = pNtOpenFile(&hPipe, desiredAccess, &objAttr, &iosb, shareAccess, openOptions); + if (NT_SUCCESS(st)) { + if (hPipe) pNtClose(hPipe); return core::add(brands::CUCKOO); } - + return false; } @@ -7598,7 +7728,7 @@ struct VM { * @implements VM::DRIVERS */ [[nodiscard]] static bool drivers() { - typedef struct _SYSTEM_MODULE_INFORMATION { + struct _SYSTEM_MODULE_INFORMATION { PVOID Reserved[2]; PVOID ImageBaseAddress; ULONG ImageSize; @@ -7608,48 +7738,40 @@ struct VM { USHORT LoadCount; USHORT PathLength; CHAR ImageName[256]; - } SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION; + }; - typedef struct _SYSTEM_MODULE_INFORMATION_EX { + struct _SYSTEM_MODULE_INFORMATION_EX { ULONG NumberOfModules; - SYSTEM_MODULE_INFORMATION Module[1]; - } SYSTEM_MODULE_INFORMATION_EX, * PSYSTEM_MODULE_INFORMATION_EX; + _SYSTEM_MODULE_INFORMATION Module[1]; + }; - typedef NTSTATUS(__stdcall* NtQuerySystemInformationFn)( - ULONG SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength - ); + using SYSTEM_MODULE_INFORMATION = _SYSTEM_MODULE_INFORMATION; + using PSYSTEM_MODULE_INFORMATION = _SYSTEM_MODULE_INFORMATION*; + using SYSTEM_MODULE_INFORMATION_EX = _SYSTEM_MODULE_INFORMATION_EX; + using PSYSTEM_MODULE_INFORMATION_EX = _SYSTEM_MODULE_INFORMATION_EX*; - typedef NTSTATUS(__stdcall* NtAllocateVirtualMemoryFn)( + using NtQuerySystemInformationFn = NTSTATUS(__stdcall*)(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); + using NtAllocateVirtualMemoryFn = NTSTATUS(__stdcall*)( HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect - ); - - typedef NTSTATUS(__stdcall* NtFreeVirtualMemoryFn)( - HANDLE ProcessHandle, - PVOID* BaseAddress, - PSIZE_T RegionSize, - ULONG FreeType - ); + ); + using NtFreeVirtualMemoryFn = NTSTATUS(__stdcall*)(HANDLE ProcessHandle, PVOID* BaseAddress, PSIZE_T RegionSize, ULONG FreeType); constexpr ULONG SystemModuleInformation = 11; - const HMODULE hModule = GetModuleHandle(_T("ntdll.dll")); - if (!hModule) return false; - - const char* functionNames[] = { "NtQuerySystemInformation", "NtAllocateVirtualMemory", "NtFreeVirtualMemory" }; - void* functionPointers[3] = { nullptr, nullptr, nullptr }; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - util::GetFunctionAddress(hModule, functionNames, functionPointers, 3); + const char* names[] = { "NtQuerySystemInformation", "NtAllocateVirtualMemory", "NtFreeVirtualMemory" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - const auto ntQuerySystemInformation = reinterpret_cast(functionPointers[0]); - const auto ntAllocateVirtualMemory = reinterpret_cast(functionPointers[1]); - const auto ntFreeVirtualMemory = reinterpret_cast(functionPointers[2]); + const auto ntQuerySystemInformation = reinterpret_cast(funcs[0]); + const auto ntAllocateVirtualMemory = reinterpret_cast(funcs[1]); + const auto ntFreeVirtualMemory = reinterpret_cast(funcs[2]); if (ntQuerySystemInformation == nullptr || ntAllocateVirtualMemory == nullptr || ntFreeVirtualMemory == nullptr) return false; @@ -7663,7 +7785,7 @@ struct VM { SIZE_T regionSize = ulSize; ntAllocateVirtualMemory(hProcess, &allocatedMemory, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - auto pSystemModuleInfoEx = reinterpret_cast(allocatedMemory); + const auto pSystemModuleInfoEx = reinterpret_cast(allocatedMemory); status = ntQuerySystemInformation(SystemModuleInformation, pSystemModuleInfoEx, ulSize, &ulSize); if (!(((NTSTATUS)(status)) >= 0)) { ntFreeVirtualMemory(hProcess, &allocatedMemory, ®ionSize, MEM_RELEASE); @@ -7704,9 +7826,16 @@ struct VM { * @implements VM::DISK_SERIAL */ [[nodiscard]] static bool disk_serial_number() { + using NtOpenFile_t = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG); + using NtDeviceIoControlFile_t = NTSTATUS(__stdcall*)(HANDLE, HANDLE, PVOID, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG); + using NtAllocateVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG); + using NtFreeVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE); + using RtlInitUnicodeString_t = void(__stdcall*)(PUNICODE_STRING, PCWSTR); + bool result = false; constexpr u8 MAX_PHYSICAL_DRIVES = 4; - constexpr SIZE_T MAX_DESCRIPTOR_SIZE = 64 * 1024; + constexpr SIZE_T MAX_DESCRIPTOR_SIZE = 64 * 1024; u8 successfulOpens = 0; auto is_qemu_serial = [](const char* str) -> bool { @@ -7720,7 +7849,7 @@ struct VM { auto toupper_char = [](char c) -> char { return (c >= 'a' && c <= 'z') ? static_cast(c - 'a' + 'A') : c; - }; + }; if (toupper_char(str[0]) != 'V' || toupper_char(str[1]) != 'B' || str[10] != '-') { return false; @@ -7729,7 +7858,7 @@ struct VM { auto is_hex = [&](char c) { char upper_c = toupper_char(c); return (upper_c >= '0' && upper_c <= '9') || (upper_c >= 'A' && upper_c <= 'F'); - }; + }; static constexpr std::array hex_positions = { { 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18 @@ -7749,74 +7878,109 @@ struct VM { return static_cast(static_cast(p) - s); }; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return result; + + const char* names[] = { + "RtlInitUnicodeString", + "NtOpenFile", + "NtDeviceIoControlFile", + "NtAllocateVirtualMemory", + "NtFreeVirtualMemory", + "NtFlushInstructionCache", + "NtClose" + }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[0]); + const auto pNtOpenFile = reinterpret_cast(funcs[1]); + const auto pNtDeviceIoControlFile = reinterpret_cast(funcs[2]); + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[3]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[4]); + const auto pNtClose = reinterpret_cast(funcs[6]); + + if (!pRtlInitUnicodeString || !pNtOpenFile || !pNtDeviceIoControlFile || + !pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtClose) { + return result; + } + for (u8 drive = 0; drive < MAX_PHYSICAL_DRIVES; ++drive) { wchar_t path[32]; - swprintf_s(path, L"\\\\.\\PhysicalDrive%u", drive); + swprintf_s(path, L"\\??\\PhysicalDrive%u", drive); - const HANDLE hDevice = CreateFileW( - path, - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, - OPEN_EXISTING, - 0, - nullptr - ); + UNICODE_STRING uPath; + pRtlInitUnicodeString(&uPath, path); - if (hDevice == INVALID_HANDLE_VALUE) { + OBJECT_ATTRIBUTES objAttr; + RtlZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPath; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + objAttr.RootDirectory = nullptr; + + IO_STATUS_BLOCK iosb; + HANDLE hDevice = nullptr; + + constexpr ACCESS_MASK desiredAccess = SYNCHRONIZE | FILE_READ_ATTRIBUTES; + constexpr ULONG shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; + constexpr ULONG openOptions = FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT; + + NTSTATUS st = pNtOpenFile(&hDevice, desiredAccess, &objAttr, &iosb, shareAccess, openOptions); + if (!NT_SUCCESS(st) || hDevice == nullptr) { continue; } ++successfulOpens; + // stack buffer attempt BYTE stackBuf[512] = { 0 }; - STORAGE_DEVICE_DESCRIPTOR* descriptor = reinterpret_cast(stackBuf); - DWORD bytesReturned = 0; + const STORAGE_DEVICE_DESCRIPTOR* descriptor = reinterpret_cast(stackBuf); + STORAGE_PROPERTY_QUERY query{}; query.PropertyId = StorageDeviceProperty; query.QueryType = PropertyStandardQuery; + const ULONG ioctl = IOCTL_STORAGE_QUERY_PROPERTY; + + st = pNtDeviceIoControlFile(hDevice, nullptr, nullptr, nullptr, &iosb, + ioctl, + &query, sizeof(query), + stackBuf, sizeof(stackBuf)); + BYTE* allocatedBuffer = nullptr; SIZE_T allocatedSize = 0; - bool ok = DeviceIoControl( - hDevice, - IOCTL_STORAGE_QUERY_PROPERTY, - &query, sizeof(query), - stackBuf, sizeof(stackBuf), - &bytesReturned, - nullptr - ); + if (!NT_SUCCESS(st)) { + DWORD reportedSize = 0; + if (descriptor && descriptor->Size > 0) { + reportedSize = descriptor->Size; + } - if (!ok) { - const DWORD err = GetLastError(); - if (err == ERROR_INSUFFICIENT_BUFFER && descriptor->Size > 0) { - DWORD reportedSize = descriptor->Size; - if (reportedSize < sizeof(STORAGE_DEVICE_DESCRIPTOR) || static_cast(reportedSize) > MAX_DESCRIPTOR_SIZE) { - CloseHandle(hDevice); - continue; - } + if (reportedSize > 0 && reportedSize < static_cast(MAX_DESCRIPTOR_SIZE) && reportedSize >= sizeof(STORAGE_DEVICE_DESCRIPTOR)) { allocatedSize = static_cast(reportedSize); - allocatedBuffer = static_cast(LocalAlloc(LMEM_FIXED, allocatedSize)); - if (!allocatedBuffer) { - CloseHandle(hDevice); + PVOID allocBase = nullptr; + SIZE_T regionSize = allocatedSize; + st = pNtAllocateVirtualMemory((HANDLE)-1, &allocBase, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st) || allocBase == nullptr) { + pNtClose(hDevice); continue; } - descriptor = reinterpret_cast(allocatedBuffer); - if (!DeviceIoControl( - hDevice, - IOCTL_STORAGE_QUERY_PROPERTY, + allocatedBuffer = reinterpret_cast(allocBase); + st = pNtDeviceIoControlFile(hDevice, nullptr, nullptr, nullptr, &iosb, + ioctl, &query, sizeof(query), - descriptor, static_cast(allocatedSize), - &bytesReturned, - nullptr)) - { - LocalFree(allocatedBuffer); - CloseHandle(hDevice); + allocatedBuffer, static_cast(allocatedSize)); + if (!NT_SUCCESS(st)) { + PVOID freeBase = reinterpret_cast(allocatedBuffer); + SIZE_T freeSize = allocatedSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); + pNtClose(hDevice); continue; } + descriptor = reinterpret_cast(allocatedBuffer); } else { - CloseHandle(hDevice); + pNtClose(hDevice); continue; } } @@ -7825,10 +7989,12 @@ struct VM { const DWORD reportedSize = descriptor->Size; if (reportedSize < sizeof(STORAGE_DEVICE_DESCRIPTOR) || static_cast(reportedSize) > MAX_DESCRIPTOR_SIZE) { if (allocatedBuffer) { - LocalFree(allocatedBuffer); + PVOID freeBase = reinterpret_cast(allocatedBuffer); + SIZE_T freeSize = allocatedSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); allocatedBuffer = nullptr; } - CloseHandle(hDevice); + pNtClose(hDevice); continue; } } @@ -7843,19 +8009,23 @@ struct VM { if (is_qemu_serial(serial) || is_vbox_serial(serial, serialLen)) { if (allocatedBuffer) { - LocalFree(allocatedBuffer); + PVOID freeBase = reinterpret_cast(allocatedBuffer); + SIZE_T freeSize = allocatedSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); allocatedBuffer = nullptr; } - CloseHandle(hDevice); + pNtClose(hDevice); return true; } } if (allocatedBuffer) { - LocalFree(allocatedBuffer); + PVOID freeBase = reinterpret_cast(allocatedBuffer); + SIZE_T freeSize = allocatedSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); allocatedBuffer = nullptr; } - CloseHandle(hDevice); + pNtClose(hDevice); } if (successfulOpens == 0) { @@ -7874,42 +8044,94 @@ struct VM { * @implements VM::IVSHMEM */ [[nodiscard]] static bool ivshmem() { + typedef struct _KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLen; + ULONG MaxClassLen; + ULONG Values; + ULONG MaxValueNameLen; + ULONG MaxValueDataLen; + WCHAR Class[1]; + } KEY_FULL_INFORMATION, * PKEY_FULL_INFORMATION; + + typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + KeyVirtualizationInformation, + KeyHandleTagsInformation, + KeyTrustInformation, + KeyLayerInformation, + MaxKeyInfoClass + } KEY_INFORMATION_CLASS; + + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "RtlInitUnicodeString", "NtOpenKey", "NtQueryKey", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[0]); + const auto pNtOpenKey = reinterpret_cast(funcs[1]); + const auto pNtQueryKey = reinterpret_cast(funcs[2]); + const auto pNtClose = reinterpret_cast(funcs[3]); + + if (!pRtlInitUnicodeString || !pNtOpenKey || !pNtQueryKey || !pNtClose) { + return false; + } + constexpr GUID GUID_IVSHMEM_IFACE = { 0xdf576976, 0x569d, 0x4672, { 0x95, 0xa0, 0xf5, 0x7e, 0x4e, 0xa0, 0xb2, 0x10 } }; wchar_t interface_class_path[256]; swprintf_s( interface_class_path, - L"SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}", + ARRAYSIZE(interface_class_path), + L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}", GUID_IVSHMEM_IFACE.Data1, GUID_IVSHMEM_IFACE.Data2, GUID_IVSHMEM_IFACE.Data3, GUID_IVSHMEM_IFACE.Data4[0], GUID_IVSHMEM_IFACE.Data4[1], GUID_IVSHMEM_IFACE.Data4[2], GUID_IVSHMEM_IFACE.Data4[3], GUID_IVSHMEM_IFACE.Data4[4], GUID_IVSHMEM_IFACE.Data4[5], GUID_IVSHMEM_IFACE.Data4[6], GUID_IVSHMEM_IFACE.Data4[7] ); - HKEY hKey = nullptr; - if (RegOpenKeyExW( - HKEY_LOCAL_MACHINE, - interface_class_path, - 0, - KEY_READ, - &hKey - ) != ERROR_SUCCESS) { + UNICODE_STRING uPath; + pRtlInitUnicodeString(&uPath, interface_class_path); + + OBJECT_ATTRIBUTES objAttr; + RtlZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPath; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + HANDLE hKey = nullptr; + NTSTATUS st = pNtOpenKey(&hKey, KEY_READ, &objAttr); + if (!NT_SUCCESS(st) || hKey == nullptr) { return false; } + BYTE infoBuf[512] = {}; + ULONG returnedLen = 0; + st = pNtQueryKey(hKey, KeyFullInformation, infoBuf, sizeof(infoBuf), &returnedLen); + DWORD number_of_subkeys = 0; - if (RegQueryInfoKeyW( - hKey, - nullptr, nullptr, nullptr, - &number_of_subkeys, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr - ) != ERROR_SUCCESS) { - RegCloseKey(hKey); + if (NT_SUCCESS(st) && returnedLen >= sizeof(KEY_FULL_INFORMATION)) { + auto* kfi = reinterpret_cast(infoBuf); + number_of_subkeys = static_cast(kfi->SubKeys); + } + else { + pNtClose(hKey); return false; } - RegCloseKey(hKey); + pNtClose(hKey); return number_of_subkeys > 0; } @@ -7959,51 +8181,94 @@ struct VM { * @implements VM::DEVICE_HANDLES */ [[nodiscard]] static bool device_handles() { - const HANDLE handle1 = CreateFile(_T("\\\\.\\VBoxMiniRdrDN"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - const HANDLE handle2 = CreateFile(_T("\\\\.\\pipe\\VBoxMiniRdDN"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - const HANDLE handle3 = CreateFile(_T("\\\\.\\VBoxTrayIPC"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - const HANDLE handle4 = CreateFile(_T("\\\\.\\pipe\\VBoxTrayIPC"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - const HANDLE handle5 = CreateFile(_T("\\\\.\\HGFS"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - const HANDLE handle6 = CreateFile(_T("\\\\.\\pipe\\cuckoo"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - bool vbox = false; + const char* names[] = { "RtlInitUnicodeString", "NtOpenFile", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - if ( - (handle1 != INVALID_HANDLE_VALUE) || - (handle2 != INVALID_HANDLE_VALUE) || - (handle3 != INVALID_HANDLE_VALUE) || - (handle4 != INVALID_HANDLE_VALUE) - ) { + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[0]); + const auto pNtOpenFile = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); + + if (!pRtlInitUnicodeString || !pNtOpenFile || !pNtClose) { + return false; + } + + auto try_open_native = [&](const wchar_t* nativePath) -> HANDLE { + UNICODE_STRING uPath; + pRtlInitUnicodeString(&uPath, nativePath); + + OBJECT_ATTRIBUTES objAttr; + RtlZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPath; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + IO_STATUS_BLOCK iosb; + HANDLE hFile = nullptr; + + // minimal read access to emulate CreateFile(...GENERIC_READ...) + constexpr ACCESS_MASK desiredAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES; + constexpr ULONG shareAccess = FILE_SHARE_READ; + constexpr ULONG openOptions = FILE_OPEN | FILE_SYNCHRONOUS_IO_NONALERT; + + const NTSTATUS st = pNtOpenFile(&hFile, desiredAccess, &objAttr, &iosb, shareAccess, openOptions); + if (NT_SUCCESS(st) && hFile) { + return hFile; + } + return INVALID_HANDLE_VALUE; + }; + + // \\.\Name -> \??\Name, \\.\pipe\name -> \??\pipe\name + const wchar_t* paths[] = { + L"\\??\\VBoxMiniRdrDN", // \\.\VBoxMiniRdrDN + L"\\??\\pipe\\VBoxMiniRdDN",// \\.\pipe\VBoxMiniRdDN + L"\\??\\VBoxTrayIPC", // \\.\VBoxTrayIPC + L"\\??\\pipe\\VBoxTrayIPC",// \\.\pipe\VBoxTrayIPC + L"\\??\\HGFS", // \\.\HGFS (VMware) + L"\\??\\pipe\\cuckoo" // \\.\pipe\cuckoo (Cuckoo) + }; + + HANDLE handles[ARRAYSIZE(paths)]{}; + for (size_t i = 0; i < ARRAYSIZE(paths); ++i) { + handles[i] = try_open_native(paths[i]); + } + + bool vbox = false; + if (handles[0] != INVALID_HANDLE_VALUE || + handles[1] != INVALID_HANDLE_VALUE || + handles[2] != INVALID_HANDLE_VALUE || + handles[3] != INVALID_HANDLE_VALUE) { vbox = true; } - CloseHandle(handle1); - CloseHandle(handle2); - CloseHandle(handle3); - CloseHandle(handle4); + for (size_t i = 0; i < 4; ++i) { + if (handles[i] != INVALID_HANDLE_VALUE) { + pNtClose(handles[i]); + } + } if (vbox) { debug("DEVICE_HANDLES: Detected VBox related device handles"); return core::add(brands::VBOX); } - if (handle5 != INVALID_HANDLE_VALUE) { - CloseHandle(handle5); + if (handles[4] != INVALID_HANDLE_VALUE) { + pNtClose(handles[4]); debug("DEVICE_HANDLES: Detected VMware related device (HGFS)"); return core::add(brands::VMWARE); } - if (handle6 != INVALID_HANDLE_VALUE) { - CloseHandle(handle6); + if (handles[5] != INVALID_HANDLE_VALUE) { + pNtClose(handles[5]); debug("DEVICE_HANDLES: Detected Cuckoo related device (pipe)"); return core::add(brands::CUCKOO); } - CloseHandle(handle5); - CloseHandle(handle6); - return false; - } + } /** @@ -8046,11 +8311,10 @@ struct VM { return false; } - typedef struct _HV_DETAILS { + struct HV_DETAILS { ULONG Data[4]; - } HV_DETAILS, * PHV_DETAILS; - - typedef struct _SYSTEM_HYPERVISOR_DETAIL_INFORMATION { + }; + struct SYSTEM_HYPERVISOR_DETAIL_INFORMATION { HV_DETAILS HvVendorAndMaxFunction; HV_DETAILS HypervisorInterface; HV_DETAILS HypervisorVersion; @@ -8058,26 +8322,26 @@ struct VM { HV_DETAILS HwFeatures; HV_DETAILS EnlightenmentInfo; HV_DETAILS ImplementationLimits; - } SYSTEM_HYPERVISOR_DETAIL_INFORMATION, * PSYSTEM_HYPERVISOR_DETAIL_INFORMATION; + }; - typedef NTSTATUS(__stdcall* FN_NtQuerySystemInformation)( + using PHV_DETAILS = HV_DETAILS*; + using PSYSTEM_HYPERVISOR_DETAIL_INFORMATION = SYSTEM_HYPERVISOR_DETAIL_INFORMATION*; + + using FN_NtQuerySystemInformation = NTSTATUS(__stdcall*)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); - const HMODULE hNtdll = GetModuleHandle(_T("ntdll.dll")); - if (!hNtdll) { - return false; - } - - const char* functionNames[] = { "NtQuerySystemInformation" }; - void* functions[1] = { nullptr }; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - util::GetFunctionAddress(hNtdll, functionNames, functions, 1); + const char* names[] = { "NtQuerySystemInformation" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - FN_NtQuerySystemInformation pNtQuerySystemInformation = reinterpret_cast(functions[0]); + const FN_NtQuerySystemInformation pNtQuerySystemInformation = reinterpret_cast(funcs[0]); if (pNtQuerySystemInformation) { SYSTEM_HYPERVISOR_DETAIL_INFORMATION hvInfo = { {} }; const NTSTATUS status = pNtQuerySystemInformation(static_cast(0x9F), &hvInfo, sizeof(hvInfo), nullptr); @@ -8101,48 +8365,44 @@ struct VM { * @implements VM::VIRTUAL_REGISTRY */ [[nodiscard]] static bool virtual_registry() { - #pragma warning(disable : 4459) - typedef struct _UNICODE_STRING { + struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; - } UNICODE_STRING, * PUNICODE_STRING; - - typedef struct _OBJECT_ATTRIBUTES { + }; + struct OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; - PUNICODE_STRING ObjectName; + UNICODE_STRING* ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService; - } OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; - - typedef enum _OBJECT_INFORMATION_CLASS { + }; + enum OBJECT_INFORMATION_CLASS { ObjectBasicInformation = 0, ObjectNameInformation = 1, ObjectTypeInformation = 2 - } OBJECT_INFORMATION_CLASS; - - typedef struct _OBJECT_NAME_INFORMATION { + }; + struct OBJECT_NAME_INFORMATION { UNICODE_STRING Name; - } OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION; - #pragma warning(default : 4459) - - typedef NTSTATUS(__stdcall* PNtOpenKey)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); - typedef NTSTATUS(__stdcall* PNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); - - const HMODULE hModule = GetModuleHandle(_T("ntdll.dll")); - if (!hModule) - return false; + }; + + using POBJECT_NAME_INFORMATION = OBJECT_NAME_INFORMATION*; + using PNtOpenKey = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); + using PNtQueryObject = NTSTATUS(__stdcall*)(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); - const char* functionNames[] = { "NtOpenKey", "NtQueryObject" }; - void* functionPointers[2] = { nullptr, nullptr }; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - util::GetFunctionAddress(hModule, functionNames, functionPointers, 2); + const char* names[] = { "NtOpenKey", "NtQueryObject", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - const auto NtOpenKey = reinterpret_cast(functionPointers[0]); - const auto NtQueryObject = reinterpret_cast(functionPointers[1]); - if (!NtOpenKey || !NtQueryObject) + const auto NtOpenKey = reinterpret_cast(funcs[0]); + const auto NtQueryObject = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); + + if (!NtOpenKey || !NtQueryObject || !pNtClose) return false; UNICODE_STRING keyPath{}; @@ -8160,18 +8420,18 @@ struct VM { }; HANDLE hKey = nullptr; - NTSTATUS status = NtOpenKey(&hKey, KEY_READ, &objAttr); + NTSTATUS status = NtOpenKey(&hKey, KEY_READ, reinterpret_cast(&objAttr)); if (!(((NTSTATUS)(status)) >= 0)) return false; alignas(16) BYTE buffer[1024]{}; ULONG returnedLength = 0; status = NtQueryObject(hKey, ObjectNameInformation, buffer, sizeof(buffer), &returnedLength); - CloseHandle(hKey); + pNtClose(hKey); if (!(((NTSTATUS)(status)) >= 0)) return false; - auto pObjectName = reinterpret_cast(buffer); + const auto pObjectName = reinterpret_cast(buffer); UNICODE_STRING expectedName{}; expectedName.Buffer = const_cast(L"\\REGISTRY\\USER"); @@ -8190,38 +8450,95 @@ struct VM { * @implements VM::AUDIO */ [[nodiscard]] static bool audio() { - HKEY hKey = nullptr; - const LONG err = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render"), - 0, - KEY_READ | KEY_WOW64_64KEY, - &hKey - ); + struct KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLen; + ULONG MaxClassLen; + ULONG Values; + ULONG MaxValueNameLen; + ULONG MaxValueDataLen; + WCHAR Class[1]; + }; + using PKEY_FULL_INFORMATION = KEY_FULL_INFORMATION*; + + enum KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + KeyVirtualizationInformation, + KeyHandleTagsInformation, + KeyTrustInformation, + KeyLayerInformation, + MaxKeyInfoClass + }; - if (err != 0L) { - return true; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "RtlInitUnicodeString", "NtOpenKey", "NtQueryKey", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pRtlInitUnicodeString = reinterpret_cast(funcs[0]); + const auto pNtOpenKey = reinterpret_cast(funcs[1]); + const auto pNtQueryKey = reinterpret_cast(funcs[2]); + const auto pNtClose = reinterpret_cast(funcs[3]); + + if (!pRtlInitUnicodeString || !pNtOpenKey || !pNtQueryKey || !pNtClose) { + return false; } - - DWORD subKeyCount = 0; - RegQueryInfoKey( - hKey, - nullptr, - nullptr, - nullptr, - &subKeyCount, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr - ); - - RegCloseKey(hKey); - - return subKeyCount == 0; + + const wchar_t* nativePath = L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render"; + + UNICODE_STRING uPath; + pRtlInitUnicodeString(&uPath, nativePath); + + OBJECT_ATTRIBUTES objAttr; + RtlZeroMemory(&objAttr, sizeof(objAttr)); + objAttr.Length = sizeof(objAttr); + objAttr.ObjectName = &uPath; + objAttr.Attributes = OBJ_CASE_INSENSITIVE; + + HANDLE hKey = nullptr; + const ACCESS_MASK desiredAccess = KEY_READ; + + NTSTATUS st = pNtOpenKey(&hKey, desiredAccess, &objAttr); + if (!NT_SUCCESS(st) || hKey == nullptr) { + return false; + } + + constexpr KEY_INFORMATION_CLASS InfoClass = KeyFullInformation; + std::vector infoBuf(512); + ULONG returnedLen = 0; + + st = pNtQueryKey(hKey, InfoClass, infoBuf.data(), static_cast(infoBuf.size()), &returnedLen); + + if (!NT_SUCCESS(st) && returnedLen > infoBuf.size()) { + infoBuf.resize(returnedLen); + st = pNtQueryKey(hKey, InfoClass, infoBuf.data(), static_cast(infoBuf.size()), &returnedLen); + } + + bool hasValues = false; + if (NT_SUCCESS(st) && returnedLen >= sizeof(KEY_FULL_INFORMATION)) { + auto* kfi = reinterpret_cast(infoBuf.data()); + const DWORD valueCount = static_cast(kfi->Values); // values, not subkeys + hasValues = (valueCount > 0); + } + else { + pNtClose(hKey); + return false; + } + + pNtClose(hKey); + + return hasValues; } @@ -8337,7 +8654,7 @@ struct VM { for (auto& wstr : paths) { if (has_excluded_token(wstr)) { - debug("ACPI_SIGNATURE: Excluded signature -> ", wstr); + debug("ACPI_SIGNATURE: Valid signature -> ", wstr); continue; } @@ -8426,7 +8743,7 @@ struct VM { */ [[nodiscard]] static bool trap() { bool hypervisorCaught = false; -#if (x86) + #if (x86) // when a single-step (TF) and hardware breakpoint (DR0) collide, Intel CPUs set both DR6.BS and DR6.B0 to report both events, which help make this detection trick // AMD CPUs prioritize the breakpoint, setting only its corresponding bit in DR6 and clearing the single-step bit, which is why this technique is not compatible with AMD if (!cpu::is_intel()) { @@ -8445,30 +8762,73 @@ struct VM { }; SIZE_T trampSize = sizeof(trampoline); - // simple way to support x86 without recurring to inline assembly - void* execMem = VirtualAlloc(nullptr, trampSize, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); - if (!execMem) { + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { + "NtAllocateVirtualMemory", + "NtProtectVirtualMemory", + "NtFreeVirtualMemory", + "NtFlushInstructionCache", + "NtClose", + "NtGetContextThread", + "NtSetContextThread" + }; + 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 NtProtectVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG); + using NtFreeVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG); + using NtFlushInstructionCache_t = NTSTATUS(__stdcall*)(HANDLE, PVOID, SIZE_T); + using NtClose_t = NTSTATUS(__stdcall*)(HANDLE); + using NtGetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); + using NtSetContextThread_t = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT); + + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); + const auto pNtProtectVirtualMemory = reinterpret_cast(funcs[1]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[2]); + const auto pNtFlushInstructionCache = reinterpret_cast(funcs[3]); + const auto pNtClose = reinterpret_cast(funcs[4]); + const auto pNtGetContextThread = reinterpret_cast(funcs[5]); + const auto pNtSetContextThread = reinterpret_cast(funcs[6]); + + if (!pNtAllocateVirtualMemory || !pNtProtectVirtualMemory || !pNtFlushInstructionCache || + !pNtFreeVirtualMemory || !pNtGetContextThread || !pNtSetContextThread || !pNtClose) { return false; } - memcpy(execMem, trampoline, trampSize); - DWORD oldProtect = 0; - if (!VirtualProtect(execMem, trampSize, PAGE_EXECUTE_READ, &oldProtect)) { - VirtualFree(execMem, 0, MEM_RELEASE); + PVOID execMem = nullptr; + SIZE_T regionSize = trampSize; + NTSTATUS st = pNtAllocateVirtualMemory((HANDLE)-1, &execMem, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st) || !execMem) { return false; } - FlushInstructionCache(GetCurrentProcess(), execMem, trampSize); + memcpy(execMem, trampoline, trampSize); + + { + PVOID tmpBase = execMem; + SIZE_T tmpSz = trampSize; + ULONG oldProt = 0; + st = pNtProtectVirtualMemory((HANDLE)-1, &tmpBase, &tmpSz, PAGE_EXECUTE_READ, &oldProt); + if (!NT_SUCCESS(st)) { + PVOID freeBase = execMem; SIZE_T freeSize = trampSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); + return false; + } + } + + pNtFlushInstructionCache((HANDLE)-1, execMem, trampSize); int hitCount = 0; - // save original debug registers CONTEXT origCtx{}; origCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS; - const HANDLE thr = GetCurrentThread(); - if (!GetThreadContext(thr, &origCtx)) { - VirtualFree(execMem, 0, MEM_RELEASE); + const HANDLE thrHandle = GetCurrentThread(); + + if (!NT_SUCCESS(pNtGetContextThread(thrHandle, &origCtx))) { + PVOID freeBase = execMem; SIZE_T freeSize = trampSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); return false; } @@ -8477,9 +8837,11 @@ struct VM { const uintptr_t baseAddr = reinterpret_cast(execMem); dbgCtx.Dr0 = baseAddr + 11; // single step breakpoint address dbgCtx.Dr7 = 1; // enable local breakpoint 0 - if (!SetThreadContext(thr, &dbgCtx)) { - SetThreadContext(thr, &origCtx); - VirtualFree(execMem, 0, MEM_RELEASE); + + if (!NT_SUCCESS(pNtSetContextThread(thrHandle, &dbgCtx))) { + pNtSetContextThread(thrHandle, &origCtx); + PVOID freeBase = execMem; SIZE_T freeSize = trampSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); return false; } @@ -8517,9 +8879,12 @@ struct VM { } } - SetThreadContext(thr, &origCtx); - VirtualFree(execMem, 0, MEM_RELEASE); -#endif + pNtSetContextThread(thrHandle, &origCtx); + + PVOID freeBase = execMem; SIZE_T freeSize = trampSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); + + #endif return hypervisorCaught; } @@ -8531,61 +8896,60 @@ struct VM { */ [[nodiscard]] static bool ud() { bool saw_ud = false; - #if (MSVC) + #if (x86) - // ud2; ret - constexpr unsigned char ud_opcodes[] = { 0x0F, 0x0B, 0xC3 }; + constexpr unsigned char ud_opcodes[] = { 0x0F, 0x0B, 0xC3 }; // ud2; ret #elif (ARM32) - // udf #0; bx lr - // (Little-endian for 0xE7F000F0 and 0xE12FFF1E) - constexpr unsigned char ud_opcodes[] = { 0xF0, 0x00, 0xF0, 0xE7, 0x1E, 0xFF, 0x2F, 0xE1 }; + constexpr unsigned char ud_opcodes[] = { 0xF0, 0x00, 0xF0, 0xE7, 0x1E, 0xFF, 0x2F, 0xE1 }; #elif (ARM64) - // hlt #0; ret - // (Little-endian for 0xD4400000 and 0xD65F03C0) - constexpr unsigned char ud_opcodes[] = { 0x00, 0x00, 0x40, 0xD4, 0xC0, 0x03, 0x5F, 0xD6 }; + constexpr unsigned char ud_opcodes[] = { 0x00, 0x00, 0x40, 0xD4, 0xC0, 0x03, 0x5F, 0xD6 }; #else - // architecture not supported by this check - return false; - #endif + return saw_ud; + #endif - void* stub = nullptr; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - __try { - stub = VirtualAlloc(nullptr, sizeof(ud_opcodes), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (!stub) { - __leave; - } + const char* names[] = { "NtAllocateVirtualMemory", "NtProtectVirtualMemory", "NtFlushInstructionCache", "NtFreeVirtualMemory" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - memcpy(stub, ud_opcodes, sizeof(ud_opcodes)); + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); + const auto pNtProtectVirtualMemory = reinterpret_cast(funcs[1]); + const auto pNtFlushInstructionCache = reinterpret_cast(funcs[2]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[3]); - DWORD oldProtect = 0; - if (!VirtualProtect(stub, sizeof(ud_opcodes), PAGE_EXECUTE_READ, &oldProtect)) { - __leave; - } + if (!pNtAllocateVirtualMemory || !pNtProtectVirtualMemory || !pNtFlushInstructionCache || !pNtFreeVirtualMemory) { + return false; + } - // the instruction cache must be flushed after writing code to memory on ARM - #if (ARM) - FlushInstructionCache(GetCurrentProcess(), stub, sizeof(ud_opcodes)); - #else - FlushInstructionCache(GetCurrentProcess(), stub, sizeof(ud_opcodes)); - #endif + PVOID base = nullptr; + SIZE_T regionSize = sizeof(ud_opcodes); + NTSTATUS st = pNtAllocateVirtualMemory((HANDLE)-1, &base, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st) || !base) { + return false; + } - __try { - reinterpret_cast(stub)(); - } - __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION - ? EXCEPTION_EXECUTE_HANDLER - : EXCEPTION_CONTINUE_SEARCH) - { - saw_ud = true; - } + memcpy(base, ud_opcodes, sizeof(ud_opcodes)); + + ULONG oldProtect = 0; + st = pNtProtectVirtualMemory((HANDLE)-1, &base, ®ionSize, PAGE_EXECUTE_READ, &oldProtect); + if (!NT_SUCCESS(st)) { + pNtFreeVirtualMemory((HANDLE)-1, &base, ®ionSize, MEM_RELEASE); + return false; } - __finally { - if (stub) { - VirtualFree(stub, 0, MEM_RELEASE); - } + + pNtFlushInstructionCache((HANDLE)-1, base, regionSize); + + __try { + reinterpret_cast(base)(); } -#endif + __except (GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + saw_ud = true; + } + + pNtFreeVirtualMemory((HANDLE)-1, &base, ®ionSize, MEM_RELEASE); + return !saw_ud; } @@ -8659,7 +9023,7 @@ struct VM { VMCallInfo vmcallInfo = {}; u64 vmcallResult = 0; - constexpr u8 intelTemplate[44] = { + constexpr unsigned char intelTemplate[44] = { 0x48,0xBA,0,0,0,0,0,0,0,0, // mov rdx, imm64 ; PW1 0x48,0xB9,0,0,0,0,0,0,0,0, // mov rcx, imm64 ; PW3 0x48,0xB8,0,0,0,0,0,0,0,0, // mov rax, imm64 ; &vmcallInfo @@ -8668,7 +9032,7 @@ struct VM { 0xC3 // ret }; - constexpr u8 amdTemplate[44] = { + constexpr unsigned char amdTemplate[44] = { 0x48,0xBA,0,0,0,0,0,0,0,0, // mov rdx, imm64 ; PW1 0x48,0xB9,0,0,0,0,0,0,0,0, // mov rcx, imm64 ; PW3 0x48,0xB8,0,0,0,0,0,0,0,0, // mov rax, imm64 ; &vmcallInfo @@ -8677,16 +9041,30 @@ struct VM { 0xC3 // ret }; - constexpr SIZE_T stubSize = 44; + const SIZE_T stubSize = sizeof(intelTemplate); const bool isAmd = cpu::is_amd(); - // allocate RW memory so we can write template + immediates - void* stub = VirtualAlloc(nullptr, stubSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (!stub) { + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "NtAllocateVirtualMemory", "NtProtectVirtualMemory", "NtFlushInstructionCache", "NtFreeVirtualMemory" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); + const auto pNtProtectVirtualMemory = reinterpret_cast(funcs[1]); + const auto pNtFlushInstructionCache = reinterpret_cast(funcs[2]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[3]); + + if (!pNtAllocateVirtualMemory || !pNtProtectVirtualMemory || !pNtFlushInstructionCache || !pNtFreeVirtualMemory) { return false; } - // copy the template while writable + PVOID stub = nullptr; + SIZE_T regionSize = stubSize; + NTSTATUS st = pNtAllocateVirtualMemory((HANDLE)-1, &stub, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st) || !stub) return false; + if (isAmd) { memcpy(stub, amdTemplate, stubSize); } @@ -8694,21 +9072,24 @@ struct VM { memcpy(stub, intelTemplate, stubSize); } - // patch in the immediate values + // rdx imm64 + // rcx imm64 + // rax imm64 + // mov [imm64], rax immediate *reinterpret_cast(reinterpret_cast(stub) + 2) = PW1; *reinterpret_cast(reinterpret_cast(stub) + 12) = PW3; *reinterpret_cast(reinterpret_cast(stub) + 22) = reinterpret_cast(static_cast(&vmcallInfo)); *reinterpret_cast(reinterpret_cast(stub) + 35) = reinterpret_cast(static_cast(&vmcallResult)); - DWORD oldProtect = 0; - if (!VirtualProtect(stub, stubSize, PAGE_EXECUTE_READ, &oldProtect)) { - VirtualFree(stub, 0, MEM_RELEASE); + ULONG oldProtect = 0; + st = pNtProtectVirtualMemory((HANDLE)-1, &stub, ®ionSize, PAGE_EXECUTE_READ, &oldProtect); + if (!NT_SUCCESS(st)) { + pNtFreeVirtualMemory((HANDLE)-1, &stub, ®ionSize, MEM_RELEASE); return false; } - FlushInstructionCache(GetCurrentProcess(), stub, stubSize); + pNtFlushInstructionCache((HANDLE)-1, stub, regionSize); - // lambda that executes the stub (Intel or AMD) and checks for the CE signature auto tryPass = [&]() -> bool { vmcallInfo.structsize = static_cast(sizeof(VMCallInfo)); vmcallInfo.level2pass = PW2; @@ -8722,13 +9103,12 @@ struct VM { vmcallResult = 0; } - // the VM returns status in bits 24–31; Cheat Engine uses 0xCE here - return (((vmcallResult >> 24) & 0xFF) == 0xCE); + return (((vmcallResult >> 24) & 0xFF) == 0xCE); // the VM returns status in bits 24–31; Cheat Engine uses 0xCE here }; const bool found = tryPass(); - VirtualFree(stub, 0, MEM_RELEASE); + pNtFreeVirtualMemory((HANDLE)-1, &stub, ®ionSize, MEM_RELEASE); if (found) return core::add(brands::DBVM); @@ -8750,13 +9130,13 @@ struct VM { #endif { #if (x86_64) - const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); + const HMODULE ntdll = util::get_ntdll(); if (!ntdll) return false; const char* function_names[] = { "NtQuerySystemInformation" }; void* functions[1] = { nullptr }; - util::GetFunctionAddress(ntdll, function_names, functions, 1); + util::get_function_address(ntdll, function_names, functions, 1); using NtQuerySysInfo_t = NTSTATUS(__stdcall*)( SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG @@ -8837,61 +9217,39 @@ struct VM { * @implements VM::OBJECTS */ [[nodiscard]] static bool objects() { - typedef struct _OBJECT_DIRECTORY_INFORMATION { + struct OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; - } OBJECT_DIRECTORY_INFORMATION, * POBJECT_DIRECTORY_INFORMATION; - - typedef NTSTATUS(__stdcall* pfnNtOpenDirectoryObject)( - OUT PHANDLE DirectoryHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes - ); - - typedef NTSTATUS(__stdcall* pfnNtQueryDirectoryObject)( - IN HANDLE DirectoryHandle, - OUT PVOID Buffer, - IN ULONG Length, - IN BOOLEAN ReturnSingleEntry, - IN BOOLEAN RestartScan, - IN OUT PULONG Context, - OUT PULONG ReturnLength OPTIONAL - ); + }; - constexpr auto DIRECTORY_QUERY = (0x0001); + using POBJECT_DIRECTORY_INFORMATION = OBJECT_DIRECTORY_INFORMATION*; + constexpr auto DIRECTORY_QUERY = 0x0001; constexpr NTSTATUS STATUS_NO_MORE_ENTRIES = 0x8000001A; - HANDLE hDir = NULL; + HANDLE hDir = nullptr; OBJECT_ATTRIBUTES objAttr{}; UNICODE_STRING dirName{}; NTSTATUS status; - const HMODULE hNtdll = GetModuleHandle(_T("ntdll.dll")); - if (hNtdll == NULL) { - return false; - } - - pfnNtOpenDirectoryObject pNtOpenDirectoryObject = nullptr; - pfnNtQueryDirectoryObject pNtQueryDirectoryObject = nullptr; + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; - const char* func_names[] = { "NtOpenDirectoryObject", "NtQueryDirectoryObject" }; - void* func_addrs[] = { &pNtOpenDirectoryObject, &pNtQueryDirectoryObject }; + const char* names[] = { "NtOpenDirectoryObject", "NtQueryDirectoryObject", "NtClose" }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); - util::GetFunctionAddress(hNtdll, func_names, (void**)func_addrs, 2); + const auto pNtOpenDirectoryObject = reinterpret_cast(funcs[0]); + const auto pNtQueryDirectoryObject = reinterpret_cast(funcs[1]); + const auto pNtClose = reinterpret_cast(funcs[2]); - pNtOpenDirectoryObject = reinterpret_cast(func_addrs[0]); - pNtQueryDirectoryObject = reinterpret_cast(func_addrs[1]); - - if (pNtOpenDirectoryObject == nullptr || pNtQueryDirectoryObject == nullptr) { - return false; - } + if (!pNtOpenDirectoryObject || !pNtQueryDirectoryObject || !pNtClose) return false; const wchar_t* deviceDirPath = L"\\Device"; dirName.Buffer = (PWSTR)deviceDirPath; dirName.Length = (USHORT)(wcslen(deviceDirPath) * sizeof(wchar_t)); dirName.MaximumLength = dirName.Length + sizeof(wchar_t); - InitializeObjectAttributes(&objAttr, &dirName, OBJ_CASE_INSENSITIVE, NULL, NULL); + InitializeObjectAttributes(&objAttr, &dirName, OBJ_CASE_INSENSITIVE, nullptr, nullptr); status = pNtOpenDirectoryObject(&hDir, DIRECTORY_QUERY, &objAttr); @@ -8924,25 +9282,25 @@ struct VM { size_t newSize = static_cast(returnedLength); if (newSize > MAX_DIR_BUFFER) newSize = MAX_DIR_BUFFER; if (newSize <= buffer.size()) { - CloseHandle(hDir); + pNtClose(hDir); return false; } try { buffer.resize(newSize); } catch (...) { - CloseHandle(hDir); + pNtClose(hDir); return false; } continue; } - CloseHandle(hDir); + pNtClose(hDir); return false; } const size_t usedLen = (returnedLength == 0) ? buffer.size() : static_cast(returnedLength); if (usedLen < sizeof(OBJECT_DIRECTORY_INFORMATION) || usedLen > buffer.size()) { - CloseHandle(hDir); + pNtClose(hDir); return false; } @@ -8976,12 +9334,12 @@ struct VM { if (!gotName) { const uintptr_t altStart = bufBase + sizeof(OBJECT_DIRECTORY_INFORMATION); if (altStart >= bufEnd) { - CloseHandle(hDir); + pNtClose(hDir); return false; } const size_t maxBytes = bufEnd - altStart; if (maxBytes < sizeof(wchar_t)) { - CloseHandle(hDir); + pNtClose(hDir); return false; } const wchar_t* altPtr = reinterpret_cast(buffer.data() + (altStart - bufBase)); @@ -8992,7 +9350,7 @@ struct VM { if (altPtr[realChars] == L'\0') break; } if (realChars == maxChars) { - CloseHandle(hDir); + pNtClose(hDir); return false; } objectName.assign(altPtr, realChars); @@ -9000,23 +9358,23 @@ struct VM { } if (!gotName) { - CloseHandle(hDir); + pNtClose(hDir); return false; } if (objectName == L"VmGenerationCounter") { - CloseHandle(hDir); + pNtClose(hDir); debug("OBJECTS: Detected VmGenerationCounter"); return core::add(brands::HYPERV); } if (objectName == L"VmGid") { - CloseHandle(hDir); + pNtClose(hDir); debug("OBJECTS: Detected VmGid"); return core::add(brands::HYPERV); } } - CloseHandle(hDir); + pNtClose(hDir); return false; } @@ -9028,11 +9386,12 @@ struct VM { * @implements VM::NVRAM */ [[nodiscard]] static bool nvram_vars() { - typedef struct _VARIABLE_NAME { + struct VARIABLE_NAME { ULONG NextEntryOffset; GUID VendorGuid; WCHAR Name[1]; - } VARIABLE_NAME, * PVARIABLE_NAME; + }; + using PVARIABLE_NAME = VARIABLE_NAME*; using NtEnumerateSystemEnvironmentValuesEx_t = NTSTATUS(__stdcall*)( ULONG InformationClass, @@ -9052,7 +9411,7 @@ struct VM { return false; } - HANDLE hToken = NULL; + HANDLE hToken = nullptr; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return false; } @@ -9061,7 +9420,7 @@ struct VM { if (token) { TOKEN_PRIVILEGES tp{}; tp.PrivilegeCount = 1; - LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME, &tp.Privileges[0].Luid); + LookupPrivilegeValue(nullptr, SE_SYSTEM_ENVIRONMENT_NAME, &tp.Privileges[0].Luid); tp.Privileges[0].Attributes = 0; AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr); CloseHandle(token); @@ -9070,7 +9429,7 @@ struct VM { std::unique_ptr token_guard(hToken, token_closer); LUID luid{}; - if (!LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME, &luid)) { + if (!LookupPrivilegeValue(nullptr, SE_SYSTEM_ENVIRONMENT_NAME, &luid)) { return false; } @@ -9170,13 +9529,29 @@ struct VM { std::wstring guidStr; { - wchar_t guidBuf[40] = { 0 }; - if (0 != ::StringFromGUID2(varName->VendorGuid, guidBuf, _countof(guidBuf))) { - guidStr = guidBuf; - } - else { - return true; - } + auto guid_to_wstring = [](const GUID& g) -> std::wstring { + wchar_t buf[40] = {}; + int written = _snwprintf_s( + buf, _countof(buf), _TRUNCATE, + L"{%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}", + static_cast(g.Data1), + static_cast(g.Data2), + static_cast(g.Data3), + static_cast(g.Data4[0]), + static_cast(g.Data4[1]), + static_cast(g.Data4[2]), + static_cast(g.Data4[3]), + static_cast(g.Data4[4]), + static_cast(g.Data4[5]), + static_cast(g.Data4[6]), + static_cast(g.Data4[7]) + ); + if (written <= 0) return std::wstring(); + return std::wstring(buf); + }; + + guidStr = guid_to_wstring(varName->VendorGuid); + if (guidStr.empty()) return true; } if (!nameStr.empty() && nameStr.rfind(L"VMM", 0) == 0) { @@ -9367,11 +9742,29 @@ struct VM { nameStr.assign(namePtr, realChars); } - wchar_t guidBuf[40]{}; - if (0 == ::StringFromGUID2(var->VendorGuid, guidBuf, _countof(guidBuf))) { - return true; - } - std::wstring guidStr = guidBuf; + auto guid_to_wstring = [](const GUID& g) -> std::wstring { + wchar_t buf[40] = {}; + int written = _snwprintf_s( + buf, _countof(buf), _TRUNCATE, + L"{%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}", + static_cast(g.Data1), + static_cast(g.Data2), + static_cast(g.Data3), + static_cast(g.Data4[0]), + static_cast(g.Data4[1]), + static_cast(g.Data4[2]), + static_cast(g.Data4[3]), + static_cast(g.Data4[4]), + static_cast(g.Data4[5]), + static_cast(g.Data4[6]), + static_cast(g.Data4[7]) + ); + if (written <= 0) return std::wstring(); + return std::wstring(buf); + }; + + std::wstring guidStr = guid_to_wstring(var->VendorGuid); + if (guidStr.empty()) return true; bool isBootIndex = false; if (nameStr.size() == 8 && nameStr.compare(0, 4, L"Boot") == 0) { @@ -9486,7 +9879,7 @@ struct VM { */ [[nodiscard]] static bool edid() { auto decodeManufacturer = [](const BYTE* edid) -> std::string { - const uint16_t word = static_cast((edid[8] << 8) | edid[9]); + const u16 word = static_cast((edid[8] << 8) | edid[9]); char m[4] = { 0, 0, 0, 0 }; const int c1 = (word >> 10) & 0x1F; @@ -9529,19 +9922,19 @@ struct VM { auto getDeviceProperty = [](HDEVINFO devInfo, SP_DEVINFO_DATA& devData, DWORD propId) -> std::string { CHAR buf[512]{}; DWORD needed = 0; - if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, NULL, (PBYTE)buf, sizeof(buf), &needed)) { + if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, (PBYTE)buf, sizeof(buf), &needed)) { return std::string(buf); } if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && needed < 65536) { std::vector big(needed); - if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, NULL, big.data(), (DWORD)big.size(), &needed)) { + if (SetupDiGetDeviceRegistryPropertyA(devInfo, &devData, propId, nullptr, big.data(), (DWORD)big.size(), &needed)) { return std::string(reinterpret_cast(big.data())); } } return std::string(); }; - const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MONITOR, NULL, NULL, DIGCF_PRESENT); + const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MONITOR, nullptr, nullptr, DIGCF_PRESENT); if (devInfo == INVALID_HANDLE_VALUE) { return false; } @@ -9554,7 +9947,7 @@ struct VM { BYTE buffer[2048]; DWORD bufSize = sizeof(buffer); - const LONG rc = RegQueryValueExA(hDevKey, "EDID", NULL, NULL, buffer, &bufSize); + const LONG rc = RegQueryValueExA(hDevKey, "EDID", nullptr, nullptr, buffer, &bufSize); RegCloseKey(hDevKey); if (rc != ERROR_SUCCESS || bufSize < 128) continue; @@ -9594,29 +9987,54 @@ struct VM { * @implements VM::CPU_HEURISTIC */ [[nodiscard]] static bool cpu_heuristic() { - // 1) Check for commonly disabled instructions on patches + using NtAllocateVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG); + using NtProtectVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG); + using NtFreeVirtualMemory_t = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG); + using NtFlushInstructionCache_t = NTSTATUS(__stdcall*)(HANDLE, PVOID, SIZE_T); + // 1) Check for commonly disabled instructions on patches // 1.1 - Test RDPID EAX); C3 (RET) const unsigned char code[] = { 0xF3, 0x0F, 0xC7, 0xF8, 0xC3 }; - void* mem = VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (!mem) { + const HMODULE ntdll = util::get_ntdll(); + if (!ntdll) return false; + + const char* names[] = { "NtAllocateVirtualMemory", "NtProtectVirtualMemory", "NtFlushInstructionCache", "NtFreeVirtualMemory" + }; + void* funcs[ARRAYSIZE(names)] = {}; + util::get_function_address(ntdll, names, funcs, ARRAYSIZE(names)); + + const auto pNtAllocateVirtualMemory = reinterpret_cast(funcs[0]); + const auto pNtProtectVirtualMemory = reinterpret_cast(funcs[1]); + const auto pNtFlushInstructionCache = reinterpret_cast(funcs[2]); + const auto pNtFreeVirtualMemory = reinterpret_cast(funcs[3]); + + if (!pNtAllocateVirtualMemory || !pNtProtectVirtualMemory || !pNtFlushInstructionCache || !pNtFreeVirtualMemory) { return false; } - memcpy(mem, code, sizeof(code)); - - DWORD oldProtect = 0; - if (!VirtualProtect(mem, sizeof(code), PAGE_EXECUTE_READ, &oldProtect)) { - VirtualFree(mem, 0, MEM_RELEASE); + PVOID mem = nullptr; + SIZE_T regionSize = sizeof(code); + PVOID freeBase = nullptr; + SIZE_T freeSize = 0; + NTSTATUS st = pNtAllocateVirtualMemory((HANDLE)-1, &mem, 0, ®ionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st) || !mem) { return false; } - if (!FlushInstructionCache(GetCurrentProcess(), mem, sizeof(code))) { - VirtualFree(mem, 0, MEM_RELEASE); + + memcpy(mem, code, sizeof(code)); + + ULONG oldProtect = 0; + st = pNtProtectVirtualMemory((HANDLE)-1, &mem, ®ionSize, PAGE_EXECUTE_READ, &oldProtect); + if (!NT_SUCCESS(st)) { + freeBase = mem; freeSize = regionSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); return false; } - typedef u64(__fastcall* rdpid_fn_t)(); + pNtFlushInstructionCache((HANDLE)-1, mem, regionSize); + + using rdpid_fn_t = u64(__fastcall*)(); rdpid_fn_t fn = reinterpret_cast(mem); bool ok = true; @@ -9629,7 +10047,8 @@ struct VM { ok = false; } - VirtualFree(mem, 0, MEM_RELEASE); + freeBase = mem; freeSize = regionSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); if (!ok) { debug("CPU_HEURISTIC: Failed to handle RDPID correctly"); @@ -9645,6 +10064,7 @@ struct VM { 0x07,0x06,0x05,0x04, 0x03,0x02,0x01,0x00 }; alignas(16) unsigned char out[16] = { 0 }; + __try { __m128i block = _mm_loadu_si128(reinterpret_cast(plaintext)); __m128i key_vec = _mm_loadu_si128(reinterpret_cast(key)); @@ -9694,8 +10114,8 @@ struct VM { const unsigned char* bytes = nullptr; SIZE_T codeSize = 0; - LPVOID amd_target_mem = NULL; - LPVOID exec_mem = NULL; + LPVOID amd_target_mem = nullptr; + LPVOID exec_mem = nullptr; const bool claimed_amd = cpu::is_amd(); const bool claimed_intel = cpu::is_intel(); @@ -9712,40 +10132,51 @@ struct VM { // one cache line = 64 bytes const SIZE_T targetSize = 64; - amd_target_mem = VirtualAlloc(NULL, targetSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (amd_target_mem == NULL) { - proceed = false; - } - else { - SecureZeroMemory(amd_target_mem, targetSize); - const std::uintptr_t paddr = reinterpret_cast(amd_target_mem); // to avoid sign-extension - const u64 addr = static_cast(paddr); - for (int i = 0; i < 8; ++i) { - amd_bytes[2 + i] = static_cast((addr >> (i * 8)) & 0xFF); + { + PVOID base = nullptr; + SIZE_T sz = targetSize; + NTSTATUS st2 = pNtAllocateVirtualMemory((HANDLE)-1, &base, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st2) || base == nullptr) { + proceed = false; } + else { + amd_target_mem = base; + SecureZeroMemory(amd_target_mem, targetSize); - bytes = amd_bytes; - codeSize = amd_stub_size; + const std::uintptr_t paddr = reinterpret_cast(amd_target_mem); + const u64 addr = static_cast(paddr); + for (int i = 0; i < 8; ++i) { + amd_bytes[2 + i] = static_cast((addr >> (i * 8)) & 0xFF); + } + bytes = amd_bytes; + codeSize = amd_stub_size; + } } if (proceed) { - exec_mem = VirtualAlloc(NULL, codeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (exec_mem == NULL) { + PVOID base = nullptr; + SIZE_T sz = codeSize; + NTSTATUS st2 = pNtAllocateVirtualMemory((HANDLE)-1, &base, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!NT_SUCCESS(st2) || base == nullptr) { proceed = false; } else { + exec_mem = base; memcpy(exec_mem, bytes, codeSize); - DWORD oldProtectExec = 0; - if (!VirtualProtect(exec_mem, codeSize, PAGE_EXECUTE_READ, &oldProtectExec)) { - proceed = false; - } - else if (!FlushInstructionCache(GetCurrentProcess(), exec_mem, codeSize)) { + // change to RX + ULONG oldProt = 0; + PVOID tmpBase = exec_mem; + SIZE_T tmpSz = codeSize; + st2 = pNtProtectVirtualMemory((HANDLE)-1, &tmpBase, &tmpSz, PAGE_EXECUTE_READ, &oldProt); + if (!NT_SUCCESS(st2)) { proceed = false; } else { - typedef void (*CodeFunc)(); + pNtFlushInstructionCache((HANDLE)-1, exec_mem, codeSize); + + using CodeFunc = void(*)(); using RunnerFn = int(*)(CodeFunc); RunnerFn runner = +[](CodeFunc func) -> int { __try { @@ -9771,12 +10202,14 @@ struct VM { } if (exec_mem) { - VirtualFree(exec_mem, 0, MEM_RELEASE); - exec_mem = NULL; + freeBase = exec_mem; freeSize = codeSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); + exec_mem = nullptr; } if (amd_target_mem) { - VirtualFree(amd_target_mem, 0, MEM_RELEASE); - amd_target_mem = NULL; + freeBase = amd_target_mem; freeSize = targetSize; + pNtFreeVirtualMemory((HANDLE)-1, &freeBase, &freeSize, MEM_RELEASE); + amd_target_mem = nullptr; } return spoofed; @@ -11160,7 +11593,7 @@ struct VM { { brands::NULL_BRAND, "Unknown" } }; - auto it = type_table.find(brand_str.c_str()); + const auto it = type_table.find(brand_str.c_str()); if (it != type_table.end()) { return it->second;