虚拟机检测技术简单实践
概述
恶意软件
- 恶意软件是指在计算机系统上执行恶意任务的病毒、蠕虫和特洛伊木马的程序,通过破坏软件进程来实施控制。恶意软件其本身可能是一种病毒,蠕虫,后门或漏洞攻击脚本,它通过动态地改变攻击代码可以逃避入侵检测系统的特征检测(Signature-based detection ,也可称为模式匹配)。攻击者常常利用这种多变代码进入互联网上的一些带有入侵侦测的系统或IDSes入侵者警告系统。
虚拟机
- 所谓的虚拟机(Virtual Machine)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。通过虚拟机软件(比如VMware,Virtual PC ,VirtualBox),你可以在一台物理计算机上模拟出一台或多台虚拟的计算机,这些虚拟机完全就像真正的计算机那样进行工作,例如你可以安装操作系统、安装应用程序、访问网络资源等等。
虚拟机检测技术
- 在信息安全的研究范畴中,对恶意软件的分析一直是研究的热点。为了提高病毒分析过程的安全性以及硬件资源的节约性,分析恶意软件往往需要用到虚拟机技术,需要在一个虚拟的相对安全封闭的环境下测试恶意软件的工作特点。考虑到虚拟机可以完全像真正的计算机那样工作,而又不会影响到主机的使用,所以对病毒的行为分析往往在虚拟机中进行。
- 基于此,病毒开发者肯定不愿意自己的病毒行为特征被分析出来,所以攻击者为了提高恶意程序的隐蔽性以及破坏真实主机的成功率,他们都在恶意程序中加入检测虚拟机的代码,以判断程序所处的运行环境。当发现程序处于虚拟机(特别是蜜罐系统)中时,它就会改变操作行为或者中断执行,掩盖其病毒的行为特征,以此提高反病毒人员分析恶意软件行为的难度。
原理介绍
基于CPU运算时间的检测
- 在虚拟机中,代码的运行速度通常不如真实主机。所以恶意代码通过运行一段特定的代码来比较这段代码在虚拟机和真实主机之中的相对运行时间,以此来判断是否处于虚拟机之中。
bool CheckVMware1()
{
__asm
{
rdtsc
xchg ebx, eax
rdtsc
sub eax, ebx
cmp eax, 0xFF
jg detected
}
return FALSE;
detected:
return TRUE;
}
基于注册表的检测
- 通过读取主机具有虚拟机特性的注册表位置来判断是否处于虚拟机环境中。针对VMware可以判断注册表项
HKEY_CLASSES_ROOT\\Applications\\VMwareHostOpen.exe
。
bool CheckVMware2()
{
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, "\\Applications\\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS)
{
return TRUE;
}
else
{
return FALSE;
}
}
基于当前进程信息的检测
bbool CheckVMware3()
{
PROCESSENTRY32 pe32; //存放快照进程信息的一个结构体
pe32.dwSize = sizeof(pe32); //在使用这个结构之前,先设置它的大小
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //给系统内的所有进程拍一个快照
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
bool bMore = Process32First(hProcessSnap, &pe32);
while (bMore)
{
if (strcmp((const char *)pe32.szExeFile, "vmtoolsd.exe") == 0)
{
return TRUE;
}
bMore = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return FALSE;
}
基于特定文件的检测
bool CheckVMware4()
{
if (PathIsDirectory("C:\\Program Files\\VMware\\VMware Tools\\") == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}
基于注册服务的检测
bool CheckVMware5()
{
//打开系统服务控制器
SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (SCMan == NULL)
{
printf("%ld", GetLastError());
printf("OpenSCManager Eorror/n");
return -1;
}
//保存系统服务的结构
LPENUM_SERVICE_STATUSA service_status;
DWORD cbBytesNeeded = NULL;
DWORD ServicesReturned = NULL;
DWORD ResumeHandle = NULL;
service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
//获取系统服务的简单信息
bool ESS = EnumServicesStatusA(SCMan, //系统服务句柄
SERVICE_WIN32, //服务的类型
SERVICE_STATE_ALL, //服务的状态
(LPENUM_SERVICE_STATUSA)service_status, //输出参数,系统服务的结构
1024 * 64, //结构的大小
&cbBytesNeeded, //输出参数,接收返回所需的服务
&ServicesReturned, //输出参数,接收返回服务的数量
&ResumeHandle); //输入输出参数,第一次调用必须为0,返回为0代表成功
if (ESS == NULL)
{
printf("EnumServicesStatus Eorror/n");
return -1;
}
for (int i = 0; i < ServicesReturned; i++)
{
if (strstr(service_status[i].lpDisplayName, "VMware Tools") != NULL || strstr(service_status[i].lpDisplayName, "VMware 物理磁盘助手服务") != NULL)
{
return TRUE;
}
}
//关闭服务管理器的句柄
CloseServiceHandle(SCMan);
return FALSE;
}
运行实现
一般实现
- 根据原理介绍中提到的5个虚拟机检测原理,将其整合到一个CPP文件下,main函数如下所示:
int main()
{
int n;
bool result;
while (1)
{
printf("虚拟机检测技术:\n");
printf("1. 基于CPU运算时间的检测\n");
printf("2. 基于注册表的检测\n");
printf("3. 基于当前进程信息的检测\n");
printf("4. 基于特定文件的检测\n");
printf("5. 基于注册服务的检测\n");
printf("0. 退出\n");
printf("请选择:");
scanf("%d", &n);
flushall();
printf("检测结果:");
switch (n)
{
case 0: return 0;
case 1: result = CheckVMware1(); break;
case 2: result = CheckVMware2(); break;
case 3: result = CheckVMware3(); break;
case 4: result = CheckVMware4(); break;
case 5: result = CheckVMware5(); break;
default:printf("输入错误,请重新输入!\n"); Sleep(2000); system("cls"); continue;
}
if (result)
printf("yes!\n");
else
printf("no!\n");
printf("按任意键返回主菜单\n");
getch();
flushall();
system("cls");
}
return 0;
}
运行效果:
DLL文件实现
主要编写2个文件:
checkvm.dll
与testvm.exe
,下面分2部分详细介绍。checkvm.dll
- 该文件主要作为可以检测虚拟机的关键代码,包含4个主要文件:
checkvm.h
、checkvm.cpp
、dllmain.cpp
、Source.def
- checkvm.h:checkvm.dll的头文件,给出dll包含的函数方法
bool chekvm();
- checkvm.cpp:该部分代码主体功能部分类与一般实现方法一致,只是不需要Main函数,提供接口即可,不同部分如下:
bool checkvm()
{
int num = 0;
if (CheckVMware1())
num++;
if (CheckVMware2())
num++;
if (CheckVMware3())
num++;
if (CheckVMware4())
num++;
if (CheckVMware5())
num++;
if (num >= 4)
{
printf("This is a virtual machine!\n");
return FALSE;
}
else
{
printf("This is not a virtual machine!\n");
return TRUE;
}
}
- dllmain.cpp:定义 DLL 应用程序的入口点(固定写法)
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
- Source.def:给出可调用的函数方法
LIBRARY "checkvm"
EXPORTS
checkvm @1
testvm.exe
- 该部分代码为测试
checkvm.dll
所编写,调用checkvm.dll
中的checkvm()
函数即可,详细代码如下:
int main()
{
HMODULE hmod = LoadLibrary("checkvm.dll"); //用于加载dll
typedef int(*LoadProc)();
LoadProc Load_proc = (LoadProc)GetProcAddress(hmod, "checkvm"); //GetProcAddress()用于获得函数地址
int iRet = Load_proc();
getchar();
return 0;
}
运行效果(将testvm.exe与checkvm.dll放在同一个文件夹下)
总结
- 选择这个项目的原因是自己对恶意软件的检测很感兴趣,恶意软件与检测就是一种博弈的关系,对于检测者来说最安全的方式就是采用虚拟机检测,而一些恶意软件的制作者显然也不傻,则会在其病毒代码执行前加载一段虚拟机检测代码,如果检测为虚拟机环境,则不会暴露其行为特征,无法对其运行行为进行分析,当检测到脱离了虚拟机环境,就可以大展拳脚实现其目的了。
- 通过这次的实践,初步体会到了一些恶意软件在绕过虚拟机检测所采取的策略,通过代码编写,更加熟悉了虚拟机检测的机制,最后运用到了本学期在另一门课程中学到的DLL开发,将关键代码写入一个dll文件中,在今后需要检测的过程中,只需要调用
checkvm.dll
即可。 - 总之,这次的实践不仅丰富了我的理论知识,而且也增加了我动手操作的能力,特别是将课上所学运用到实际项目中的时候,形成了一个可在现实中使用的小工具,特别有成就感。