Windows 10 任务管理器如何检测虚拟机?

How does Windows 10 task manager detect a virtual machine?

Windows 10 任务管理器 (taskmgr.exe) 知道它是 运行 在物理机还是虚拟机上。

如果您查看 性能 选项卡,您会注意到处理器标签的数量显示为 逻辑处理器:虚拟处理器:

另外,如果运行在一个虚拟机里面,还有标签虚拟机:是

请看下面两张截图:

我的问题是是否有记录的 API 调用 taskmgr 用于进行此类检测?

我粗略地看了一眼反汇编,检测代码似乎与 GetLogicalProcessorInformationEx and/or IsProcessorFeaturePresent and/or NtQuerySystemInformation.

有某种关联

但是,我不知道如何(至少在不花更多时间分析汇编代码的情况下)。

并且:这个问题是 IMO 与其他现有问题无关,例如 我如何检测我的程序是否在虚拟机中 运行? 因为我没有查看任何试图将 smbios table 字符串或 cpu 供应商字符串与现有已知的典型管理程序字符串("qemu"、"virtualbox"、"vmware")进行比较的代码。我不排除较低级别的 API 实现可以做到这一点,但我没有在 taskmgr.exe.

中看到这种代码

更新:我也可以排除 taskmgr.exe 正在使用 CPUID 指令(使用 EAX=1 并检查 hypervisor ECX 中的第 31 位)来检测矩阵。

更新:仔细查看反汇编表明确实检查了位 31,只是显然没有检查。

下面我会自己回答这个问题。

我分析了来自 Windows 10 1803(OS Build 17134.165)的 x64 taskmgr.exe,方法是追溯写入到在虚拟 machine: 是 标签已设置。

负责该变量值的是函数 WdcMemoryMonitor::CheckVirtualStatus

的 return 代码

这里是这个函数中第一次使用cpuid指令的反汇编:

lea     eax, [rdi+1]                 // results in eax set to 1
cpuid
mov     dword ptr [rbp+var_2C], ebx  // save CPUID feature bits for later use
test    ecx, ecx
jns     short loc_7FF61E3892DA       // negative value check equals check for bit 31
...
return 1
loc_7FF61E3892DA:
// different feature detection code if hypervisor bit is not set

因此 taskmgr 没有使用任何硬件字符串、mac 地址或其他一些复杂的技术,而只是检查 hypervisor 位(CPUID 叶 0x01 ECX 位 31)) 已设置。

结果当然是假的,因为例如将 -hypervisor 添加到 qemu 的 cpu 参数会禁用管理程序 cpuid 标志,这会导致任务管理器不显示 Virtual machine: yes没有了。

最后是一些示例代码(在 Windows 和 Linux 上测试),它完美地模仿了 Windows 任务管理器的测试:

#include <stdio.h>

#ifdef _WIN32
#include <intrin.h>
#else
#include <cpuid.h>
#endif

int isHypervisor(void)
{
#ifdef _WIN32
    int cpuinfo[4];
    __cpuid(cpuinfo, 1);
    if (cpuinfo[2] >> 31 & 1)
        return 1;
#else
    unsigned int eax, ebx, ecx, edx;
    __get_cpuid (1, &eax, &ebx, &ecx, &edx);
    if (ecx >> 31 & 1)
        return 1;
#endif
    return 0;
}

int main(int argc, char **argv)
{
    if (isHypervisor())
        printf("Virtual machine: yes\n");
    else
        printf("Virtual machine: no\n"); /* actually "maybe */

    return 0;
}