关于 Windows UserMode 反虚拟机技术(总结)

1. Anti-VM 技术

1.1 CPUID 指令

// EAX 为 0x1 时, 执行 CPUID 指令, ECX 第 31 位为0则在物理机, 为1则在虚拟机
BOOL CheckVmCpuid()
{
	BOOL dwVmFlag = FALSE;
	__asm {
		mov eax, 0x1;
		cpuid;
		// 判断其第 31 位是否为0
		bt ecx, 0x1f;
		jc Vm_Code;
		mov dwVmFlag, 0x0;
		jmp NoVm_Code
	Vm_Code:
		// 有虚拟机
		mov dwVmFlag, 0x1;
	NoVm_Code:		
	}

	return dwVmFlag;
}

// 对抗方式:
// 当客户机里执行了CPUID指令时,VM_Exit事件就发生了,这就是修改CPUID结果绕过Anti-VM的最佳时间。
// WMWare产品中,找到该虚拟机的.vmx配置文件,在最后一行加上
// cpuid.1.ecx="0---:----:----:----:----:----:----:----"
// EAX 为 0x40000000 时, 执行 CPUID 指令, ECX\EDX 会返回 Vmwaware 等字符串

// 对抗方式:
cpuid.40000000.ecx="0000:0000:0000:0000:0000:0000:0000:0000"
cpuid.40000000.edx="0000:0000:0000:0000:0000:0000:0000:0000"

1.2 检测 MAC 地址

// MAC地址的前三个字节标识一个提供商
// 以00:05:69、00:0c:29和00:50:56开始的MAC地址与VMware相对应
#include <nb30.h>
#pragma comment(lib, "netapi32.lib")
#pragma warning(disable:4996)

using namespace std;
string dwMacAddess;
typedef struct _ASTAT_
{
	ADAPTER_STATUS adapt;
	NAME_BUFFER NameBuff[30];
} ASTAT, *PASTAT;

VOID GetPcMacInfo()
{
	NCB dwNcb;
	ASTAT dwAapter;
	UCHAR dwRetCode;
	LANA_ENUM lenum;

	memset(&dwNcb, 0, sizeof(dwNcb));
	dwNcb.ncb_command = NCBENUM; // 枚举LAN适配器(LANA)编号
	dwNcb.ncb_buffer = (UCHAR *)&lenum; // 接受信息
	dwNcb.ncb_length = sizeof(lenum);

	dwRetCode = Netbios(&dwNcb);
	for (int i = 0; i < lenum.length; i++)
	{
		memset(&dwNcb, 0, sizeof(dwNcb));
		dwNcb.ncb_command = NCBRESET;
		dwNcb.ncb_lana_num = lenum.lana[i];
		dwRetCode = Netbios(&dwNcb);
		memset(&dwNcb, 0, sizeof(dwNcb));
		dwNcb.ncb_command = NCBASTAT;
		dwNcb.ncb_lana_num = lenum.lana[i];
		strcpy((char *)dwNcb.ncb_callname, "*");
		dwNcb.ncb_buffer = (unsigned char *)&dwAapter;
		dwNcb.ncb_length = sizeof(dwAapter);
		dwRetCode = Netbios(&dwNcb);
		if (dwRetCode == 0)
		{
			char tmp[128];
			sprintf(tmp, "%02x-%02x-%02x",
				dwAapter.adapt.adapter_address[0],
				dwAapter.adapt.adapter_address[1],
				dwAapter.adapt.adapter_address[2]
			);
			dwMacAddess = tmp;
		}
	}
}

BOOL CheckVmMacAddress()
{
	string dwMacAddess;

	// 获取 mac 信息
	GetPcMacInfo();

	if (dwMacAddess == "00-05-69" || dwMacAddess == "00-0c-29" || dwMacAddess == "00-50-56")
	{
		return TRUE;
	}
	else
		return FALSE;
}

1.3 执行”IN”特权指令

// IN指令属于特权指令, 虚拟机使用“IN”指令来读取特定端口的数据以进行两机通讯
// 在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为“EXCEPTION_PRIV_INSTRUCTION”的异常, 而在虚拟机中并不会发生异常
// 在指定功能号0A(获取VMware版本)的情况下,它会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于虚拟机中
// 以上两种方式都可以检测虚拟机

