Init commit
This commit is contained in:
+487
@@ -0,0 +1,487 @@
|
||||
#include "Common.h"
|
||||
#include "VmDetect.h"
|
||||
|
||||
#include <intrin.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
static std::string ToLowerAscii(const std::string& in)
|
||||
{
|
||||
std::string out = in;
|
||||
|
||||
for (auto& c : out)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c = (char)(c + 32);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string NarrowAscii(const wchar_t* w)
|
||||
{
|
||||
std::string out;
|
||||
|
||||
if (!w)
|
||||
return out;
|
||||
|
||||
for (; *w; ++w)
|
||||
{
|
||||
if (*w < 128)
|
||||
out.push_back((char)*w);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool ContainsAny(const std::string& haystackLower, std::initializer_list<const char*> needles)
|
||||
{
|
||||
for (const char* n : needles)
|
||||
{
|
||||
if (haystackLower.find(n) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CPUID
|
||||
|
||||
struct CpuidResult
|
||||
{
|
||||
bool hypervisorPresent = false;
|
||||
std::string vendor; // raw 12-byte hypervisor vendor
|
||||
std::string brand;
|
||||
};
|
||||
|
||||
static CpuidResult QueryCpuid()
|
||||
{
|
||||
CpuidResult r;
|
||||
|
||||
int regs[4]{};
|
||||
__cpuid(regs, 0);
|
||||
int maxLeaf = regs[0];
|
||||
|
||||
__cpuid(regs, 1);
|
||||
r.hypervisorPresent = (regs[2] & (1 << 31)) != 0;
|
||||
|
||||
if (maxLeaf >= 0x40000000)
|
||||
{
|
||||
int hv[4]{};
|
||||
__cpuid(hv, 0x40000000);
|
||||
|
||||
char vendor[13]{};
|
||||
memcpy(vendor + 0, &hv[1], 4);
|
||||
memcpy(vendor + 4, &hv[2], 4);
|
||||
memcpy(vendor + 8, &hv[3], 4);
|
||||
r.vendor = vendor;
|
||||
|
||||
if (maxLeaf >= 0x40000001)
|
||||
{
|
||||
int b[4]{};
|
||||
__cpuid(b, 0x40000001);
|
||||
|
||||
char brand[13]{};
|
||||
memcpy(brand + 0, &b[0], 4);
|
||||
memcpy(brand + 4, &b[1], 4);
|
||||
memcpy(brand + 8, &b[2], 4);
|
||||
r.brand = brand;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- SMBIOS
|
||||
|
||||
static std::string ReadSmbiosStrings()
|
||||
{
|
||||
std::string collected;
|
||||
|
||||
DWORD size = GetSystemFirmwareTable('RSMB', 0, nullptr, 0);
|
||||
|
||||
if (size < 8 || size > 1024 * 1024)
|
||||
return collected;
|
||||
|
||||
std::vector<BYTE> buf(size);
|
||||
DWORD got = GetSystemFirmwareTable('RSMB', 0, buf.data(), size);
|
||||
|
||||
if (got < 8)
|
||||
return collected;
|
||||
|
||||
const BYTE* data = buf.data();
|
||||
DWORD length = got;
|
||||
|
||||
// Raw SMBIOS table: 8-byte header, then structure table.
|
||||
DWORD tableLen = *(const DWORD*)(data + 4);
|
||||
|
||||
if (tableLen == 0 || tableLen > length - 8)
|
||||
tableLen = length - 8;
|
||||
|
||||
const BYTE* p = data + 8;
|
||||
const BYTE* end = data + 8 + tableLen;
|
||||
|
||||
while (p + 4 <= end)
|
||||
{
|
||||
BYTE structLen = p[1];
|
||||
|
||||
if (structLen < 4 || p + structLen > end)
|
||||
break;
|
||||
|
||||
const BYTE* strings = p + structLen;
|
||||
|
||||
// Strings: NUL-terminated ASCII, double NUL ends the list.
|
||||
while (strings + 1 < end && !(strings[0] == 0 && strings[1] == 0))
|
||||
{
|
||||
const char* s = (const char*)strings;
|
||||
size_t len = strlen(s);
|
||||
|
||||
collected += s;
|
||||
collected += ' ';
|
||||
|
||||
strings += len + 1;
|
||||
}
|
||||
|
||||
p = strings + 2;
|
||||
}
|
||||
|
||||
return collected;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- registry
|
||||
|
||||
static bool RegKeyExists(HKEY root, const wchar_t* subKey)
|
||||
{
|
||||
HKEY h = nullptr;
|
||||
|
||||
if (RegOpenKeyExW(root, subKey, 0, KEY_READ, &h) != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
RegCloseKey(h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string ReadDiskEnumValue()
|
||||
{
|
||||
std::string out;
|
||||
|
||||
HKEY h = nullptr;
|
||||
|
||||
if (RegOpenKeyExW(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum",
|
||||
0,
|
||||
KEY_READ,
|
||||
&h) != ERROR_SUCCESS)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
wchar_t buf[1024]{};
|
||||
DWORD size = sizeof(buf);
|
||||
DWORD type = 0;
|
||||
|
||||
if (RegQueryValueExW(h, L"0", nullptr, &type, (LPBYTE)buf, &size) == ERROR_SUCCESS &&
|
||||
(type == REG_SZ || type == REG_EXPAND_SZ))
|
||||
{
|
||||
out = NarrowAscii(buf);
|
||||
}
|
||||
|
||||
RegCloseKey(h);
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- network
|
||||
|
||||
static bool HasVirtualMac()
|
||||
{
|
||||
// Vendor OUIs that only appear on virtual adapters.
|
||||
static const BYTE oui[][3] =
|
||||
{
|
||||
{ 0x00, 0x0C, 0x29 }, // VMware
|
||||
{ 0x00, 0x50, 0x56 }, // VMware
|
||||
{ 0x00, 0x05, 0x69 }, // VMware
|
||||
{ 0x00, 0x1C, 0x14 }, // VMware
|
||||
{ 0x08, 0x00, 0x27 }, // VirtualBox
|
||||
{ 0x52, 0x54, 0x00 }, // QEMU / KVM
|
||||
{ 0x00, 0x16, 0x3E }, // Xen
|
||||
{ 0x00, 0x15, 0x5D }, // Hyper-V
|
||||
{ 0x00, 0x03, 0xFF }, // Microsoft virtual
|
||||
{ 0x00, 0x1C, 0x42 }, // Parallels
|
||||
};
|
||||
|
||||
ULONG size = 16 * 1024;
|
||||
std::vector<BYTE> buf(size);
|
||||
|
||||
ULONG ret = GetAdaptersInfo((PIP_ADAPTER_INFO)buf.data(), &size);
|
||||
|
||||
if (ret == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
buf.resize(size);
|
||||
ret = GetAdaptersInfo((PIP_ADAPTER_INFO)buf.data(), &size);
|
||||
}
|
||||
|
||||
if (ret != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
for (PIP_ADAPTER_INFO a = (PIP_ADAPTER_INFO)buf.data(); a; a = a->Next)
|
||||
{
|
||||
for (const auto& prefix : oui)
|
||||
{
|
||||
if (a->AddressLength >= 3 &&
|
||||
a->Address[0] == prefix[0] &&
|
||||
a->Address[1] == prefix[1] &&
|
||||
a->Address[2] == prefix[2])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- artifacts
|
||||
|
||||
static bool HasVmServices()
|
||||
{
|
||||
static const wchar_t* services[] =
|
||||
{
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VBoxService",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VBoxGuest",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VBoxMouse",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VBoxSF",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VBoxWddm",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vmci",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vmhgfs",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\VMTools",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vmrawdsk",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vmusbmouse",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vm3dservice",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\vmmouse",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\xenvdb",
|
||||
L"SYSTEM\\CurrentControlSet\\Services\\xenbus",
|
||||
};
|
||||
|
||||
for (const wchar_t* s : services)
|
||||
{
|
||||
if (RegKeyExists(HKEY_LOCAL_MACHINE, s))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasVmDriversOnDisk()
|
||||
{
|
||||
static const wchar_t* files[] =
|
||||
{
|
||||
L"C:\\Windows\\System32\\drivers\\VBoxGuest.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\VBoxMouse.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\VBoxSF.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\vmmouse.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\vmhgfs.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\vmrawdsk.sys",
|
||||
L"C:\\Windows\\System32\\drivers\\vmusbmouse.sys",
|
||||
L"C:\\Windows\\System32\\VBoxService.exe",
|
||||
L"C:\\Windows\\System32\\VBoxTray.exe",
|
||||
};
|
||||
|
||||
for (const wchar_t* f : files)
|
||||
{
|
||||
DWORD attr = GetFileAttributesW(f);
|
||||
|
||||
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasVmProcesses()
|
||||
{
|
||||
static const wchar_t* names[] =
|
||||
{
|
||||
L"VBoxService.exe",
|
||||
L"VBoxTray.exe",
|
||||
L"vmtoolsd.exe",
|
||||
L"vmwaretray.exe",
|
||||
L"vmwareuser.exe",
|
||||
L"VGAuthService.exe",
|
||||
L"qemu-ga.exe",
|
||||
L"xenservice.exe",
|
||||
};
|
||||
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (snap == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
PROCESSENTRY32W pe{};
|
||||
pe.dwSize = sizeof(pe);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (Process32First(snap, &pe))
|
||||
{
|
||||
do
|
||||
{
|
||||
for (const wchar_t* n : names)
|
||||
{
|
||||
if (_wcsicmp(pe.szExeFile, n) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
} while (Process32Next(snap, &pe));
|
||||
}
|
||||
|
||||
CloseHandle(snap);
|
||||
return found;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- public
|
||||
|
||||
bool IsVirtualMachine(std::wstring* reason)
|
||||
{
|
||||
auto setReason = [reason](const wchar_t* text)
|
||||
{
|
||||
if (reason)
|
||||
*reason = text;
|
||||
};
|
||||
|
||||
if (reason)
|
||||
reason->clear();
|
||||
|
||||
CpuidResult cpu = QueryCpuid();
|
||||
|
||||
// A hypervisor bit alone is not proof: VBS / Credential Guard / Hyper-V
|
||||
// on bare metal sets it too. Strong vendors are decisive.
|
||||
if (cpu.hypervisorPresent)
|
||||
{
|
||||
const std::string vendor = cpu.vendor;
|
||||
|
||||
if (vendor == "VMwareVMware") { setReason(L"cpuid: VMware"); return true; }
|
||||
if (vendor == "VBoxVBoxVBox") { setReason(L"cpuid: VirtualBox"); return true; }
|
||||
if (vendor == "KVMKVMKVM") { setReason(L"cpuid: KVM"); return true; }
|
||||
if (vendor == "XenVMMXenVMM") { setReason(L"cpuid: Xen"); return true; }
|
||||
if (vendor.rfind("TCGTCGTCG", 0) == 0) { setReason(L"cpuid: QEMU TCG"); return true; }
|
||||
if (vendor == "bhyve bhyve") { setReason(L"cpuid: bhyve"); return true; }
|
||||
if (vendor == "VServerVServer") { setReason(L"cpuid: Virtual Server"); return true; }
|
||||
}
|
||||
|
||||
const std::string smbios = ToLowerAscii(ReadSmbiosStrings());
|
||||
|
||||
if (ContainsAny(smbios, { "virtualbox", "vmware", "qemu", "kvm", "bochs", "parallels", "xen", "virtual machine", "hyper-v" }))
|
||||
{
|
||||
setReason(L"smbios: virtual vendor string");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cpu.hypervisorPresent && cpu.vendor == "Microsoft Hv")
|
||||
{
|
||||
// Hyper-V guest: confirmed by SMBIOS containing "virtual machine"
|
||||
// (handled above) or by Hyper-V integration services.
|
||||
if (RegKeyExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\vmicheartbeat"))
|
||||
{
|
||||
setReason(L"hyperv: integration services");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasVirtualMac())
|
||||
{
|
||||
setReason(L"hyperv: virtual MAC");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string diskEnum = ToLowerAscii(ReadDiskEnumValue());
|
||||
|
||||
if (ContainsAny(diskEnum, { "vbox", "vmware", "qemu", "xen", "virtio" }))
|
||||
{
|
||||
setReason(L"disk enum: virtual controller");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasVmServices())
|
||||
{
|
||||
setReason(L"registry: guest service installed");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasVmDriversOnDisk())
|
||||
{
|
||||
setReason(L"driver: guest driver present");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasVmProcesses())
|
||||
{
|
||||
setReason(L"process: guest helper running");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasVirtualMac())
|
||||
{
|
||||
setReason(L"network: virtual MAC OUI");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef ARDRV_ALLOW_VM_DEFAULT
|
||||
#define ARDRV_ALLOW_VM_DEFAULT 0
|
||||
#endif
|
||||
|
||||
bool VmOverrideRequested(int argc, wchar_t** argv)
|
||||
{
|
||||
#if ARDRV_ALLOW_VM_DEFAULT
|
||||
// Built for lab/testing images that are themselves virtual machines.
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return true;
|
||||
#endif
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (argv[i] && (_wcsicmp(argv[i], L"--allow-vm") == 0 ||
|
||||
_wcsicmp(argv[i], L"-allow-vm") == 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t env[64]{};
|
||||
DWORD n = GetEnvironmentVariableW(L"ARDRV_ALLOW_VM", env, _countof(env));
|
||||
|
||||
if (n > 0 && n < _countof(env) && (env[0] == L'1' || env[0] == L'y' || env[0] == L'Y'))
|
||||
return true;
|
||||
|
||||
HKEY h = nullptr;
|
||||
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\ArDrvLoader", 0, KEY_READ, &h) == ERROR_SUCCESS)
|
||||
{
|
||||
DWORD value = 0;
|
||||
DWORD size = sizeof(value);
|
||||
DWORD type = 0;
|
||||
|
||||
bool enabled =
|
||||
RegQueryValueExW(h, L"AllowVm", nullptr, &type, (LPBYTE)&value, &size) == ERROR_SUCCESS &&
|
||||
type == REG_DWORD &&
|
||||
value != 0;
|
||||
|
||||
RegCloseKey(h);
|
||||
|
||||
if (enabled)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user