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;
}
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
这里是这个函数中第一次使用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;
}