From d8f6602b4bde097f0a5a2d83f22c003a05a73cb6 Mon Sep 17 00:00:00 2001 From: Requiem Date: Mon, 10 Mar 2025 03:52:02 +0100 Subject: [PATCH] Improved debug analysis --- docs/documentation.md | 2 +- src/vmaware.hpp | 116 +++++++++++++++++++++++++----------------- 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index 9c6091bc..8d5bf1ab 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -404,7 +404,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f | `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | | 50% | | | | | | | `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | | 100% | | | | | | `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | | 75% | | | | | -| `VM::TIMER` | Check for timing anomalies in the system | | 20% | | | | | | +| `VM::TIMER` | Check for timing anomalies in the system | | 45% | | | | | | | `VM::THREADCOUNT` | 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% | | | | | | `VM::MAC` | Check if mac address starts with certain VM designated values | Linux and Windows | 20% | | | | | | `VM::TEMPERATURE` | Check if thermal directory in linux is present, might not be present in VMs | Linux | 15% | | | | diff --git a/src/vmaware.hpp b/src/vmaware.hpp index 58bc8460..d5c80b31 100644 --- a/src/vmaware.hpp +++ b/src/vmaware.hpp @@ -3052,6 +3052,7 @@ struct VM { for (const auto& check : dll_checks) { if (GetModuleHandleA(check.dll_name) != nullptr) { + debug("DLL: Found ", check.dll_name, " (", check.brand, ")"); return core::add(check.brand); } } @@ -3272,7 +3273,7 @@ struct VM { wow64RedirectDisabled = true; } - // Use the system directory instead of the Windows directory. + // System directory instead of the Windows directory char szSysDir[MAX_PATH] = { 0 }; if (GetSystemDirectoryA(szSysDir, MAX_PATH) == 0) { if (wow64RedirectDisabled) { @@ -3281,23 +3282,26 @@ struct VM { return false; } - constexpr std::array vbox_and_vmware = { { - "drivers\\Vmmouse.sys", - "drivers\\Vmusbmouse.sys", - "drivers\\vm3dgl.dll", - "drivers\\vmdum.dll", - "drivers\\VmGuestLibJava.dll", - "drivers\\vm3dver.dll", - "drivers\\vmtray.dll", - "drivers\\VMToolsHook.dll", - "drivers\\vmGuestLib.dll", - "drivers\\vmhgfs.dll", + constexpr std::array vmwareFiles = { { + "drivers\\Vmmouse.sys", + "drivers\\Vmusbmouse.sys", + "drivers\\vm3dgl.dll", + "drivers\\vmdum.dll", + "drivers\\VmGuestLibJava.dll", + "drivers\\vm3dver.dll", + "drivers\\vmtray.dll", + "drivers\\VMToolsHook.dll", + "drivers\\vmGuestLib.dll", + "drivers\\vmhgfs.dll", + "vm3dum64_loader.dll", + "vm3dum64_10.dll" + } }; + + constexpr std::array vboxFiles = { { "drivers\\VBoxMouse.sys", "drivers\\VBoxGuest.sys", "drivers\\VBoxSF.sys", "drivers\\VBoxVideo.sys", - "vm3dum64_loader.dll", - "vm3dum64_10.dll", "vboxoglpackspu.dll", "vboxoglpassthroughspu.dll", "vboxservice.exe", @@ -3341,25 +3345,21 @@ struct VM { "drivers\\vpc-s3.sys" } }; - u8 vbox = 0, vmware = 0, kvm = 0, vpc = 0, parallels = 0; - - auto file_exists = [](const char* path) -> bool { - DWORD attrs = GetFileAttributesA(path); - return (attrs != INVALID_FILE_ATTRIBUTES) && !(attrs & FILE_ATTRIBUTE_DIRECTORY); - }; + u8 vmware = 0, vbox = 0, kvm = 0, vpc = 0, parallels = 0; auto checkFiles = [&](const auto& files, u8& count) { for (const auto& relativePath : files) { char szPath[MAX_PATH] = { 0 }; - // system directory + relative path, so for example: C:\Windows\System32\ + drivers\VBoxMouse.sys + // Combination of the system directory with the relative path PathCombineA(szPath, szSysDir, relativePath); - if (file_exists(szPath)) { + if (util::exists(szPath)) { count++; } } }; - checkFiles(vbox_and_vmware, vbox); + checkFiles(vmwareFiles, vmware); + checkFiles(vboxFiles, vbox); checkFiles(kvmFiles, kvm); checkFiles(parallelsFiles, parallels); checkFiles(vpcFiles, vpc); @@ -3368,28 +3368,30 @@ struct VM { Wow64RevertWow64FsRedirection(&oldValue); } - if (file_exists("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\agent.pyw")) { + if (util::exists("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\agent.pyw")) { + debug("VM Files: Found startup agent (agent.pyw), indicating CUCKOO VM."); return core::add(brands::CUCKOO); } - if (vbox > vmware && vbox > kvm && vbox > vpc && vbox > parallels) { + debug("VM Files: Detected VBox files with count ", vbox); return core::add(brands::VBOX); } if (vmware > vbox && vmware > kvm && vmware > vpc && vmware > parallels) { + debug("VM Files: Detected VMware files with count ", vmware); return core::add(brands::VMWARE); } if (kvm > vbox && kvm > vmware && kvm > vpc && kvm > parallels) { + debug("VM Files: Detected KVM files with count ", kvm); return core::add(brands::KVM); } if (vpc > vbox && vpc > vmware && vpc > kvm && vpc > parallels) { + debug("VM Files: Detected VPC files with count ", vpc); return core::add(brands::VPC); } if (parallels > vbox && parallels > vmware && parallels > kvm && parallels > vpc) { + debug("VM Files: Detected Parallels files with count ", parallels); return core::add(brands::PARALLELS); } - if (vbox > 0 && vmware > 0 && kvm > 0 && vpc > 0 && parallels > 0) { - return true; - } return false; #endif @@ -3808,39 +3810,50 @@ struct VM { * @implements VM::VM_PROCESSES */ [[nodiscard]] static bool vm_processes() { -#if (!WINDOWS) + #if (!WINDOWS) return false; -#else - if (util::is_proc_running(("joeboxserver.exe")) || util::is_proc_running(("joeboxcontrol.exe"))) { + #else + if (util::is_proc_running("joeboxserver.exe") || util::is_proc_running("joeboxcontrol.exe")) { + debug("VM_PROCESSES: Detected JoeBox process."); return core::add(brands::JOEBOX); } - if (util::is_proc_running(("prl_cc.exe")) || util::is_proc_running(("prl_tools.exe"))) { + if (util::is_proc_running("prl_cc.exe") || util::is_proc_running("prl_tools.exe")) { + debug("VM_PROCESSES: Detected Parallels process."); return core::add(brands::PARALLELS); } - if (util::is_proc_running(("vboxservice.exe")) || util::is_proc_running(("vboxtray.exe"))) { + if (util::is_proc_running("vboxservice.exe") || util::is_proc_running("vboxtray.exe")) { + debug("VM_PROCESSES: Detected VBox process."); return core::add(brands::VBOX); } - if (util::is_proc_running(("vmsrvc.exe")) || util::is_proc_running(("vmusrvc.exe"))) { + if (util::is_proc_running("vmsrvc.exe") || util::is_proc_running("vmusrvc.exe")) { + debug("VM_PROCESSES: Detected VPC process."); return core::add(brands::VPC); } - if (util::is_proc_running(("xenservice.exe")) || util::is_proc_running(("xsvc_depriv.exe"))) { + if (util::is_proc_running("xenservice.exe") || util::is_proc_running("xsvc_depriv.exe")) { + debug("VM_PROCESSES: Detected Xen process."); return core::add(brands::XEN); } - if (util::is_proc_running(("vm3dservice.exe")) || util::is_proc_running(("VGAuthService.exe")) || util::is_proc_running(("vmtoolsd.exe"))) { + if (util::is_proc_running("vm3dservice.exe") || + util::is_proc_running("VGAuthService.exe") || + util::is_proc_running("vmtoolsd.exe")) { + debug("VM_PROCESSES: Detected VMware process."); return core::add(brands::VMWARE); } - if (util::is_proc_running(("vdagent.exe")) || util::is_proc_running(("vdservice.exe")) || util::is_proc_running(("qemuwmi.exe"))) { + if (util::is_proc_running("vdagent.exe") || + util::is_proc_running("vdservice.exe") || + util::is_proc_running("qemuwmi.exe")) { + debug("VM_PROCESSES: Detected QEMU process."); return core::add(brands::QEMU); } return false; -#endif + #endif } @@ -4385,6 +4398,7 @@ struct VM { if (dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ) { buffer[bufferSize - 1] = '\0'; if (strstr(buffer, comp_string) != nullptr) { + debug("HKLM_REGISTRIES: Found ", comp_string, " in ", subKey, " for brand ", p_brand); core::add(p_brand); count++; } @@ -6595,20 +6609,20 @@ struct VM { } if (lowestReservedAddrRangeEnd != lowestLoaderReservedAddrRangeEnd) { - return false; // No VM detected + return false; } - /* Now check for Hyper-V or VirtualBox by memory ranges */ + /* Hyper-V and VirtualBox by memory ranges */ for (DWORD i = 0; i < phys_count; i++) { if (phys[i].address == HYPERV_PHYS_LO && (phys[i].address + phys[i].size) == HYPERV_PHYS_HI) { - return true; // Detected Hyper-V + return core::add(VM::brands::HYPERV); } if (phys[i].address == VBOX_PHYS_LO && (phys[i].address + phys[i].size) == VBOX_PHYS_HI) { - return true; // Detected VirtualBox + return core::add(VM::brands::VBOX); } } - return false; // Possibly VM, but unable to identify platform + return false; #endif } @@ -7463,7 +7477,6 @@ struct VM { #if (!WINDOWS) return false; #else - typedef struct _SYSTEM_MODULE_INFORMATION { PVOID Reserved[2]; PVOID ImageBaseAddress; @@ -7545,6 +7558,8 @@ struct VM { strstr(driverPath, "VBoxMouse") || strstr(driverPath, "VBoxSF") ) { + debug("DRIVER_NAMES: Detected VBox driver: ", driverPath); + ntFreeVirtualMemory(hProcess, &allocatedMemory, ®ionSize, MEM_RELEASE); return core::add(brands::VBOX); } @@ -7553,6 +7568,8 @@ struct VM { strstr(driverPath, "vmmouse") || strstr(driverPath, "vmmemctl") ) { + debug("DRIVER_NAMES: Detected VMware driver: ", driverPath); + ntFreeVirtualMemory(hProcess, &allocatedMemory, ®ionSize, MEM_RELEASE); return core::add(brands::VMWARE); } } @@ -7889,15 +7906,18 @@ struct VM { CloseHandle(handle4); if (result) { + debug("VM_DEVICES: Detected VBox related device handles."); return core::add(brands::VBOX); } if (handle5 != INVALID_HANDLE_VALUE) { CloseHandle(handle5); + debug("VM_DEVICES: Detected VMware related device (HGFS)."); return core::add(brands::VMWARE); } if (handle6 != INVALID_HANDLE_VALUE) { CloseHandle(handle6); + debug("VM_DEVICES: Detected Cuckoo related device (pipe)."); return core::add(brands::CUCKOO); } @@ -8470,7 +8490,7 @@ struct VM { QueryPerformanceCounter(&endQPC); LONGLONG dummyTime = endQPC.QuadPart - startQPC.QuadPart; - const bool qpc_check = (dummyTime != 0) && ((cpuIdTime / dummyTime) > 1100); + const bool qpc_check = (dummyTime != 0) && ((cpuIdTime / dummyTime) > 1500); debug("QPC check - CPUID: ", cpuIdTime, "ns, Dummy: ", dummyTime, "ns, Ratio: ", (cpuIdTime / dummyTime)); // TSC sync check across cores. Try reading the invariant TSC on two different cores to attempt to detect vCPU timers being shared @@ -8674,7 +8694,6 @@ struct VM { if (pNtQuerySystemInformation) { SYSTEM_HYPERVISOR_DETAIL_INFORMATION hvInfo = { {} }; const NTSTATUS status = pNtQuerySystemInformation(static_cast(0x9F), &hvInfo, sizeof(hvInfo), nullptr); - if (status != 0) { return false; } @@ -8975,6 +8994,7 @@ struct VM { for (size_t j = 0; j < badPoolCount; ++j) { if (currentTag == BadPoolDwords[j]) { + debug("Bad Pools: Detected bad pool tag: 0x", std::hex, currentTag, std::dec); ++bad_pool_number; break; } @@ -9814,7 +9834,6 @@ struct VM { if (!ntqsi) return false; - // List of target strings (all are now treated as valid hits) constexpr const char* targets[] = { "Parallels Software International", "Parallels(R)", "innotek", "Oracle", "VirtualBox", "VS2005R2", "VMware, Inc.", @@ -10068,6 +10087,7 @@ struct VM { } } + debug("UNKNOWN_MANUFACTURER: CPU brand '", brand, "' did not match known vendor IDs."); return true; // no known manufacturer matched, likely a VM } @@ -11668,7 +11688,7 @@ std::pair VM::core::technique_list[] = { { VM::CPU_BRAND, { 50, VM::cpu_brand } }, { VM::HYPERVISOR_BIT, { 100, VM::hypervisor_bit}} , { VM::HYPERVISOR_STR, { 75, VM::hypervisor_str } }, - { VM::TIMER, { 20, VM::timer } }, + { VM::TIMER, { 45, VM::timer } }, { VM::THREADCOUNT, { 35, VM::thread_count } }, { VM::MAC, { 20, VM::mac_address_check } }, { VM::TEMPERATURE, { 15, VM::temperature } },