检测虚拟机环境的常见技术

news2024/11/15 23:22:31

下面列出检测 VMware 虚拟机的常见技术:

#include <iostream>
#include <windows.h>
#include <sysinfoapi.h>
#include <comdef.h>
#include <Wbemidl.h>
#include <ShlObj.h>
#include <LM.h>
#include <TlHelp32.h>
#include <string.h>
#include <string>
#include <atlbase.h>
#include <Iphlpapi.h>

#include <Psapi.h>
#include <shlwapi.h>  //PathFileExists
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "shlwapi.lib")

#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "netapi32.lib")
#pragma comment(lib,"Iphlpapi.lib") //GetAdaptersInfo需要添加Iphlpapi.lib库

#define  ARRAY_SIZE 1024
using namespace std;

// 检查管理员权限
BOOL isAdmin()
{
	BOOL bElevated = FALSE;
	HANDLE hToken = NULL;

	// Get current process token
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
		return FALSE;

	TOKEN_ELEVATION tokenEle;
	DWORD dwRetLen = 0;

	// Retrieve token elevation information
	if (GetTokenInformation(hToken, TokenElevation, &tokenEle, sizeof(tokenEle), &dwRetLen))
	{
		if (dwRetLen == sizeof(tokenEle))
		{
			bElevated = tokenEle.TokenIsElevated;
		}
	}

	CloseHandle(hToken);
	return bElevated;
}

// 检查CPU核心数
// SYSTEM_INFO.dwNumberOfProcessors
BOOL checkCPUCores(INT cores)
{
	INT i = 0;
	_asm { // x64编译模式下不支持__asm的汇编嵌入
		mov eax, dword ptr fs : [0x18] ; // TEB
		mov eax, dword ptr ds : [eax + 0x30] ; // PEB
		mov eax, dword ptr ds : [eax + 0x64] ;
		mov i, eax;
	}
	return i < cores;
}

// 检查CPU温度(需要管理员权限)
// Get-WMIObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"
// VM中无返回结果
// https://docs.microsoft.com/en-us/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer
BOOL checkCPUTemperature()
{
	HRESULT hres;
	BOOL res = -1;

	do
	{
		// Step 1: --------------------------------------------------
	// Initialize COM. ------------------------------------------

		hres = CoInitializeEx(0, COINIT_MULTITHREADED);
		if (FAILED(hres))
		{
			// cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
			break;                  // Program has failed.
		}

		// Step 2: --------------------------------------------------
		// Set general COM security levels --------------------------

		hres = CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
			RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities 
			NULL                         // Reserved
		);

		if (FAILED(hres))
		{
			// cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
			CoUninitialize();
			break;                    // Program has failed.
		}

		// Step 3: ---------------------------------------------------
		// Obtain the initial locator to WMI -------------------------

		IWbemLocator* pLoc = NULL;

		hres = CoCreateInstance(
			CLSID_WbemLocator,
			0,
			CLSCTX_INPROC_SERVER,
			IID_IWbemLocator, (LPVOID*)&pLoc);

		if (FAILED(hres))
		{
			// cout << "Failed to create IWbemLocator object." << " Err code = 0x" << hex << hres << endl;
			CoUninitialize();
			break;                 // Program has failed.
		}

		// Step 4: -----------------------------------------------------
		// Connect to WMI through the IWbemLocator::ConnectServer method

		IWbemServices* pSvc = NULL;

		// Connect to the root\cimv2 namespace with
		// the current user and obtain pointer pSvc
		// to make IWbemServices calls.
		hres = pLoc->ConnectServer(
			// _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
			_bstr_t(L"ROOT\\WMI"),
			NULL,                    // User name. NULL = current user
			NULL,                    // User password. NULL = current
			0,                       // Locale. NULL indicates current
			NULL,                    // Security flags.
			0,                       // Authority (for example, Kerberos)
			0,                       // Context object 
			&pSvc                    // pointer to IWbemServices proxy
		);

		if (FAILED(hres))
		{
			// cout << "Could not connect. Error code = 0x" << hex << hres << endl;
			pLoc->Release();
			CoUninitialize();
			break;                // Program has failed.
		}

		// cout << "Connected to ROOT\\WMI WMI namespace" << endl;

		// Step 5: --------------------------------------------------
		// Set security levels on the proxy -------------------------

		hres = CoSetProxyBlanket(
			pSvc,                        // Indicates the proxy to set
			RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
			RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
			NULL,                        // Server principal name 
			RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
			RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
			NULL,                        // client identity
			EOAC_NONE                    // proxy capabilities 
		);

		if (FAILED(hres))
		{
			// cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
			pSvc->Release();
			pLoc->Release();
			CoUninitialize();
			break;               // Program has failed.
		}

		// Step 6: --------------------------------------------------
		// Use the IWbemServices pointer to make requests of WMI ----

		// For example, get the name of the operating system
		IEnumWbemClassObject* pEnumerator = NULL;
		hres = pSvc->ExecQuery(
			bstr_t("WQL"),
			bstr_t("SELECT * FROM MSAcpi_ThermalZoneTemperature"),
			WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
			NULL,
			&pEnumerator);

		if (FAILED(hres))
		{
			// cout << "Query for operating system name failed." << " Error code = 0x" << hex << hres << endl;
			pSvc->Release();
			pLoc->Release();
			CoUninitialize();
			break;               // Program has failed.
		}

		// Step 7: -------------------------------------------------
		// Get the data from the query in step 6 -------------------

		IWbemClassObject* pclsObj = NULL;
		ULONG uReturn = 0;

		while (pEnumerator)
		{
			HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

			if (0 == uReturn) // VM中结果为空
			{
				if (-1 == res)
				{
					res = TRUE;
				}
				break;
			}

			VARIANT vtProp;

			// Get the value of the Name property
			hr = pclsObj->Get(L"CurrentTemperature", 0, &vtProp, 0, 0);
			// res = vtProp.ullVal / 10.0 - 273.15; // 开氏转摄氏
			res = FALSE;

			VariantClear(&vtProp);

			pclsObj->Release();
		}

		// Cleanup
		// ========

		pSvc->Release();
		pLoc->Release();
		pEnumerator->Release();
		CoUninitialize();

	} while (false);

	return res;
}


