# VMProtect 虚拟机检测模块实现详解

## 1. 概述
VMProtect 的虚拟机检测模块用于检测程序是否在虚拟化环境中运行,包括 VMware、VirtualBox、Hyper-V、QEMU、Parallels 等常见虚拟机。检测分为硬件检测和软件检测两个层次。
### 检测目标
– **VMware** – VMware Workstation/Player/ESXi
– **VirtualBox** – Oracle VirtualBox
– **Hyper-V** – Microsoft Hyper-V (需排除根分区)
– **QEMU** – QEMU/KVM
– **Parallels** – Parallels Desktop (macOS)
– **Sandboxie** – 沙箱环境
## 2. 硬件检测方法
### 2.1 CPUID 指令检测
CPUID 指令是检测虚拟机的最可靠方法之一。当在虚拟机中执行 CPUID 时,第 31 位会被置位。
“`cpp
// 检测 Hypervisor 存在
int cpu_info[4];
__cpuid(cpu_info, 1);
if ((cpu_info[2] >> 31) & 1) {
// Hypervisor 位被置位,说明在虚拟机中
return true;
}
“`
### 2.2 Hyper-V 根分区排除
Hyper-V 的特殊之处在于根分区(Root Partition)是物理主机,不应被检测为虚拟机。
“`cpp
bool IsHyperVRootPartition()
{
int cpu_info[4];
// 获取 Hypervisor 供应商 ID
cpu_info[1] = 0;
cpu_info[2] = 0;
cpu_info[3] = 0;
__cpuid(cpu_info, 0x40000000);
// 检查是否为 “Microsoft Hv”
if (cpu_info[1] == 0x7263694d && // “Micr”
cpu_info[2] == 0x666f736f && // “osof”
cpu_info[3] == 0x76482074) { // “t Hv”
// 检查是否为根分区
cpu_info[1] = 0;
__cpuid(cpu_info, 0x40000003);
// 如果 bit 0 被置位,说明是根分区
if (cpu_info[1] & 1)
return true; // 是根分区,不是虚拟机
}
return false;
}
“`
### 2.3 陷阱标志检测
虚拟机通常无法正确处理陷阱标志(Trap Flag),这是检测虚拟机的另一种方法。
“`cpp
bool CheckTrapFlag()
{
uint8_t mem_val;
uint64_t val;
__try {
// 设置陷阱标志 (EFLAGS 的第 8 位)
__writeeflags(__readeflags() | 0x100);
// 执行 RDTSC 指令
val = __rdtsc();
__nop();
// 如果正常执行到这里,说明不是虚拟机
return false;
}
__except (mem_val = *static_cast<uint8_t *>(
GetExceptionInformation()->ExceptionRecord->ExceptionAddress
), EXCEPTION_EXECUTE_HANDLER) {
// 异常处理程序被调用
// 检查触发异常的指令是否为 NOP (0x90)
if (mem_val != 0x90)
return true; // 检测到虚拟机
}
return false;
}
“`
检测原理:
– 设置陷阱标志后,每条指令执行后都会触发单步异常
– 虚拟机的异常处理机制与物理机不同
– 通过检查异常处理的行为差异来检测虚拟机
## 3. 软件检测方法
### 3.1 固件供应商检测
虚拟机通常在固件中留下特定的字符串标识。
“`cpp
// 固件供应商字符串列表
static const char *VM_VENDORS[] = {
“VMware”, // VMware
“VirtualBox”, // VirtualBox
“Parallels”, // Parallels
“QEMU”, // QEMU (Linux)
“Microsoft”, // Hyper-V (Linux)
“innotek” // VirtualBox 旧版本 (Linux)
};
bool LoaderFindFirmwareVendor(const uint8_t *data, size_t data_size)
{
for (size_t i = 0; i < data_size; i++) {
// 检测 QEMU
if (i + 3 < data_size &&
data[i + 0] == ‘Q’ && data[i + 1] == ‘E’ &&
data[i + 2] == ‘M’ && data[i + 3] == ‘U’)
return true;
// 检测 Microsoft (Hyper-V)
if (i + 8 < data_size &&
data[i + 0] == ‘M’ && data[i + 1] == ‘i’ &&
data[i + 2] == ‘c’ && data[i + 3] == ‘r’ &&
data[i + 4] == ‘o’ && data[i + 5] == ‘s’ &&
data[i + 6] == ‘o’ && data[i + 7] == ‘f’ &&
data[i + 8] == ‘t’)
return true;
// 检测 innotek (VirtualBox)
if (i + 6 < data_size &&
data[i + 0] == ‘i’ && data[i + 1] == ‘n’ &&
data[i + 2] == ‘n’ && data[i + 3] == ‘o’ &&
data[i + 4] == ‘t’ && data[i + 5] == ‘e’ &&
data[i + 6] == ‘k’)
return true;
// 检测 VirtualBox
if (i + 9 < data_size &&
data[i + 0] == ‘V’ && data[i + 1] == ‘i’ &&
data[i + 2] == ‘r’ && data[i + 3] == ‘t’ &&
data[i + 4] == ‘u’ && data[i + 5] == ‘a’ &&
data[i + 6] == ‘l’ && data[i + 7] == ‘B’ &&
data[i + 8] == ‘o’ && data[i + 9] == ‘x’)
return true;
// 检测 VMware
if (i + 5 < data_size &&
data[i + 0] == ‘V’ && data[i + 1] == ‘M’ &&
data[i + 2] == ‘w’ && data[i + 3] == ‘a’ &&
data[i + 4] == ‘r’ && data[i + 5] == ‘e’)
return true;
// 检测 Parallels
if (i + 8 < data_size &&
data[i + 0] == ‘P’ && data[i + 1] == ‘a’ &&
data[i + 2] == ‘r’ && data[i + 3] == ‘a’ &&
data[i + 4] == ‘l’ && data[i + 5] == ‘l’ &&
data[i + 6] == ‘e’ && data[i + 7] == ‘l’ &&
data[i + 8] == ‘s’)
return true;
}
return false;
}
“`
### 3.2 Windows 固件表检测
Windows 提供了 `EnumSystemFirmwareTables` 和 `GetSystemFirmwareTable` API 来访问固件信息。
“`cpp
bool CheckWindowsFirmwareTables()
{
HMODULE kernel32 = GetModuleHandleA(“kernel32.dll”);
typedef UINT (WINAPI *tEnumSystemFirmwareTables)(
DWORD FirmwareTableProviderSignature,
PVOID pFirmwareTableEnumBuffer,
DWORD BufferSize
);
typedef UINT (WINAPI *tGetSystemFirmwareTable)(
DWORD FirmwareTableProviderSignature,
DWORD FirmwareTableID,
PVOID pFirmwareTableBuffer,
DWORD BufferSize
);
tEnumSystemFirmwareTables *enum_tables =
(tEnumSystemFirmwareTables *)GetProcAddress(
kernel32, “EnumSystemFirmwareTables”
);
tGetSystemFirmwareTable *get_table =
(tGetSystemFirmwareTable *)GetProcAddress(
kernel32, “GetSystemFirmwareTable”
);
if (!enum_tables || !get_table)
return false;
// 获取固件表列表大小
UINT tables_size = enum_tables(‘FIRM’, NULL, 0);
if (!tables_size)
return false;
// 分配缓冲区
DWORD *tables = new DWORD[tables_size / sizeof(DWORD)];
enum_tables(‘FIRM’, tables, tables_size);
// 遍历每个固件表
for (size_t i = 0; i < tables_size / sizeof(DWORD); i++) {
UINT data_size = get_table(‘FIRM’, tables[i], NULL, 0);
if (data_size) {
uint8_t *data = new uint8_t[data_size];
get_table(‘FIRM’, tables[i], data, data_size);
// 检查固件数据中是否包含虚拟机标识
if (LoaderFindFirmwareVendor(data, data_size)) {
delete[] data;
delete[] tables;
return true;
}
delete[] data;
}
}
delete[] tables;
return false;
}
“`
### 3.3 物理内存扫描
在旧版 Windows 上,通过映射物理内存来扫描固件标识。
“`cpp
bool CheckPhysicalMemory()
{
HMODULE ntdll = GetModuleHandleA(“ntdll.dll”);
typedef NTSTATUS (NTAPI *tNtOpenSection)(
PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
typedef NTSTATUS (NTAPI *tNtMapViewOfSection)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
SECTION_INHERIT InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect
);
typedef NTSTATUS (NTAPI *tNtUnmapViewOfSection)(
HANDLE ProcessHandle,
PVOID BaseAddress
);
typedef NTSTATUS (NTAPI *tNtClose)(HANDLE Handle);
tNtOpenSection *open_section =
(tNtOpenSection *)GetProcAddress(ntdll, “NtOpenSection”);
tNtMapViewOfSection *map_view =
(tNtMapViewOfSection *)GetProcAddress(ntdll, “NtMapViewOfSection”);
tNtUnmapViewOfSection *unmap_view =
(tNtUnmapViewOfSection *)GetProcAddress(ntdll, “NtUnmapViewOfSection”);
tNtClose *close =
(tNtClose *)GetProcAddress(ntdll, “NtClose”);
if (!open_section || !map_view || !unmap_view || !close)
return false;
// 打开物理内存设备
HANDLE physical_memory = NULL;
UNICODE_STRING str;
OBJECT_ATTRIBUTES attrs;
wchar_t buf[] = L”\\device\\physicalmemory”;
str.Buffer = buf;
str.Length = sizeof(buf) – sizeof(wchar_t);
str.MaximumLength = sizeof(buf);
InitializeObjectAttributes(&attrs, &str, OBJ_CASE_INSENSITIVE, NULL, NULL);
NTSTATUS status = open_section(&physical_memory, SECTION_MAP_READ, &attrs);
if (!NT_SUCCESS(status))
return false;
// 映射物理内存 (0xC0000 – 0xD0000 通常包含固件数据)
void *data = NULL;
SIZE_T data_size = 0x10000; // 64KB
LARGE_INTEGER offset;
offset.QuadPart = 0xC0000;
status = map_view(physical_memory, GetCurrentProcess(), &data,
NULL, data_size, &offset, &data_size,
ViewShare, 0, PAGE_READONLY);
bool is_vm = false;
if (NT_SUCCESS(status)) {
is_vm = LoaderFindFirmwareVendor((uint8_t *)data, data_size);
unmap_view(GetCurrentProcess(), data);
}
close(physical_memory);
return is_vm;
}
“`
### 3.4 Linux DMI 检测
在 Linux 上,通过读取 DMI(Desktop Management Interface)信息来检测虚拟机。
“`cpp
bool CheckLinuxDMI()
{
FILE *fsys_vendor = fopen(“/sys/devices/virtual/dmi/id/sys_vendor”, “r”);
if (!fsys_vendor)
return false;
char sys_vendor[256] = {0};
fgets(sys_vendor, sizeof(sys_vendor), fsys_vendor);
fclose(fsys_vendor);
return LoaderFindFirmwareVendor(
(uint8_t *)sys_vendor,
strlen(sys_vendor)
);
}
“`
### 3.5 沙箱检测
检测 Sandboxie 等沙箱环境。
“`cpp
bool CheckSandboxie()
{
// 检查 sbiedll.dll 是否存在
HMODULE sbie_dll = GetModuleHandleA(“sbiedll.dll”);
return (sbie_dll != NULL);
}
“`
## 4. 运行时检测流程
### 4.1 加载器检测流程
“`cpp
uint32_t SetupImage()
{
// … 其他初始化代码 …
// 检测虚拟机
if (data.options() & LOADER_OPTION_CHECK_VIRTUAL_MACHINE) {
int cpu_info[4];
__cpuid(cpu_info, 1);
if ((cpu_info[2] >> 31) & 1) {
// Hypervisor 位被置位
bool is_found = true;
// 检查是否为 Hyper-V 根分区
cpu_info[1] = 0;
cpu_info[2] = 0;
cpu_info[3] = 0;
__cpuid(cpu_info, 0x40000000);
if (cpu_info[1] == 0x7263694d &&
cpu_info[2] == 0x666f736f &&
cpu_info[3] == 0x76482074) { // “Microsoft Hv”
cpu_info[1] = 0;
__cpuid(cpu_info, 0x40000003);
// 根分区不视为虚拟机
if (cpu_info[1] & 1)
is_found = false;
}
if (is_found) {
LoaderMessage(mtVirtualMachineFound);
return LOADER_ERROR;
}
}
else {
// 没有 Hypervisor 位,进行软件检测
#ifdef __unix__
// Linux DMI 检测
if (CheckLinuxDMI()) {
LoaderMessage(mtVirtualMachineFound);
return LOADER_ERROR;
}
#else
// Windows 陷阱标志检测
if (CheckTrapFlag()) {
LoaderMessage(mtVirtualMachineFound);
return LOADER_ERROR;
}
// Windows 固件表检测
if (CheckWindowsFirmwareTables()) {
LoaderMessage(mtVirtualMachineFound);
return LOADER_ERROR;
}
// 沙箱检测
if (CheckSandboxie()) {
LoaderMessage(mtVirtualMachineFound);
return LOADER_ERROR;
}
#endif
}
}
// … 继续初始化 …
return LOADER_SUCCESS;
}
“`
### 4.2 SDK API 检测
VMProtect SDK 提供了 `VMProtectIsVirtualMachinePresent()` 函数供开发者调用。
“`cpp
// runtime/core.cc
bool WINAPI ExportedIsVirtualMachinePresent()
{
// 硬件检测
int cpu_info[4];
__cpuid(cpu_info, 1);
if ((cpu_info[2] >> 31) & 1) {
// Hypervisor 位被置位
#ifndef VMP_GNU
// 检查 Hyper-V 根分区
cpu_info[1] = 0;
cpu_info[2] = 0;
cpu_info[3] = 0;
__cpuid(cpu_info, 0x40000000);
if (cpu_info[1] == 0x7263694d &&
cpu_info[2] == 0x666f736f &&
cpu_info[3] == 0x76482074) { // “Microsoft Hv”
cpu_info[1] = 0;
__cpuid(cpu_info, 0x40000003);
if (cpu_info[1] & 1)
return false; // 根分区
}
#endif
return true;
}
// 软件检测 (Windows)
#ifndef VMP_GNU
// 陷阱标志检测
if (CheckTrapFlag())
return true;
// 固件表检测
if (CheckWindowsFirmwareTables())
return true;
// 沙箱检测
if (CheckSandboxie())
return true;
#endif
// Linux DMI 检测
#ifdef __unix__
if (CheckLinuxDMI())
return true;
#endif
return false;
}
“`
## 5. .NET 运行时检测
### 5.1 C# 实现
“`csharp
// VMProtect.Runtime/Core.cs
public static bool IsVirtualMachinePresent()
{
// CPUID 检测
var info = CpuId.Invoke(1);
if (((info[2] >> 31) & 1) != 0)
{
// 检查 Hyper-V 根分区
info = CpuId.Invoke(0x40000000);
if (info[1] == 0x7263694d &&
info[2] == 0x666f736f &&
info[3] == 0x76482074) // “Microsoft Hv”
{
info = CpuId.Invoke(0x40000003);
if ((info[1] & 1) != 0)
return false; // 根分区
}
return true;
}
// 固件表检测
try
{
uint tableSignature = (byte)’R’ << 24 |
(byte)’S’ << 16 |
(byte)’M’ << 8 |
(byte)’B’;
int size = (int)Win32.EnumSystemFirmwareTables(
tableSignature, IntPtr.Zero, 0
);
if (size > 0)
{
IntPtr nativeBuffer = Marshal.AllocHGlobal(size);
Win32.EnumSystemFirmwareTables(tableSignature, nativeBuffer, (uint)size);
byte[] buffer = new byte[size];
Marshal.Copy(nativeBuffer, buffer, 0, size);
Marshal.FreeHGlobal(nativeBuffer);
for (var i = 0; i < size / sizeof(uint); i += sizeof(uint))
{
uint tableId = BitConverter.ToUInt32(buffer, i);
int dataSize = (int)Win32.GetSystemFirmwareTable(
tableSignature, tableId, IntPtr.Zero, 0
);
if (dataSize > 0)
{
nativeBuffer = Marshal.AllocHGlobal(dataSize);
Win32.GetSystemFirmwareTable(
tableSignature, tableId, nativeBuffer, (uint)dataSize
);
byte[] data = new byte[dataSize];
Marshal.Copy(nativeBuffer, data, 0, dataSize);
Marshal.FreeHGlobal(nativeBuffer);
if (FindFirmwareVendor(data))
return true;
}
}
}
}
catch (EntryPointNotFoundException) { }
return false;
}
“`
## 6. 编译时集成
### 6.1 选项定义
“`cpp
// core/core.h
enum CompileOption {
// … 其他选项 …
cpCheckVirtualMachine = 0x00000800, // 检测虚拟机
};
enum MessageType {
// … 其他消息 …
MESSAGE_VIRTUAL_MACHINE_FOUND_STR, // 发现虚拟机消息
};
“`
### 6.2 运行时选项
“`cpp
// runtime/common.h
enum {
LOADER_OPTION_CHECK_VIRTUAL_MACHINE = 0x10 // 加载器虚拟机检测选项
};
// 消息类型
enum MessageType {
mtVirtualMachineFound // 发现虚拟机
};
// 字符串资源
#define MESSAGE_VIRTUAL_MACHINE_FOUND_STR \
VMP_STR(“Sorry, this application cannot run under a Virtual Machine.”)
“`
### 6.3 编译时字符串加密
“`cpp
// il.cc / intel.cc
// 加密虚拟机检测消息字符串
loader_string_list[FACE_VIRTUAL_MACHINE_FOUND] = AddCommand(
EncryptString(
os::FromUTF8(ctx.options.messages[MESSAGE_VIRTUAL_MACHINE_FOUND]).c_str(),
string_key
)
);
// 条件编译虚拟机检测代码
if (ctx.options.flags & cpCheckVirtualMachine) {
// 包含虚拟机检测代码
IncludeVMCheckCode();
}
“`
## 7. 反检测技术
### 7.1 字符串加密
所有用于检测的字符串都经过加密,防止静态分析:
“`cpp
// 加密字符串宏
#define VMProtectDecryptStringA(str) DecryptString(str, KEY)
// 使用示例
FILE *f = fopen(VMProtectDecryptStringA(“/sys/devices/virtual/dmi/id/sys_vendor”), “r”);
“`
### 7.2 动态 API 解析
使用动态 API 解析避免导入表暴露检测行为:
“`cpp
// 动态获取 API 地址
tEnumSystemFirmwareTables *enum_tables =
(tEnumSystemFirmwareTables *)InternalGetProcAddress(
kernel32,
VMProtectDecryptStringA(“EnumSystemFirmwareTables”)
);
“`
### 7.3 多平台支持
“`cpp
// 平台适配
#ifdef __APPLE__
// macOS 检测 (FIXME – 待实现)
#elif defined(__unix__)
// Linux 检测
CheckLinuxDMI();
#elif defined(WIN_DRIVER)
// Windows 驱动检测
CheckDriverFirmware();
#else
// Windows 用户态检测
CheckWindowsFirmwareTables();
CheckTrapFlag();
CheckSandboxie();
#endif
“`
## 8. 检测策略总结
| 检测方法 | 目标虚拟机 | 可靠性 | 平台 |
|———-|———–|——–|——|
| CPUID Hypervisor 位 | 所有支持硬件虚拟化的 VM | 高 | 全平台 |
| Hyper-V 根分区排除 | Hyper-V | 高 | Windows |
| 陷阱标志检测 | VMware, VirtualBox | 中 | Windows |
| 固件表扫描 | VMware, VirtualBox, Parallels | 高 | Windows |
| 物理内存扫描 | VMware, VirtualBox | 中 | Windows |
| DMI 信息读取 | QEMU, KVM, Hyper-V | 高 | Linux |
| 沙箱 DLL 检测 | Sandboxie | 高 | Windows |
虚拟机检测模块通过多层次、多方法的检测策略,能够有效识别大多数常见的虚拟化环境,为软件保护提供重要的安全屏障。














暂无评论内容