Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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% | | | |
Expand Down
116 changes: 68 additions & 48 deletions src/vmaware.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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) {
Expand All @@ -3281,23 +3282,26 @@ struct VM {
return false;
}

constexpr std::array<const char*, 29> 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<const char*, 12> 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<const char*, 17> 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",
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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
}


Expand Down Expand Up @@ -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++;
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -7463,7 +7477,6 @@ struct VM {
#if (!WINDOWS)
return false;
#else

typedef struct _SYSTEM_MODULE_INFORMATION {
PVOID Reserved[2];
PVOID ImageBaseAddress;
Expand Down Expand Up @@ -7545,6 +7558,8 @@ struct VM {
strstr(driverPath, "VBoxMouse") ||
strstr(driverPath, "VBoxSF")
) {
debug("DRIVER_NAMES: Detected VBox driver: ", driverPath);
ntFreeVirtualMemory(hProcess, &allocatedMemory, &regionSize, MEM_RELEASE);
return core::add(brands::VBOX);
}

Expand All @@ -7553,6 +7568,8 @@ struct VM {
strstr(driverPath, "vmmouse") ||
strstr(driverPath, "vmmemctl")
) {
debug("DRIVER_NAMES: Detected VMware driver: ", driverPath);
ntFreeVirtualMemory(hProcess, &allocatedMemory, &regionSize, MEM_RELEASE);
return core::add(brands::VMWARE);
}
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -8674,7 +8694,6 @@ struct VM {
if (pNtQuerySystemInformation) {
SYSTEM_HYPERVISOR_DETAIL_INFORMATION hvInfo = { {} };
const NTSTATUS status = pNtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(0x9F), &hvInfo, sizeof(hvInfo), nullptr);

if (status != 0) {
return false;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -11668,7 +11688,7 @@ std::pair<VM::enum_flags, VM::core::technique> 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 } },
Expand Down