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

18
Windows 10任务管理器(taskmgr.exe)可以知道它是否正在物理机器或虚拟机上运行。如果您查看“性能”选项卡,您会注意到处理器数量标签的文字要么是“逻辑处理器:”,要么是“虚拟处理器:”。此外,如果在虚拟机中运行,则还有一个标签“虚拟机:是”。请参见以下两个屏幕截图:

taskmgr locical processors

taskmgr virtual processors

我的问题是,任务管理器是否使用了文档化的API调用来进行此类检测?
我简短地查看了反汇编代码,似乎检测代码与GetLogicalProcessorInformationEx和/或IsProcessorFeaturePresent和/或NtQuerySystemInformation有关。
然而,我不知道如何做到这一点(至少不花费更多时间分析汇编代码)。
此外:我认为这个问题与其他现有的问题不相关,例如“如何检测我的程序是否在虚拟机内运行?”因为我没有看到任何代码尝试将smbios表字符串或CPU供应商字符串与已知的针对虚拟机监控程序(“qemu”,“virtualbox”,“vmware”)的字符串进行比较。我不能排除低级API实现会这样做,但我在taskmgr.exe中没有看到这种代码。
更新:我也可以排除taskmgr.exe使用CPUID指令(使用EAX=1并检查ECX中的hypervisor位31)来检测矩阵。

更新:仔细查看反汇编代码后发现确实有一个检查位31的操作,只是没有明显地展示出来。

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


@CodeCaster 我不这么认为。至少我没有在代码中看到任何提示,表明它试图将smbios表数据或供应商字符串与已知的虚拟化字符串(qemu、vmware等)进行匹配。 - gollum
1
Taskmgr使用CPUID在函数**__int64 WdcMemoryMonitor :: CheckVirtualStatus()**中来检查状态。 - magicandre1981
1
@magicandre1981 是的和不是。CheckVirtualStatus()使用CPUID进行多个检测任务(例如签名、制造商ID字符串),但仅在EAX=1的情况下使用一次,并且在这种情况下,只分析ECX的第5位以检查VMX扩展。而缺少VMX标志并不是虚拟机的合适指示器。 - gollum
好的,我只是用IDA简单地查看了一下,并没有检查EAX值代表什么。 - magicandre1981
@magicandre1981 不过你是对的。我错过了它,因为我集中精力于eax位移,没有注意到该函数中第一次使用CPUID后跟随着一个jns测试ecx,ecx,这与测试31位相同 ;) - gollum
1个回答

28
我已经分析了来自Windows 10 1803 (OS Build 17134.165)的x64 taskmgr.exe,通过追溯写入内存位置来确定在设置“Virtual machine: Yes”标签的点处所查询的变量值。
该变量值的负责人是函数WdcMemoryMonitor::CheckVirtualStatus的返回代码。
以下是该函数中第一次使用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

所以,任务管理器并没有使用任何硬件串、MAC地址或其他复杂技术,而只是检查CPUID(叶0x01 ECX位31)是否设置了hypervisor位。
当然,结果是虚假的。例如,将-hypervisor添加到qemu的cpu参数中会禁用hypervisor 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;
}

3
好的!这里是来自VMWare的文档,描述了叶子0x01的ECX位31:https://kb.vmware.com/s/article/1009458 - 正好就像你发现的那样。 - Petr
我想知道这个值是否可以从WMI(Windows Management Instrumentation)存储库中找到/读取。我四处看了一下,但没有找到它。 - Sam Sirry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接