// 检测MAC地址
// 00:05:69、00:0c:29、00:50:56开始的MAC地址与VMware相对应
// 00:03:ff开始的MAC地址与virtualpc对应
// 08:00:27开始的MAC地址与virtualbox对应
BOOL CheckMACEx()
{

	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();//PIP_ADAPTER_INFO结构体指针存储本机网卡信息
	unsigned long stSize = sizeof(IP_ADAPTER_INFO);         //得到结构体大小,用于GetAdaptersInfo参数
	int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);     //调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量;其中stSize参数既是一个输入量也是一个输出量
	wchar_t MAC[5][9] = { L"08-00-27", L"00-03-FF", L"00-0C-29" };//mac=="00-05-69", mac=="00-50-56"该方法不准确
	WCHAR buffer[20]{ 0 };
	//WCHAR* CurrentAPR;
	bool bRet = false;// 返回值

	if (ERROR_BUFFER_OVERFLOW == nRel)
	{
		//如果函数返回的是ERROR_BUFFER_OVERFLOW
		//则说明GetAdaptersInfo参数传递的内存空间不够,同时其传出stSize,表示需要的空间大小
		//这也是说明为什么stSize既是一个输入量也是一个输出量               
		delete pIpAdapterInfo;//释放原来的内存空间     
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];//重新申请内存空间用来存储所有网卡信息        
		nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);    //再次调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量
	}
	if (ERROR_SUCCESS == nRel)
	{
		//可能有多网卡,因此通过循环去判断
		while (pIpAdapterInfo)
		{
			wsprintf(buffer, L"%02X-%02X-%02X", pIpAdapterInfo->Address[0], pIpAdapterInfo->Address[1], pIpAdapterInfo->Address[2]);
			for (int i = 0; i < 3; ++i)
			{
				// 检查MAC地址前3位是否在拼接好的内容中。
				if (wcsstr(buffer, MAC[i]))
				{
					bRet = TRUE;
					break;
				}
			}

			pIpAdapterInfo = pIpAdapterInfo->Next;
		}

	}
	//释放内存空间
	if (pIpAdapterInfo)
	{
		delete[]pIpAdapterInfo;
	}

	return bRet;
}



