检测当前环境是否是虚拟机

1、执行特权指令检测
在x86体系中,一些指令在获取硬件相关信息时不产生异常,如sidt、sgdt、sldt、cpuid等,而VMware因为性能原因并没有虚拟这些指令,所以意味着这些指令在vm虚拟机中和物理机中运行时会返回不同的结果。

Redpill
  简单说,就是通过运行sidt指令获取IDT寄存器的值(IDT: 中断描述符表,可以简单理解为查找处理中断时所用的函数,共256项,如第3项就是我们常用的int3断点)。Redpill的作者测试说明虚拟机中的IDT地址通常位于0xFFXXXXXX,而在真实主机上位于0x80xxxxxx。所以可通过判断执行SIDT指令后返回的第一字节是否大于0xD0,判断是否在虚拟机中。同时这项技术必须满足运行在单核处理器上,因为每个核心只有一个IDT表如果是多核切换就很难确定具体值了

可能会因为虚拟机的升级,导致结果不一样。
2、使用cpuid指令来检测是否设置了代码正在虚拟机程序上运行的特征位,有的虚拟机可能不会设置该特征位!

bool IsVMWare()int main(int argc,char* argv[])
{
    bool bRet;

	bRet = IsVMWare();
	if (bRet) {
		printf("运行在虚拟机环境");
	}
	else
	{
		printf("运行在真实物理机环境");
	}

	getchar();

	return 0;
}
bool IsVMWare()
{
	unsigned int cpuInfo[4];
	__cpuid((int*)cpuInfo, 1);
	return ((cpuInfo[2] >> 31) & 1) == 1;
}

3、LDT(局部描述符表)
sgdt与sldt指令探测技术,依赖于LDT(局部描述符表)由处理器分配而非操作系统分配的事实。因为Windows正常情况下不使用LDT,但VM提供了LDT的虚拟化支持,结果就是:真机中LDT位置为0,而在虚拟机,不为0。同时对于GTR,虚拟机中应为0xFFXXXXXX , 否则为真机。

inline bool IsVirtualPC_LDTCheck()
{
	unsigned short ldt_addr = 0;
	unsigned char ldtr[2];

	_asm sldt ldtr
	ldt_addr = *((unsigned short *)&ldtr);
	return ldt_addr != 0x00000000;
}

4、注册表检测

inline bool DetectVM()
{
	HKEY hKey;

	char szBuffer[64];

	unsigned long hSize = sizeof(szBuffer)-1;

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\BIOS\\", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
	{

		RegQueryValueEx(hKey, "SystemManufacturer", NULL, NULL, (unsigned char *)szBuffer, &hSize);

		if (strstr(szBuffer, "VMWARE"))
		{
			RegCloseKey(hKey);
			return true;
		}

		RegCloseKey(hKey);
	}

	return false;
}

5、GDT检测

inline bool IsVirtualPC_GDTCheck()
{
	unsigned int gdt_addr = 0;
	unsigned char gdtr[6];

	_asm sgdt gdtr
	gdt_addr = *((unsigned int *)&gdtr[2]);
	return (gdt_addr >> 24) == 0xff;
}

6、TSS检测

inline bool IsVirtualPC_TSSCheck()
{
	unsigned char mem[4] = { 0 };

	__asm str mem;
	return (mem[0] == 0x00) && (mem[1] == 0x40);
}

7、I/O通信端口检测
原理:使用IN指令来读取特定端口的数据进行两机通讯,但由于IN指令属于特权指令,在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为"EXCEPTION_PRIV_INSTRUCTION"的异常,而在虚拟机中并不会发生异常,在指定功能号为0xA/10(获取VMware版本)时,会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于虚拟机中。代码分析如下:

//查询I/O通信端口
BOOL CheckVMWare1()
{
        BOOL bResult = TRUE;
        __try
        {
                __asm
                {
                        push   edx
                        push   ecx
                        push   ebx                //保存环境
                        mov    eax, 'VMXh'
                        mov    ebx, 0             //将ebx清零
                        mov    ecx, 10            //指定功能号,用于获取VMWare版本,为0x14时获取VM内存大小
                        mov    edx, 'VX'          //端口号
                        in     eax, dx            //从端口edx 读取VMware到eax
                        cmp    ebx, 'VMXh'        //判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中
                        setz[bResult]             //为零 (ZF=1) 时设置字节
                        pop    ebx                //恢复环境
                        pop    ecx
                        pop    edx
                }
        }
        __except (EXCEPTION_EXECUTE_HANDLER)       //如果未处于VMware中,则触发此异常
        {
                bResult = FALSE;
        }
        return bResult;
}

8、枚举系统服务并匹配特定的虚拟机相关服务名称来判断
通过枚举系统服务的状态,循环遍历枚举到的服务状态,检查是否有与虚拟机相关的服务。

BOOL CheckVM()
{
	//打开系统服务控制器    
	SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
	if (SCMan == NULL) {
		//cout << GetLastError() << endl;
		//printf("OpenSCManager Eorror/n");
		return FALSE;
	}

	//保存系统服务的结构  
	LPENUM_SERVICE_STATUSA service_status;
	DWORD cbBytesNeeded = NULL;
	DWORD ServicesReturned = NULL;
	DWORD ResumeHandle = NULL;
	service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
	if (service_status == nullptr)
		return FALSE;

	//获取系统服务的简单信息    
	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 FALSE;
	}

	const char* vms[] = { 
		"VMware Tools", 
		"VMware 物理磁盘助手服务",
		"VirtualBox Guest",
		"Virtual Machine",
	};

	for (uint32_t i = 0; i < ServicesReturned; i++) {

		for (int n = 0; n < ARRAYSIZE(vms); n++) {

			if (strstr(service_status[i].lpDisplayName, vms[n]) != NULL)
				return TRUE;
		}
	}

	if (service_status != nullptr)
		LocalFree(service_status);
  
	CloseServiceHandle(SCMan);

	return FALSE;
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
可以通过监控系统调用来判断当前运行环境是否为虚拟机。在C/C++语言中,可以使用ptrace函数来监控系统调用,具体步骤如下: 1. 使用ptrace函数attach到当前进程。 2. 使用waitpid函数等待被监控的系统调用。 3. 使用peekuser函数获取寄存器值,判断是否为系统调用号。 4. 如果是系统调用,使用ptrace函数获取系统调用参数。 5. 判断系统调用参数中是否存在虚拟机相关的字符串或特征。 6. 如果检测到了虚拟机相关的字符串或特征,说明当前运行环境虚拟机。 以下是一个简单的C/C++代码示例: ```c++ #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/user.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { pid_t pid = getpid(); int status; struct user_regs_struct regs; long syscall_no, syscall_arg1, syscall_arg2, syscall_arg3; if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { perror("ptrace attach"); exit(1); } waitpid(pid, &status, 0); while (WIFSTOPPED(status)) { if (ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1) { perror("ptrace getregs"); exit(1); } syscall_no = regs.orig_rax; syscall_arg1 = regs.rdi; syscall_arg2 = regs.rsi; syscall_arg3 = regs.rdx; if (syscall_no == SYS_write) { char buf[1024]; ptrace(PTRACE_PEEKDATA, pid, syscall_arg2, &buf); if (strstr(buf, "VirtualBox") != NULL || strstr(buf, "VMware") != NULL) { printf("Detected virtual machine environment.\n"); break; } } if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == -1) { perror("ptrace syscall"); exit(1); } waitpid(pid, &status, 0); } ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; } ``` 该代码会监控系统调用write,并检查write的第二个参数中是否包含"VirtualBox"或"VMware"字符串,如果包含则说明当前环境虚拟机

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值