// 通过执行特权指令来检测虚拟机
bool CheckVmIn()
{
	bool rc = true;

	__try
	{
		__asm
		{
			push   edx
			push   ecx
			push   ebx

			mov    eax, 'VMXh'
			mov    ebx, 0  // 将ebx设置为非幻数’VMXH’的其它值
			mov    ecx, 10 // 指定功能号,用于获取VMWare版本,当它为0x14时用于获取VMware内存大小
			mov    edx, 'VX' // 端口号
			in     eax, dx // 从端口dx读取VMware版本到eax
			//若上面指定功能号为0x14时,可通过判断eax中的值是否大于0,若是则说明处于虚拟机中
			cmp    ebx, 'VMXh' // 判断ebx中是否包含VMware版本'VMXh’,若是则在虚拟机中
			setz[rc] // 设置返回值(为零 (ZF=1) 时设置字节)

			pop    ebx
			pop    ecx
			pop    edx
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER)  //如果未处于VMware中,则触发此异常
	{
		rc = false;
	}
	return rc;
}

1.4 遍历进程信息

// 通过进程快照读取当前进程信息,查找是否存在虚拟机中特有的进程,如VMware中的vmware.exe
BOOL CheckVMWare()  
{  
    DWORD ret = 0;  
    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(pe32.szExeFile, "vmware.exe")==0)  
        {  
            return TRUE;  
        }  
        bMore = Process32Next(hProcessSnap, &pe32);   
    }  
    CloseHandle(hProcessSnap);   
    return FALSE;  
}  

1.5 特定的文件夹或文件信息

// 通过查找磁盘中是否存在特定的文件夹或文件,判断当前是否在虚拟机中。VMware虚拟机中通常会有路径C:\Program Files\VMware\VMware Tools\;
BOOL CheckVMware()  
{  
    if (PathIsDirectory("C:\\Program Files\\VMware\\VMware Tools\\") == 0)  
    {  
        return FALSE;  
    }  
    else  
    {  
        return TRUE;  
    }  
}  

1.6  根据特定注册表信息

// 通过读取主机具有虚拟机特性的注册表位置来判断是否处于虚拟机环境中。针对VMware可以判断注册表项HKEY_CLASSES_ROOT\\Applications\\VMwareHostOpen.exe;
BOOL CheckVMWare()  
{  
    HKEY hkey;  
    if (RegOpenKey(HKEY_CLASSES_ROOT, "\\Applications\\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS)  
    {  
        return TRUE;  
    }  
    else  
    {  
        return FALSE;  
    }  
} 

1.7 根据特定服务名

// 在VMware中通常会存在VMware物理磁盘助手服务和VMware Tools服务等;
BOOL CheckVMWare()  
{  
    int menu = 0;    
    //打开系统服务控制器    
    SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);   
    if(SCMan == NULL)    
    {  
        cout << GetLastError() << endl;  
        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++)    
    {   
        // 对比判断是否有 VMware 的服务
        if (strstr(service_status[i].lpDisplayName, "VMware Tools")!=NULL || strstr(service_status[i].lpDisplayName, "VMware 物理磁盘助手服务")!=NULL)  
        {  
            return TRUE;  
        }  
    }    
    //关闭服务管理器的句柄   
    CloseServiceHandle(SCMan);   
    return FALSE;  
}  

1.8 检测开机时间

// 许多沙箱检测完毕后会重置系统
// 我们使用WINAPI GetTickCount()来获取机器已运行的时间(以秒为单位)。
// 然后判断开机运行时间是否大于1个小时,如果开机时间小于1小时就返回false。
// 检测开机时间
bool CheckUptime()
{
	DWORD dwUpTime = GetTickCount();
	if (dwUpTime < 3600000)
	{
		return false;
	}
	else
		return true;
}

原创文章,转载请注明: 转载自Windows内核安全驱动编程

本文链接地址: 关于 Windows UserMode 反虚拟机技术(总结)

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注