// 检测内存大小
// SELECT * FROM Win32_ComputerSystem
// TotalPhysicalMemory
BOOL checkMemory(INT memory)
{
	_MEMORYSTATUSEX mst;
	mst.dwLength = sizeof(mst);
	GlobalMemoryStatusEx(&mst);
	if (mst.ullTotalPhys / (1024.0 * 1024 * 1024) < memory) // B
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// 检测磁盘大小(需要管理员权限)
// SELECT * FROM Win32_LogicalDisk
// Size
BOOL checkPhyDisk(INT disk)
{
	HANDLE hDrive;
	GET_LENGTH_INFORMATION size;
	DWORD lpBytes;
	hDrive = CreateFileA("\\\\.\\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if (hDrive == INVALID_HANDLE_VALUE)
	{
		CloseHandle(hDrive);
		return FALSE;
	}
	bool result = DeviceIoControl(hDrive, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &size, sizeof(GET_LENGTH_INFORMATION), &lpBytes, NULL);
	CloseHandle(hDrive);

	if ((size.Length.QuadPart / 1073741824) < disk)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// 检测进程
BOOL checkProcess()
{
	const char* list[5] = { "VBoxService.exe", "VBoxTray.exe", "VGAuthService.exe","vm3dservice.exe", "vmtoolsd.exe" };
	PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(pe32);
	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	BOOL bResult = Process32First(hProcessSnap, &pe32);
	while (bResult)
	{
		char sz_Name[MAX_PATH] = { 0 };
		WideCharToMultiByte(CP_ACP, 0, pe32.szExeFile, -1, sz_Name, sizeof(sz_Name), NULL, NULL);
		for (int i = 0; i < 4; ++i)
		{
			if (strcmp(sz_Name, list[i]) == 0)
			{
				return TRUE;
			}
		}
		bResult = Process32Next(hProcessSnap, &pe32);
	}
	return FALSE;
}

// 检测注册表和文件路径(可能需要管理员权限)
BOOL checkPath()
{
	HKEY hkey;
	int CoeckStrictMode = 0;
	WIN32_FIND_DATA wfd1, wfd2;


	if (RegOpenKeyA(HKEY_CLASSES_ROOT, "\\Applications\\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS ||
		RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Oracle\\VirtualBox Guest Additions", &hkey) == ERROR_SUCCESS)
	{
		return TRUE;
	}

	// 文件夹路径
	//if (!PathIsDirectoryA("C:\\Program Files\\VMware\\VMware Tools\\") || 
		//!PathIsDirectoryA("C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\"))
	//{
		//printf("E2\n");
		//return TRUE;
	//}

	HANDLE hFind1 = FindFirstFileW(L"C:\\Program Files\\VMware\\VMware Tools\\", &wfd1);
	HANDLE hFind2 = FindFirstFileW(L"C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\", &wfd2);
	if (INVALID_HANDLE_VALUE != hFind1 && (wfd1.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		CoeckStrictMode++;
	if (INVALID_HANDLE_VALUE != hFind2 && (wfd2.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		CoeckStrictMode++;

	CloseHandle(hFind1);
	CloseHandle(hFind2);

	if (PathFileExistsW(L"C:\\Program Files\\VMware\\VMware Tools\\") ||
		PathFileExistsW(L"C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\"))
		CoeckStrictMode++;


	if (INVALID_FILE_ATTRIBUTES != GetFileAttributesW(L"C:\\Program Files\\VMware\\VMware Tools\\")
		||
		INVALID_FILE_ATTRIBUTES != GetFileAttributesW(L"C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\"))
		CoeckStrictMode++;


	if (CoeckStrictMode > 1) return TRUE;

	return FALSE;
}


int EnumDriverDevice() {
	DWORD cbNeeded = 0; // drivers[] 返回的字节数
	LPVOID drivers[ARRAY_SIZE] = { 0 }; // 驱动程序地址列表数组
	int cDrivers = 0;	// 驱动个数
	int CoeckStrictMode = 0;

	const wchar_t* wsDRVFile[8] = {
		L"vmmouse.sys",
		L"vmusbmouse.sys",
		L"vmrawdsk.sys",
		L"vmmemctl.sys",
		L"vm3dmp.sys",
		L"vm3dmp_loader.sys",
		L"vm3dmp-debug.sys",
		L"vm3dmp-stats.sys"
	};


	if (K32EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) // EnumDeviceDrivers 检索每个驱动文件的加载地址
	{
		wchar_t szDriver[ARRAY_SIZE] = { 0 };	// 驱动文件名
		wchar_t szPath[ARRAY_SIZE] = { 0 };	// 存放驱动文件全路径
		wchar_t szSystemPath[ARRAY_SIZE] = { 0 }; // 存放 system32 文件夹路径
		cDrivers = cbNeeded / sizeof(LPVOID);	// 驱动个数

		//得到C:\Windows\system32\dbghelp.dll
		GetSystemDirectoryW(szSystemPath, sizeof(szSystemPath));
		wcscat_s(szSystemPath, L"\\dbghelp.dll");

		for (int i = 0; i < cDrivers; i++)
		{
			if (K32GetDeviceDriverBaseNameW(drivers[i], szDriver, sizeof(szDriver) / sizeof(LPVOID)))
			{
				for (int i = 0; i < 8; i++)
					if (wcscmp(szDriver, wsDRVFile[i]) == 0)
						CoeckStrictMode++;
				// 打印驱动名
				//printf("【%d】:%ws\n", i + 1, szDriver);

				// 打印驱动文件路径
				//GetDeviceDriverFileName(drivers[i], szPath, sizeof(szPath));
				//printf("%s\n", szPath);
			}
		}
	}
	return CoeckStrictMode;
}


BOOL checkVMDriversFile()
{
	if (EnumDriverDevice() >= 1) return TRUE;

	return FALSE;
}

// 检测服务
BOOL checkSerivce()
{
	int menu = 0;
	// 打开系统服务控制器    
	SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
	if (SCMan == NULL)
	{
		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)
	{
		return -1;
	}
	for (DWORD i = 0; i < ServicesReturned; i++)
	{
		if (strstr(service_status[i].lpDisplayName, "VMware Tools") != NULL ||
			strstr(service_status[i].lpDisplayName, "VMware 物理磁盘助手服务") != NULL || 
			strstr(service_status[i].lpDisplayName, "Virtual Machine") != NULL ||
			strstr(service_status[i].lpDisplayName, "VirtualBox Guest") != NULL)
		{
			return TRUE;
		}
	}
	//关闭服务管理器的句柄   
	CloseServiceHandle(SCMan);
	return FALSE;
}

// 检测开机时间
BOOL checkUptime(DWORD msTime)
{
	DWORD UpTime = GetTickCount();
	if (UpTime < msTime)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// 使用CPUID指令
// 当EAX=1时,CPUID的ECX中HYPERV_HYPERVISOR_PRESENT_BIT标识是否在虚拟环境中
BOOL checkCPUID()
{
	DWORD dw_ecx;
	bool bFlag = true;
	_asm {
		pushad; // 将32位通用寄存器压入堆栈
		pushfd; // 将32位标志寄存器EFLAGS压入堆栈
		mov eax, 1; // Processor Info and Feature Bits
		cpuid; // 根据传递给EAX寄存器的值,将对应的信息返回给EAX、EBX、ECX、EDX
		mov dw_ecx, ecx; // Feature Information
		and ecx, 0x80000000; // Hypervisor present (always zero on physical CPUs) 即 HYPERV_HYPERVISOR_PRESENT_BIT
		test ecx, ecx; // AND为0的话ZF=1
		setz[bFlag]; // ZF为1的话bFlag=1
		popfd;
		popad;
	}
	if (bFlag) // 真实机器
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}

// 检测TEMP目录下的文件数量
BOOL checkTempDir(INT aNum)
{
	int file_count = 0;
	DWORD dwRet;
	LPSTR pszOldVal;
	pszOldVal = (LPSTR)malloc(MAX_PATH * sizeof(char));
	dwRet = GetEnvironmentVariableA("TEMP", pszOldVal, MAX_PATH);

	std::string stdstr = pszOldVal;
	stdstr += "\\*";

	LPSTR s = const_cast<char*>(stdstr.c_str());

	WIN32_FIND_DATAA data;
	HANDLE hFind = FindFirstFileA(s, &data);
	if (hFind != INVALID_HANDLE_VALUE)
	{
		do
		{
			file_count++;
		} while (FindNextFileA(hFind, &data));
		FindClose(hFind);
	}

	if (file_count < aNum)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// 检测主板序列号、主机型号、系统盘所在磁盘名称等硬件信息
// WMI ROOT\\CIMV2
BOOL ManageWMIInfo(string& result, string table, wstring wcol)
{
	char bord[1024];
	HRESULT hres = CoInitialize(0);

	IWbemLocator* pLoc = NULL;
	hres = CoCreateInstance(
		CLSID_WbemLocator,
		0,
		CLSCTX_INPROC_SERVER,
		IID_IWbemLocator, (LPVOID*)&pLoc
	);

	if (FAILED(hres))
	{
		CoUninitialize();
		return FALSE;
	}

	IWbemServices* pSvc = NULL;
	hres = pLoc->ConnectServer(
		_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace  
		NULL, // User name. NULL = current user  
		NULL, // User password. NULL = current  
		0, // Locale. NULL indicates current  
		NULL, // Security flags.  
		0, // Authority (e.g. Kerberos)  
		0, // Context object   
		&pSvc // pointer to IWbemServices proxy  
	);
	if (FAILED(hres))
	{
		pLoc->Release();
		CoUninitialize();
		return FALSE;
	}

	hres = CoSetProxyBlanket(
		pSvc, // Indicates the proxy to set  
		RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx  
		RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx  
		NULL, // Server principal name   
		RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx   
		RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx  
		NULL, // client identity  
		EOAC_NONE // proxy capabilities   
	);
	if (FAILED(hres))
	{
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return FALSE;
	}

	IEnumWbemClassObject* pEnumerator = NULL;
	string select = "SELECT * FROM " + table;
	hres = pSvc->ExecQuery(
		bstr_t("WQL"),
		bstr_t(select.c_str()),
		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
		NULL,
		&pEnumerator
	);
	if (FAILED(hres))
	{
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return FALSE;
	}

	ULONG uReturn = 0;
	IWbemClassObject* pclsObj;
	while (pEnumerator)
	{
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
		if (0 == uReturn)
		{
			break;
		}
		VARIANT vtProp;
		VariantInit(&vtProp);
		hr = pclsObj->Get(wcol.c_str(), 0, &vtProp, 0, 0);
		if (!FAILED(hr))
		{
			CW2A tmpstr(vtProp.bstrVal);
			strcpy_s(bord, 200, tmpstr);
			result = bord;
		}
		VariantClear(&vtProp);
		pclsObj->Release();
	}

	pSvc->Release();
	pLoc->Release();
	pEnumerator->Release();
	CoUninitialize();

	return TRUE;
}

// 检测磁盘驱动程序发布商名称
BOOL checkHardwareInfo()
{
	BOOL bRet = TRUE;

	do
	{
		string ret;
		ManageWMIInfo(ret, "Win32_BaseBoard", L"SerialNumber");
		if (ret == "None")
		{
			break;
		}

		ManageWMIInfo(ret, "Win32_DiskDrive", L"Caption");
		if (ret.find("VMware") != string::npos || 
			ret.find("VBOX") != string::npos || 
			ret.find("Virtual HD") != string::npos)
		{
			break;
		}

		ManageWMIInfo(ret, "Win32_computersystem", L"Model");
		if (ret.find("VMware") != string::npos || 
			ret.find("VirtualBox") != string::npos || 
			ret.find("Virtual Machine") != string::npos)
		{
			break;
		}

		bRet = FALSE;
	} while (FALSE);

	return bRet;
}


// 使用sgdt和sldt指令探测VMware的技术通常被称为No Pill
// 通过禁用VMware加速可以防止No Pill技术的探测
BOOL checkNoPill()
{
	ULONG xdt = 0;
	ULONG InVM = 0;
	__asm
	{
		push edx
		sidt[esp - 2] // 将中断描述符表寄存器IDTR的内容存入指定地址单元
		pop edx
		nop
		mov xdt, edx
	}
	if (xdt > 0xd0000000)
	{
		InVM = 1;
	}

	__asm
	{
		push edx
		sgdt[esp - 2] // 将全局描述符表格寄存器GDTR的内容存入指定地址单元
		pop edx
		nop
		mov xdt, edx
	}
	if (xdt > 0xd0000000)
	{
		InVM += 1;
	}

	if (InVM == 0)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}

// 检测IO端口
// VMware会监视in指令的执行,并捕获目的通信端口为0x5668(VX)的I/O
// VMware会检查第二个操作数是否是VX,在这种情况发生时
// EAX寄存器载入的值是0x564D5868(VMXh)
// ECX寄存器为在端口上执行相应操作的值
// 0xA:get VMware version type
// 0x14:get the memory size
// 则EBX为magic数VMXh,ECX为版本号
// 在真实机器上运行会触发EXCEPTION_EXECUTE_HANDLER异常
// https://www.aldeid.com/wiki/VMXh-Magic-Value
BOOL checkIOPort()
{
	bool rc = true;
	__try
	{
		__asm
		{
			push   edx
			push   ecx
			push   ebx
			mov    eax, 'VMXh'
			mov    ebx, 0
			mov    ecx, 10
			mov    edx, 'VX'
			in     eax, dx // 从一个源操作数指定的端口dx复制数据到目的操作数指定的内存地址
			cmp    ebx, 'VMXh'
			setz[rc]
			pop    ebx
			pop    ecx
			pop    edx
		}
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		rc = false;
	}
	return rc;
}

// 检测无效指令
// VirtualPC使用一堆无效指令来允许虚拟机和VirtualPC之间连接,如果VirtualPC存在则不引起异常
DWORD IslnsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep)
{
	PCONTEXT ctx = ep->ContextRecord;
	ctx->Ebx = -1; // 未运行在VPC中  
	ctx->Eip += 4; // 跳过call VPC操作  
	return EXCEPTION_CONTINUE_EXECUTION;
}

BOOL checkUnISA()
{
	bool rc = TRUE;
	__try
	{
		__asm
		{
			push ebx
			mov ebx, 0
			mov eax, 1
			__emit 0fh // 在当前位置直接插入数据
			__emit 3fh
			__emit 07h
			__emit 0bh
			test ebx, ebx
			setz[rc]
			pop ebx
		}
	}
	__except (IslnsideVPC_exceptionFilter(GetExceptionInformation()))
	{
		rc = FALSE;
	}
	return rc;
}



int DetectVisualMachine(int *s, int drd) {
	int* p = s;
	if (drd == 0 && checkCPUTemperature()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 1 && checkPhyDisk(250)) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 2 && checkPath()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 3 && checkCPUCores(4)) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 4 && CheckMACEx()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}
	
	if (*p == 5 && checkMemory(4)) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 6 && checkProcess()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}


	if (*p == 7 && checkSerivce()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 8 && checkCPUID()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 9 && checkHardwareInfo()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 10 && checkIOPort()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}

	if (*p == 11 && checkVMDriversFile()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}


	if (*p == 12 && checkProcess()) {
		s = new int;
		*s = *p;
		*p = (*s) + 1;
		DetectVisualMachine(p, ++drd);
	}
	else {
		s = new int;
		*s = *p;
		*p = (*s);
		DetectVisualMachine(p, ++drd);
	}
}


int main()
{
	// 需要管理员权限
	if (isAdmin() && IsUserAnAdmin())
	{
		printf("[+] Admin\n");
		if (checkCPUTemperature())
		{
			printf("[-] checkCPUTemperature - VM\n");
		}
		else
		{
			printf("[+] checkCPUTemperature - not VM\n");
		}

		if (checkPhyDisk(250))
		{
			printf("[-] checkPhyDisk - VM\n");
		}
		else
		{
			printf("[+] checkPhyDisk - not VM\n");
		}

		if (checkPath())//
		{
			printf("[-] checkPath - VM\n");
		}
		else
		{
			printf("[+] checkPath - not VM\n");
		}

	}
	//printf("[+] Not Admin\n");

	if (checkCPUCores(4))
	{
		printf("[-] checkCPUCores - VM\n");
	}
	else
	{
		printf("[+] checkCPUCores - not VM\n");
	}


	if (CheckMACEx())
	{
		printf("[-] checkMAC - VM\n");
	}
	else
	{
		printf("[+] checkMAC - not VM\n");
	}

	if (checkMemory(4))
	{
		printf("[-] checkMemory - VM\n");
	}
	else
	{
		printf("[+] checkMemory - not VM\n");
	}

	if (checkProcess())
	{
		printf("[-] checkProcess - VM\n");
	}
	else
	{
		printf("[+] checkProcess - not VM\n");
	}

	if (checkSerivce())
	{
		printf("[-] checkSerivce - VM\n");
	}
	else
	{
		printf("[+] checkSerivce - not VM\n");
	}


	if (checkCPUID())
	{
		printf("[-] checkCPUID - VM\n");
	}
	else
	{
		printf("[+] checkCPUID - not VM\n");
	}

	/**
	if (checkTempDir(30))
	{
		printf("[-] checkTempDir - VM\n");
	}
	else
	{
		printf("[+] checkTempDir - not VM\n");
	}
	*/

	if (checkHardwareInfo())
	{
		printf("[-] checkHardwareInfo - VM\n");
	}
	else
	{
		printf("[+] checkHardwareInfo - not VM\n");
	}


	if (checkIOPort())
	{
		printf("[-] checkIOPort - VM\n");
	}
	else
	{
		printf("[+] checkIOPort - not VM\n");
	}


	if (checkVMDriversFile())
	{
		printf("[-] checkDriversFile - VM\n");
	}
	else
	{
		printf("[+] checkDriversFile - not VM\n");
	}

	cin.get();
	return 0;
}

虚拟机实测:


发布于:2024.03.10.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1508874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第16章——西瓜书强化学习

在强化学习中&#xff0c;智能体通过与环境的交互来学习如何做出决策。在每个时间步&#xff0c;智能体观察当前的环境状态&#xff0c;并根据其策略选择一个动作。环境会对智能体的动作做出响应&#xff0c;并给出一个奖励信号&#xff08;reward&#xff09;&#xff0c;该信…

2024 PhpStorm激活,分享几个PhpStorm激活的方案

文章目录 PhpStorm 公司简介我这边使用PhpStorm的理由PhpStorm 2023.3 最新变化AI Assistant 预览阶段结束 正式版基于 LLM 的代码补全测试代码生成编辑器内代码生成控制台中基于 AI 的错误解释 Pest 更新PHP 8.3 支持#[\Override] 特性新的 json_validate() 函数类型化类常量弃…

Java - 探究Java优雅退出的两种机制

文章目录 概述Java优雅停机_ ShutdownHook 机制步骤Code Java优雅停机_ 信号量机制SignalHandler 工作原理使用步骤Linux支持的信号量根据操作系统选择信号量Code 注意事项 概述 在Linux上通过kill -9 pid方式强制终止进程的副作用&#xff0c;这种方式虽然简单高效&#xff0…

git代码回退

对于代码的回退主要有 git reset 、git revert 1.git reset git reset commitId --soft&#xff1a;回退当前代码仓库到指定提交commitId&#xff0c;当前HEAD和commitId之间的修改会保留&#xff0c;这些修改会在暂存区。就是保留了add的状态git reset commitId --hard&…

WPF —— TextBlock、LineBreak RadioButton控件详解

一:TextBlock 1&#xff1a;TextBlock 简介 <LineBreak/> 换行 显示文本 标签内容和content属性共存 2、TextBlock 常用的属性 Foreground&#xff1a;TextBlock的文本内容的颜色。 Background&#xff1a;背景&#xff0c;获取或设置要用于填充内容区域背景的 Brush…

2-LINUX--Linux 系统文件类型与文件权限

一.文件类型 Linux 下所有的东西都可以看做文件&#xff0c;Linux 将文件分为以下几种类型&#xff1a; 1. 普通文件 ‘-’ 2. 目录文件 ‘d’ 3. 管道文件 ‘p’ 4. 链接文件 ‘l’ 5. 设备文件&#xff08;块设备 ’b’ 、字符设备 ‘c’&#xff09; 6. 套接字…

二叉搜索树题目:前序遍历构造二叉搜索树

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 解法三思路和算法代码复杂度分析 解法四思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;前序遍历构造二叉搜索树 出处&#xff1a;1008. …

2024年春招程序员个人简历范本(精选5篇|附模板)

HR浏览一份简历也就25秒左右,如果你连「好简历」都没有,怎么能找到好工作呢? 如果你不懂得如何在简历上展示自己,或者觉得怎么改简历都不出彩,那请你一定仔细读完。 Java开发工程师简历范本> 性别 男 年龄 24 学历 本科 张三 专业 计算机科学与技术 毕业院校 …

Python3虚拟环境之pipenv

pipenv是python官方推荐的包管理工具&#xff0c;集成了virtualenv, pip和pyenv三者的功能。集合了所有的包管理工具的长处&#xff0c;自动为项目创建和管理虚拟环境。 安装 pip install pipenv在Pycharm中使用 修改Pipfile的安装源参数url&#xff0c;改为https://pypi.tun…

20240309web前端_第一周作业_古诗词

作业三&#xff1a;古诗词 成果展示&#xff1a; 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…

BUUCTF---easyre1

1.记录一下第一次做逆向题目 2.题目描述&#xff0c;下载附件 3.解压之后是一个可执行文件&#xff0c;先用PE查看是否有壳 4.没有壳&#xff0c;接下来用ida打开&#xff0c;直接拖进ida即可&#xff0c;接下来使用快捷键fnshiftf12查看字符&#xff0c;若是没有出现搜索框&a…

探究精酿啤酒的秘密:原料中的天然酵母与纯净水质

在啤酒的世界中&#xff0c;Fendi Club精酿啤酒以其与众不同的口感和深远的余味吸引了全球的啤酒爱好者。而这一切&#xff0c;都归功于其选用的上好原料&#xff0c;特别是天然酵母和纯净水质。 天然酵母是啤酒的灵魂。与工业生产的啤酒酵母不同&#xff0c;天然酵母富含丰富的…

navicat过期了,直接用idea连接mysql

1、我的是社区版&#xff0c;需要下载一个插件&#xff0c;直接搜索安装即可。 2、找到data source&#xff0c;点击mysql 3、你们熟悉的&#xff0c;输入账户密码&#xff0c;点击test Connection测试是否连接成功 4、这个本来是在右边&#xff0c;但是你可以把他挪到左边。 5…

Neo4j 批量导入数据 从官方文档学习LOAD CSV 命令 小白可食用版

学习LOAD CSV&#x1f680; 在使用Neo4j进行大量数据导入的时候&#xff0c;发现如果用代码自动一行一行的导入效率过低&#xff0c;因此明白了为什么需要用到批量导入功能&#xff0c;在Neo4j中允许批量导入CSV文件格式&#xff0c;刚开始从网上的中看了各种半残的博客或者视频…

Servlet容器部署教程

Servlet容器介绍 Sercvlet是基于java的动态网站开发技术&#xff0c;其所有类和组件都是基于java实现的&#xff0c;要想使用Servlet&#xff0c;就必须提前配置好java运行环境。Servlet基于java&#xff0c;可以使用几乎全部的java API&#xff0c;所以它的功能异常强大&…

Oracle 层级查询(Hierarchical Queries)

如果一张表中的数据存在分级&#xff08;即数据间存在父子关系&#xff09;&#xff0c;利用普通SQL语句显示数据间的层级关系非常复杂&#xff0c;可能需要多次连接才能完整的展示出完成的层级关系&#xff0c;更困难的是你可能不知道数据到底有多少层。而利用Oracle的层级查询…

Android Framework 通过脚本动态修改应用私有文件执行权限

你只活一次 要悦己 脚本配置 Android_source/device/sprd/***/test/test_chmod.rc service test_chmod /vendor/bin/test_chmod.shuser rootdisabledoneshoton property:sys.test_chmodtruestart test_chmodAndroid_source/device/sprd/***/test/test_chmod.sh #!/system/bin/…

【CRC】一文搞懂CRC-8 SAE J1850 ZERO校验和

CRC在线计算 一、什么是 CRC 校验和 CRC —— Cyclic redundancy check 循环冗余校验&#xff0c;一种校验接收到的数据是否完整的算法&#xff0c;广泛应用于数据通信&#xff0c;大概流程如下 二、CRC-8 如何计算 首先&#xff0c;想要确定一个 CRC 算法&#xff0c;我们需…

论文笔记:Evaluating the Performance of Large Language Models on GAOKAO Benchmark

1 论文思路 采用zero-shot prompting的方式&#xff0c;将试题转化为ChatGPT的输入 对于数学题&#xff0c;将公式转化为latex输入 主观题由专业教师打分 2 数据 2010~2022年&#xff0c;一共13年间的全国A卷和全国B卷 3 结论 3.1 不同模型的zeroshot 高考总分 3.2 各科主…

web自动化测试框架都是有哪些?

Web自动化测试框架主要有以下几种&#xff1a; 1.Selenium&#xff1a;轻量级的Web自动化测试框架&#xff0c;支持多种Web浏览器和语言的集成。Selenium提供了一个IDE来录制和运行自动化测试脚本&#xff0c;还提供了WebDriver&#xff0c;可以通过编程语言编写自动化测试脚本…