虚拟机运行环境检测
虚拟机运行环境检测,指的是软件能够判断当前是不是在虚拟机中运行,根据判断结果,做对应的处理。从恶意软件的视角,它可以在虚拟机中改变自身行为,加大分析难度。从软件自身安全出发,用于防止被逆向调试以及某些场景下的非正常使用。
检测思路
要实现这个功能,可有如下几个入手点:
- 硬件检测
- 虚拟机会创建具有特定标示符的硬件设备以及驱动。例如vmware会虚拟出两个网卡设备VMnet1(Host Only方式)和VMnet8(NAT方式),vbox会创建
VirtualBox Host-Only Network
- 虚拟机会创建具有特定标示符的硬件设备以及驱动。例如vmware会虚拟出两个网卡设备VMnet1(Host Only方式)和VMnet8(NAT方式),vbox会创建
- 运行环境检测
- vmware虚拟机会默认开启几个硬件端口与宿主机通讯。
- 利用特权指令在虚拟机和真实机器上执行结果不一致来区分。目前在网上找到的方法大部分是用来检测Vmware,不适用于VirtualBox以及VirtuaPC。
- 应用程序检测
- 虚拟机会安装一些特定软件以及服务,比如
vmtoolsd.exe
、vboxserivce.exe
等。 - 在特定路径下,存在特殊标识,例如注册表、系统驱动文件等。
- 虚拟机会安装一些特定软件以及服务,比如
分享一个网上各虚拟机的硬件区别,原文点此进入。
安装虚拟机以及测试的注意事项
通过VirtualBox
安装虚拟机时,有时会出现如下错误:
通过查阅网上相关资料,发现是因为权限问题,重新以管理员权限打开VirtualBox
,就可以继续安装。
本次测试发现Virtaul Box
和Virtual PC
两款虚拟机会相互冲突,就是说,当Virtaul box
启动时,再启动Virtual PC
,Virtaul box
会弹出如下提示:
可能这两种虚拟机不兼容,读者在自行测试时,需要注意这一点。
安装VirtualBox
的增强功能时,需要安装多个驱动,有时候安装界面会把驱动安装确认界面给盖住,此时安装界面会出现类似卡死的情况,因此,建议把安装界面移到一边,这样当驱动确认界面出现时,可确认并继续安装。
为了便于在不同虚拟机下测试,选择原版XP系统。在构建测试软件时,注意,要使用兼容XP模式的平台工具集来编译,否则测试软件无法在xp上运行。同时,将程序运行需要的运行库放在同一个共享目录中,各个虚拟机都访问该共享目录。
通过VirtualPC
安装虚拟机时,安装集成组件后,无法查看共享目录。网上解答连接在此,需要设置登陆用户名和密码,然后重新启动,在打开VirtualPC
虚拟机之前,输入用户名和密码,方可看到共享目录。
实践
以下给出三种实例来检测虚拟机是否存在,分布是基于进程名、基于注册表键以及基于硬盘驱动器检测,更多检测方案,参考文末实例代码链接。
基于进程名检测
通过检测检测指定进程名是否存在,来判断是否运行在虚拟机。
bool IsExist(const wchar_t* pName)
{
if (nullptr == pName)
return false;
const wchar_t* list[] = {
//Vmware
L"vmtoolsd.exe",
L"vmacthlp.exe",
L"vmwaretray.exe",
L"vmwareuser.exe",
//virtuablBox
L"vboxserivce.exe",
L"vboxtray.exe",
//virtualPC
L"vmsrvc.exe",
L"vmusrvc.exe",
L"vpcmap.exe",
};
std::wstring strProcessName(pName);
std::transform(strProcessName.begin(), strProcessName.end(), strProcessName.begin(), ::tolower);
for (int i = 0; i < sizeof(list)/sizeof(list[0]); i++)
{
if (0 == strProcessName.compare(list[i]))
return true;
}
return false;
}
BOOL CheckVMByProcessName()
{
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcessSnap)
return false;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bMore = Process32First(hProcessSnap, &pe32);
while (bMore)
{
char szName[MAX_PATH] = { 0 };
if (IsExist(pe32.szExeFile))
{
_tprintf(L"进程名: %s 进程PID: %d", pe32.szExeFile, pe32.th32ProcessID);
break;
}
bMore = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return bMore;
}
基于注册表键检测
利用注册表键值是否存在来检测,具体如下:
LONG registerOpenKey(char *value)
{
HKEY HK = 0;
return RegOpenKeyExA(HKEY_LOCAL_MACHINE, value, 0, KEY_READ, &HK);
}
bool CheckVMByReg()
{
std::string stringVmRegKeys[] =
{
//VMWare
"SOFTWARE\\Clients\\StartMenuInternet\\VMWAREHOSTOPEN.EXE",
"SOFTWARE\\VMware, Inc.\\VMware Tools",
"SYSTEM\\CurrentControlSet\\Enum\\SCSI\\Disk&Ven_VMware_&Prod_VMware_Virtual_S",
// Virtual PC or VirtualBox
"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers",
};
bool bCheckflag = false;
for (size_t i = 0; i < sizeof(stringVmRegKeys) / sizeof(stringVmRegKeys[0]); i++)
{
if (ERROR_SUCCESS == registerOpenKey((char *)stringVmRegKeys[i].c_str()))
{
bCheckflag = true;
printf("Analysis environment detected (%s)\n", stringVmRegKeys[i].c_str());
}
}
return bCheckflag;
}
基于硬盘驱动器名称检测
基于特定硬盘驱动器标题名称来检测。
BOOL CheckVMByDiskName()
{
std::vector<std::wstring> vtBlockHardDisc({ L"vmware", L"vbox", L"virtual"});
std::vector<std::wstring> vtDrivers;
CWmiDataHelper querier;
if (querier.ExecComQuery(L"select * from Win32_DiskDrive", L"Caption", vtDrivers) && vtDrivers.size())
{
std::transform(vtDrivers[0].begin(), vtDrivers[0].end(), vtDrivers[0].begin(), tolower);
for (unsigned int i = 0; i < vtBlockHardDisc.size(); i++)
{
if (wcsstr(vtDrivers[0].c_str(), vtBlockHardDisc[i].c_str()))
{
_tprintf(L"local disk name: %s. find key word:%s \n", vtDrivers[0].c_str(), vtBlockHardDisc[i].c_str());
return TRUE;
}
}
}
return FALSE;
}
测试结果
测试环境:
- VMware 10.0.1
- Virtual Box 6.1.18
- Virtual PC 6.1.7601
在没有进行任何反检测措施的情况下,下表为检测结果汇总。
检测方法 | Vmware | Virtual Box | Virtual PC | 反检测手段 |
---|---|---|---|---|
特定进程名检测 | 可以 | 可以 | 可以 | 删除特定进程 |
硬盘名称检测 | 可以 | 可以 | 可以 | 修改驱动或者配置文件vmx |
MAC地址检测 | 可以 | 可以 | 可以 | 修改MAC地址 |
CPUId检测 | 可以 | 不行 | 可以 | 未找到 |
特定服务检测 | 可以 | 可以 | 可以 | 卸载对应服务程序 |
特定文件检测 | 可以 | 可以 | 可以 | 修改特定文件 |
注册表检测 | 可以 | 可以 | 可以 | 修改特定注册表选项 |
IN特权指令 | 可以 | 不可以 | 不可以 | 未找到 |
IDT基址 | 不可以 | 不可以 | 不可以 | 未找到 |
LDT基址 | 不可以 | 不可以 | 不可以 | 未找到 |
GDT基址 | 不可以 | 不可以 | 不可以 | 未找到 |
STR | 不可以 | 不可以 | 不可以 | 未找到 |
通过上表可以知道,三种虚拟机环境都可以检测的方法有
- 特定进程名
- 硬盘名
- MAC地址
- 注册表检测
- 特定文件
- 特定服务
特定进程名、硬盘名以及MAC
地址这三种很容易反检测,不建议使用。注册表选项也很容易绕过,不建议。
最后剩下特定文件以及特定服务,经过实际测试发现,即使禁用掉特定服务,只要没有卸载掉服务程序,还是可以检测到的。一般来说,虚拟机提供的服务是为了方便与宿主机通讯的,共享文件的,卸载后会很麻烦。
最终结论,优先使用特定文件检测,具体检测文件可根据实际虚拟机环境来设定,其次使用特定服务检测。
上述每一种方法,均不能百分百保证检测正确,各位读者可根据具体情况来使用。
总结
本文搜集网上现有总结,并介绍基于进程、注册表键以及硬盘驱动器名称等多种检测虚拟机运行环境的方法,给出优先使用特定文件检测,其次检测特定后台服务的方案。
参考资料:
本文工程代码链接在此
软件如何判断自己运行在虚拟机中?
反虚拟机和沙箱检测的一些小技巧
sems
修改虚拟机中的硬盘ID等信息
[原创]虚拟机检测技术剖析
VMware | ESXi 等虚拟软件的反虚拟机检测技术方法探讨
VIrtual Machine Detection Techniques