检测软件何时在 VMware 虚拟机中运行依赖于两种机制:
- 测试 CPUID 虚拟机监控程序当前位
- 测试虚拟 BIOS DMI 信息和虚拟机监控程序端口
测试 CPUID 虚拟机监控程序当前位
Intel 和 AMD CPU 已将 CPUID 叶0x1的 ECX 的第 31 位保留为虚拟机管理程序的当前位。此位允许虚拟机监控程序向客户机操作系统指示其存在。虚拟机管理程序设置此位,物理 CPU(所有现有和将来的 CPU)将此位设置为零。客户机操作系统可以测试位 31 以检测它们是否在虚拟机内运行。
英特尔和AMD还保留了CPUID 0x40000000-0x400000FF供软件使用。虚拟机管理程序可以使用这些叶子来提供一个接口,用于将信息从虚拟机管理程序传递到虚拟机内运行的客户机操作系统。虚拟机监控程序位指示虚拟机管理程序的存在,并且测试这些附加软件叶是安全的。VMware 将0x40000000叶定义为虚拟机管理程序 CPUID 信息叶。在 VMware 虚拟机监控程序上运行的代码可以测试虚拟机监控程序签名的 CPUID 信息叶。VMware 将字符串“VMwareVMware”存储在 CPUID 叶0x40000000的 EBX、ECX、EDX 中。
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
int cpuid_check() { unsigned int eax, ebx, ecx, edx; char hyper_vendor_id[13]; cpuid(0x1, &eax, &ebx, ecx, edx); if (bit 31 of ecx is set) { cpuid(0x40000000, &eax, &ebx, ecx, edx); memcpy(hyper_vendor_id + 0, &ebx, 4); memcpy(hyper_vendor_id + 4, &ecx, 4); memcpy(hyper_vendor_id + 8, &edx, 4); hyper_vendor_id[12] = '\0'; if (!strcmp(hyper_vendor_id, "VMwareVMware")) return 1; // Success - running under VMware } return 0; } |
测试虚拟 BIOS DMI 信息和虚拟机监控程序端口
除了用于 VMware 虚拟机检测的基于 CPUID 的方法外,VMware 还出于以下原因提供了回退机制:
- 当 VT/AMD-V 不可用或未启用时,这种基于 CPUID 的技术将不适用于在 CPL3 上运行的客户机代码。
- 虚拟机管理程序当前位和虚拟机管理程序信息叶仅针对基于 VMware 硬件版本 7 的产品定义。
虚拟 BIOS DMI 信息
VMware 虚拟 BIOS 具有许多特定于 VMware 的标识符,程序可以使用这些标识符来检测虚拟机管理程序。对于 DMI 字符串检查,请使用 BIOS 序列号并检查字符串“VMware-”或“VMW”(对于在 Fusion 上运行的 Mac OS X 客户机)。
示例代码
1 2 3 4 5 6 7 8 |
int dmi_check(void) { char string[10]; GET_BIOS_SERIAL(string); if (!memcmp(string, "VMware-", 7) || !memcmp(string, "VMW", 3)) return 1; // DMI contains VMware specific string. else return 0; } |
仅执行 DMI 检查是不够的,因为 BIOS 的序列号可能偶然包含字符串“VMware-”或“VMW”。您还应该测试虚拟机监控程序端口。
虚拟机监控程序端口
VMware 实现了一个 I/O 端口,程序可以查询该端口以检测软件是否在 VMware 虚拟机管理程序中运行。此虚拟机监控程序端口的行为会根据某些寄存器中的幻数值而有所不同,并修改某些寄存器作为副作用。通过对端口 0x5658(VMware 虚拟机管理程序端口)执行 IN 操作来检测 VMware 虚拟机管理程序。
在
端口0x5658上执行 IN 时 eax = 0x564D5868 (VMware 虚拟机管理程序幻值)
ebx = 0xFFFFFFFF (UINT_MAX)
ecx = 10 (Getversion 命令标识符)
edx = 0x5658 (虚拟机监控程序端口号)
在 VMware 上,此操作会将寄存器 ebx 的值修改为 0x564D5868(VMware 虚拟机管理程序幻值)。
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868 #define VMWARE_HYPERVISOR_PORT 0x5658 #define VMWARE_PORT_CMD_GETVERSION 10 #define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \ __asm__("inl (%%dx)" : \ "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \ "0"(VMWARE_HYPERVISOR_MAGIC), \ "1"(VMWARE_PORT_CMD_##cmd), \ "2"(VMWARE_HYPERVISOR_PORT), "3"(UINT_MAX) : \ "memory"); int hypervisor_port_check(void) { uint32_t eax, ebx, ecx, edx; VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx); if (ebx == VMWARE_HYPERVISOR_MAGIC) return 1; // Success - running under VMware else return 0; } |
尽管此端口位于 x86 ISA 空间之外,但物理系统的设备可能使用与 VMware 虚拟机监控程序端口相同的端口号。访问此类系统上的物理设备端口可能会对该设备产生不确定的影响。为防止出现这种情况,请在查询虚拟机管理程序端口之前测试虚拟 BIOS 信息。
推荐代码
1 2 3 4 5 6 7 8 9 10 |
int Detect_VMware(void) { if (cpuid_check()) return 1; // Success running under VMware. else if (dmi_check() && hypervisor_port_check()) return 1; return 0; } |