VMware 虚拟机环境的检测有多种方法,这里以注册表痕迹检测法为例,谈谈如何快速检测虚拟环境。
一、理论分析
注册表是 Windows 操作系统的重要概念,对注册表的操作直接涉及系统的核心配置,具有牵一发而动全身的作用。在 HKEY_LOCAL_MACHINE 项下,有 HARDWARE 键,这是我们今天的主角。
HARDWARE 键中包含了计算机硬件信息的子项。在启动系统时,该项都会被重新创建,这样就很容易向系统中添加硬件了。该项是系统根据硬件信息在启动时自己填写的,而不是根据数据去启动硬件,所以用户对该项的修改不会生效。在该项下面的四个项中含有 CPU、FPU、系统总线、 PCI 总线的设备、即插即用总线、高级控制电源接口、键盘、打印机端口、鼠标、屏幕等信息,有些信息要在 BIOS 才能看见(比如高级控制电源接口)。
通过对比客户机和虚拟机的硬件信息,就可以发现它们存在较多不同点,这篇文章以 SCSI 信息为例,讲解如何区分虚拟机和物理机环境。
首先打开虚拟机:依次展开左侧导航栏中的 HKEY_LOCAL_MACHINE > HARDWARE > DEVICEMAP > Scsi。
在 SCSI 下有很多端口 SCSI Port XX,其中至少有一个端口下包含的 Identifier 数据值包含 VMware 厂商名称,如图所示:
我们再尝试打开物理机上的该路径,可以看出他显示的是真实设备的厂商名称,而不是虚拟设备:
显然,可以根据该特征来判断环境是不是虚拟环境。
二、代码实现
从编写程序的角度上来讲,这里由于我们并不知道特征值会位于 SCSI 下的哪个子键内,所以我们需要利用递归的方法,遍历每一层每一个 Identifier 值,看是否有特征值,只要找到了特征值,就算作是虚拟环境。
首先,使用 RegOpenKeyEx 函数打开注册表项。在每一层,我们都需要利用 RegQueryInfoKeyW 和 RegEnumKeyEx 函数的组合来枚举指定打开的注册表项的子项。使用 RegEnumValueW 获取对应值项的数据,并比较获取到的字符串是否包含 VMware 字样。将这些操作写入到一个 SearchKey 函数中,然后对于找到的键(而不是值)递归调用它本身,即可对每一层每一个值项进行遍历。
调好的代码如下,需要以管理员身份启动,我用的 VMware 是 17 版本,不知道其他版本是否一致:
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
DWORD GetRegKeyCount(HKEY hKey, DWORD dwIndex, TCHAR* achKey, DWORD dwKeyLen, BOOL bEnable)
{
LONG lRet = ERROR_SUCCESS;
DWORD cSubKeys = 0;
DWORD cValues = 0;
DWORD cMaxKeyLens = 0;
DWORD cMaxValueLens = 0;
lRet = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &cSubKeys, &cMaxKeyLens, NULL, &cValues, &cMaxValueLens, NULL, NULL, NULL);
if (lRet != ERROR_SUCCESS)
{
return 0;
}
if (cSubKeys != 0 && achKey != NULL)
{
lRet = RegEnumKeyEx(
hKey,
dwIndex,
achKey,
&dwKeyLen,
NULL,
NULL,
NULL,
NULL
);
if (lRet != ERROR_SUCCESS)
{
return 0;
}
}
if (bEnable) return cSubKeys;
else return cValues;
}
#define MAX_DATA_NAME 300
bool IsFindVMKey = FALSE;
// QueryKey - Enumerates the subkeys of key and its associated values.
// hKey - Key whose subkeys and values are to be enumerated.
BOOL SearchKey(HKEY hKey, LPCTSTR lpSubKey)
{
IsFindVMKey = FALSE;
HKEY hSubKey = NULL;
TCHAR subKeyName[MAX_PATH + 1] = { 0 };
TCHAR achValue[MAX_VALUE_NAME]{};
TCHAR lpData[MAX_DATA_NAME]{};
DWORD cchValue = MAX_VALUE_NAME;
DWORD cbData = MAX_DATA_NAME;
DWORD DataType = REG_SZ;
if (ERROR_SUCCESS == RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &hSubKey))
{
DWORD dwValueNum = GetRegKeyCount(hSubKey, 0, NULL, 0, FALSE);
for (DWORD j = 0, retCode = ERROR_SUCCESS; j < dwValueNum; j++)
{
// 枚举指定的开放注册表项的值。该函数每次调用键时都会复制一个索引值名称和键的数据块。
cchValue = MAX_VALUE_NAME;
cbData = MAX_DATA_NAME;
achValue[0] = '\0';
lpData[0] = '\0';
retCode = RegEnumValueW(hSubKey, j,
achValue,
&cchValue,
NULL,
&DataType,
(PBYTE)&lpData,
&cbData);
if (retCode == ERROR_SUCCESS)
{
_tprintf(TEXT("(%d) %s\n"), j + 1, achValue);
if (!wcsncmp(achValue, L"Iden", 4) && wcsstr(lpData, L"VMware"))
{
//printf("Value: %ws\n", lpData);
IsFindVMKey = TRUE;
return TRUE;
//break;
}
//SearchKey(hSubKey, subKeyName);
}
}
//获取lpSubKey下面有多少个子项
DWORD dwSubKey = GetRegKeyCount(hSubKey, 0, NULL, 0, TRUE);
for (DWORD i = 0; i < dwSubKey; i++)
{
if (ERROR_SUCCESS == RegEnumKey(hSubKey, i, subKeyName, MAX_PATH))
{
//打印项的名字
printf("%ws\n", subKeyName);
SearchKey(hSubKey, subKeyName);
}
}
//关闭
if (hSubKey)
{
RegCloseKey(hSubKey);
}
}
else {
IsFindVMKey = TRUE;
return FALSE;
}
return IsFindVMKey;
}
int __cdecl _tmain()
{
HKEY hTestKey;
DPI_AWARENESS_CONTEXT OldTidDpiAware = NULL;
OldTidDpiAware = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
if (OldTidDpiAware == NULL)
printf("设置线程上下文DPI配置失败。\n");
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("HARDWARE\\DEVICEMAP"),
0,
KEY_READ,
&hTestKey) == ERROR_SUCCESS
)
{
if (SearchKey(hTestKey, L"Scsi") && IsFindVMKey == true)
{
MessageBoxW(NULL, L"程序运行环境异常,请不要在虚拟机中运行该程序!", L"Ooops!", MB_SYSTEMMODAL | MB_ICONWARNING | MB_OK);
RegCloseKey(hTestKey);
exit(1033);
}
else if(IsFindVMKey == false)
{
MessageBoxW(NULL, L"恭喜你,Handware注册表检验通过,程序认定当前环境为物理机。", L"Congratulations!", MB_SYSTEMMODAL | MB_ICONINFORMATION | MB_OK);
}
else {
MessageBoxW(NULL, L"程序运行环境异常,请重启计算机!", L"Ooops!", MB_SYSTEMMODAL | MB_ICONWARNING | MB_OK);
RegCloseKey(hTestKey);
exit(1034);
}
}
RegCloseKey(hTestKey);
system("pause");
return 0;
}
下面是代码实测的结果:
本文属于原创文章,转载请注明出处:
https://blog.csdn.net/qq_59075481/article/details/133661342
更新于:2023.10.24