在 Windows 上检查虚拟化功能时,最容易犯的错误,是把 VT-x、VT-d、SVM 和 IOMMU 当成同一个开关。
它们其实分成两组:
| CPU 平台 | CPU 虚拟化 | I/O 虚拟化 |
|---|---|---|
| Intel | VT-x / VMX | VT-d / DMA Remapping |
| AMD | AMD-V / SVM | AMD-Vi / IOMMU |
VT-x 和 SVM 负责让处理器高效运行虚拟机;VT-d 和 AMD IOMMU 负责限制、转换 PCIe 等设备发起的 DMA 内存访问。前者主要属于 CPU 执行能力,后者是处理器、芯片组、主板固件和操作系统共同提供的平台能力。
因此,可靠的检测不能只回答一个“开”或“关”,而应该区分:
- 硬件是否支持;
- BIOS/UEFI 是否已经开放给操作系统;
- Windows 是否已经在使用这项能力。
一、最快的图形界面检查
1. 用任务管理器检查 VT-x 或 SVM
按 Ctrl + Shift + Esc 打开任务管理器,进入:
性能 → CPU → 虚拟化
如果显示“已启用”,通常表示当前物理机的 Intel VT-x 或 AMD SVM 已在 BIOS/UEFI 中开启,并且 Windows 能够使用。
但任务管理器的“虚拟化”不代表 VT-d 或 AMD IOMMU。它主要回答的是 CPU 虚拟化扩展是否可用。
2. 用系统信息检查 IOMMU 的实际使用迹象
按 Win + R,运行:
msinfo32
在“系统摘要”中关注两项:
- “固件中已启用虚拟化”:对应 VT-x 或 SVM;
- “内核 DMA 保护”:与 VT-d/AMD IOMMU 的实际使用有关。
微软说明,Windows 的内核 DMA 保护利用系统 IOMMU 对外部 DMA 设备进行隔离,并可以在 msinfo32.exe 中检查状态。Microsoft Learn:Kernel DMA Protection
这里要特别注意:
“内核 DMA 保护 = 开启”是 Windows 正在利用 IOMMU 的强证据;但“关闭”不能单独证明 VT-d/IOMMU 一定在 BIOS 中关闭。
它还会受到平台拓扑、ACPI 标志、设备范围、驱动兼容性和 Windows 版本等条件影响。
二、用 PowerShell 检查 VT-x 或 SVM
以普通权限打开 PowerShell,运行:
$cpu = Get-CimInstance Win32_Processor
$computer = Get-CimInstance Win32_ComputerSystem
[pscustomobject]@{
CPU名称 = ($cpu.Name -join '; ')
支持虚拟机监控扩展 = ($cpu.VMMonitorModeExtensions -contains $true)
固件已开启CPU虚拟化 = ($cpu.VirtualizationFirmwareEnabled -contains $true)
支持二级地址转换 = ($cpu.SecondLevelAddressTranslationExtensions -contains $true)
已检测到Hypervisor = [bool]$computer.HypervisorPresent
}
各字段的含义如下:
VMMonitorModeExtensions:CPU 是否支持 Intel VMX 或 AMD SVM;VirtualizationFirmwareEnabled:固件是否已启用 CPU 虚拟化扩展;SecondLevelAddressTranslationExtensions:是否支持 SLAT,也就是 Intel EPT 或 AMD NPT;HypervisorPresent:Windows 当前是否检测到正在运行的 Hypervisor。
微软对 Win32_Processor.VirtualizationFirmwareEnabled 的定义就是“固件已启用虚拟化扩展”。这些属性从 Windows 8 / Windows Server 2012 起才受支持。Microsoft Learn:Win32_Processor
不要把 SecondLevelAddressTranslationExtensions 当成 VT-d。EPT/NPT 是 CPU 为虚拟机内存转换提供的 SLAT;VT-d/IOMMU 是设备 DMA 地址转换,两者不是同一功能。
三、systeminfo 为什么有时不给出四项结果
运行:
systeminfo
在输出底部通常能看到 Hyper-V 要求,包括“固件中已启用虚拟化”等信息。
如果 Hyper-V、VBS、内存完整性等组件已经启动,输出可能变成:
已检测到虚拟机监控程序。将不显示 Hyper-V 所需的功能。
这并不表示虚拟化被关闭。相反,在物理 Windows 主机上,Hypervisor 已经运行本身就是 CPU 虚拟化可用的强证据。微软也说明,已运行 Hyper-V 时,相关功能明细会被隐藏。Microsoft Learn:Hyper-V hardware requirements
如果当前 Windows 本身运行在虚拟机里,这个结论只能描述“来宾系统看到的虚拟 CPU”,不能据此判断物理宿主机 BIOS 的真实设置。
四、程序如何检测 VT-x 与 SVM
1. CPUID 只能先判断“硬件支持”
在 Intel 处理器上执行 CPUID(EAX=1):
ECX bit 5 = 1 → 支持 VMX,也就是 VT-x
Intel 的架构手册明确使用 CPUID.1:ECX.VMX[bit 5] 判断处理器是否支持 VMX。Intel 64 and IA-32 SDM,VMX support
在 AMD 处理器上执行 CPUID(EAX=0x80000001):
ECX bit 2 = 1 → 支持 SVM,也就是 AMD-V
AMD 将该能力记为 CPUID Fn8000_0001_ECX[SVM];固件还可以通过 SVM 禁用/锁定位阻止系统使用它。AMD64 Architecture Programmer’s Manual, Volume 2
所以,CPUID 位为 1 只代表处理器具有这项能力,不等于 BIOS 已经打开开关。
2. 用户态程序应让 Windows 判断“固件是否开放”
Windows 提供:
IsProcessorFeaturePresent(PF_VIRT_FIRMWARE_ENABLED)
其中 PF_VIRT_FIRMWARE_ENABLED 的值为 21。微软对它的定义是:虚拟化已经在固件中启用,并由操作系统提供。Microsoft Learn:IsProcessorFeaturePresent
一个简化的 C++ 检测片段如下:
#include <windows.h>
#include <intrin.h>
#include <array>
#include <cstring>
#include <string>
struct CpuVirtInfo {
std::string vendor;
bool vmxSupported = false;
bool svmSupported = false;
bool hypervisorPresent = false;
bool enabledByFirmware = false;
};
CpuVirtInfo QueryCpuVirtualization() {
CpuVirtInfo result;
std::array<int, 4> cpu{};
__cpuid(cpu.data(), 0);
char vendor[13]{};
std::memcpy(vendor, &cpu[1], 4); // EBX
std::memcpy(vendor + 4, &cpu[3], 4); // EDX
std::memcpy(vendor + 8, &cpu[2], 4); // ECX
result.vendor = vendor;
__cpuid(cpu.data(), 1);
result.vmxSupported = (cpu[2] & (1 << 5)) != 0;
result.hypervisorPresent = (cpu[2] & (1U << 31)) != 0;
__cpuid(cpu.data(), static_cast<int>(0x80000000U));
if (static_cast<unsigned int>(cpu[0]) >= 0x80000001U) {
__cpuid(cpu.data(), static_cast<int>(0x80000001U));
result.svmSupported = (cpu[2] & (1 << 2)) != 0;
}
result.enabledByFirmware =
IsProcessorFeaturePresent(PF_VIRT_FIRMWARE_ENABLED) != FALSE;
return result;
}
推荐的判定顺序是:
- 先按
GenuineIntel或AuthenticAMD区分厂商; - 用对应 CPUID 位判断硬件是否支持;
- 用
PF_VIRT_FIRMWARE_ENABLED判断固件是否已开放; - 如果检测到正在运行的 Hypervisor,则单独报告,避免把被 Hypervisor 虚拟化或屏蔽后的 CPUID 误判成“不支持”。
五、程序如何检测 VT-d 与 AMD IOMMU
VT-d/AMD IOMMU 没有一个与 VirtualizationFirmwareEnabled 等价、适用于所有 Windows 版本的简单布尔属性。
通用用户态程序通常会枚举 ACPI 固件表:
- Intel 平台查找
DMAR; - AMD 平台查找
IVRS。
Intel VT-d 固件通过 DMAR 表描述 DMA Remapping 硬件单元;AMD IOMMU 规范则定义了 IVRS,也就是 I/O Virtualization Reporting Structure。Intel VT-d Architecture Specification、AMD IOMMU Specification
Windows 用户态可以调用:
EnumSystemFirmwareTables('ACPI', ...)
微软文档说明,使用 ACPI provider 时,返回的每个四字节标识都对应当前 ACPI 命名空间中的一个表签名。Microsoft Learn:EnumSystemFirmwareTables
如果只想快速检查表名,可以在完整 Windows 的 PowerShell 中运行下面的脚本:
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class NativeFirmware {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint EnumSystemFirmwareTables(
uint provider, IntPtr buffer, uint size);
}
'@
# Windows API 中多字符常量 'ACPI' 对应 0x41435049
$acpi = [uint32]0x41435049
$size = [NativeFirmware]::EnumSystemFirmwareTables(
$acpi, [IntPtr]::Zero, 0)
if ($size -eq 0) {
throw '无法枚举 ACPI 固件表。'
}
$buffer = [Runtime.InteropServices.Marshal]::AllocHGlobal([int]$size)
try {
$written = [NativeFirmware]::EnumSystemFirmwareTables(
$acpi, $buffer, $size)
if ($written -eq 0) {
throw '读取 ACPI 固件表失败。'
}
$bytes = [byte[]]::new($written)
[Runtime.InteropServices.Marshal]::Copy(
$buffer, $bytes, 0, [int]$written)
$tables = for ($i = 0; $i -le [int]$written - 4; $i += 4) {
[Text.Encoding]::ASCII.GetString($bytes, $i, 4)
}
[pscustomobject]@{
Intel_VTd_DMAR = $tables -contains 'DMAR'
AMD_IOMMU_IVRS = $tables -contains 'IVRS'
ACPI表数量 = $tables.Count
}
}
finally {
[Runtime.InteropServices.Marshal]::FreeHGlobal($buffer)
}
Intel_VTd_DMAR = True 表示检测到 DMAR;AMD_IOMMU_IVRS = True 表示检测到 IVRS。两项不能简单合并,因为应先根据 CPU 厂商选择对应的判断结果。
示例:
#include <windows.h>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
constexpr DWORD Signature(char a, char b, char c, char d) {
return (static_cast<DWORD>(a) << 24) |
(static_cast<DWORD>(b) << 16) |
(static_cast<DWORD>(c) << 8) |
static_cast<DWORD>(d);
}
bool HasAcpiTable(const char wanted[5]) {
constexpr DWORD acpi = Signature('A', 'C', 'P', 'I');
const UINT size = EnumSystemFirmwareTables(acpi, nullptr, 0);
if (size == 0) return false;
std::vector<std::uint8_t> buffer(size);
const UINT written = EnumSystemFirmwareTables(
acpi, buffer.data(), static_cast<DWORD>(buffer.size()));
if (written == 0 || written > buffer.size()) return false;
for (UINT offset = 0; offset + 4 <= written; offset += 4) {
if (std::memcmp(buffer.data() + offset, wanted, 4) == 0)
return true;
}
return false;
}
const bool intelVtdExposed = HasAcpiTable("DMAR");
const bool amdIommuExposed = HasAcpiTable("IVRS");
ACPI 表存在应该怎样描述
严谨的输出建议使用:
检测到 DMAR:固件已向 Windows 暴露 Intel DMA Remapping 信息 | 检测到 IVRS:固件已向 Windows 暴露 AMD IOMMU 信息
不要仅凭表名就声称“所有设备都已启用 DMA 重映射”。ACPI 表存在是非常强的固件暴露证据,但操作系统是否为所有设备建立重映射,还要看表内容、驱动、系统策略和当前启动环境。
如果还需要判断 Windows 是否正在将其用于安全防护,再结合 msinfo32 的“内核 DMA 保护”状态。
六、一个实用的判定矩阵
| 检测结果 | 推荐解释 |
|---|---|
| CPUID 不支持,固件标志为否 | CPU 不支持,或者当前虚拟环境屏蔽了该能力 |
| CPUID 支持,固件标志为否 | CPU 支持,但 BIOS/UEFI 可能未开启 VT-x/SVM |
| 固件标志为是 | VT-x/SVM 已由固件开放给 Windows |
| 已检测到 Hypervisor | 当前环境正在使用虚拟化;物理机上通常可视为已开启 |
| Intel 平台存在 DMAR | 固件已暴露 VT-d/DMA Remapping 信息 |
| AMD 平台存在 IVRS | 固件已暴露 AMD IOMMU 信息 |
| 内核 DMA 保护为开启 | Windows 正在使用 IOMMU 提供相应 DMA 防护 |
| 内核 DMA 保护为关闭 | 不能单独断定 VT-d/IOMMU 已关闭 |
七、常见误区
误区 1:CPU 型号支持,就等于 BIOS 已开启
不对。CPUID 能证明“硬件有”,不能单独证明固件已经允许操作系统使用。
误区 2:任务管理器显示“虚拟化已启用”,所以 VT-d 也已启用
不对。任务管理器该字段主要对应 VT-x/SVM。
误区 3:SLAT 就是 IOMMU
不对。Intel EPT、AMD NPT 面向 CPU 的虚拟机内存转换;VT-d/AMD IOMMU 面向设备 DMA。
误区 4:内核 DMA 保护关闭,就说明 VT-d 关闭
不一定。Windows 的 DMA 防护还有额外的平台、ACPI、端口和驱动要求。
误区 5:直接读取 MSR 才是唯一可靠方法
Intel 的 IA32_FEATURE_CONTROL、AMD 的 VM_CR 等寄存器确实能提供更底层的信息,但用户态程序不能直接安全读取这些 MSR,通常需要签名内核驱动。普通检测工具优先组合 Windows 官方 API、CPUID、Hypervisor 状态和 ACPI 表即可,不应为了一个状态查询加载来源不明或存在漏洞的驱动。
八、Windows PE 下怎么检测
在精简 Windows PE 中,任务管理器、msinfo32、PowerShell 和 WMI/CIM 可能不存在。更适合的方案是编写原生 x64 程序,直接使用:
CPUID:判断 VMX/SVM 硬件能力;IsProcessorFeaturePresent(PF_VIRT_FIRMWARE_ENABLED):查询 Windows 暴露的固件虚拟化状态;EnumSystemFirmwareTables('ACPI'):查找 DMAR/IVRS;CPUID.1:ECX[31]:辅助识别 Hypervisor 环境。
这组方法不依赖 WMI 或 .NET,适合完整 Windows 与 WinPE 共用。不过,在虚拟机里的 PE 仍然只能看到虚拟固件和虚拟 CPU,无法绕过 Hypervisor 查询物理宿主机 BIOS。
总结
Windows 下识别虚拟化状态,最稳妥的思路不是寻找一个万能字段,而是组合证据:
VT-x / SVM:CPU 厂商 + CPUID 支持位 + Windows 固件标志 + Hypervisor 状态
VT-d / IOMMU:CPU 厂商 + ACPI DMAR/IVRS + Windows 内核 DMA 保护状态
对于日常排查,先看任务管理器和 msinfo32;对于批量检测、WinPE 或硬件采集程序,再使用原生 Windows API、CPUID 和 ACPI 表。这样既能避免把“支持”误认为“开启”,也不会把 Windows 某项安全功能的关闭误判为 IOMMU 本身关闭。








暂无评论内容