首页
社区
课程
招聘
[求助]VT对DR拦截的时候,总会发生任务切换,造成虚拟机卡死
发表于: 2015-12-18 17:10 7535

[求助]VT对DR拦截的时候,总会发生任务切换,造成虚拟机卡死

2015-12-18 17:10
7535
大家好,这几天在研究VT,目前只处理了CPUID VMCALL CR和DR,在没处理DR前,VT运行正常,系统也不会卡死,也没有任务切换发生VM-EXIT,但是当拦截DR的时候,每次都会虚拟机卡死,看了下ExitReason是9,ExitQualifiction是0xC0000050, 发现每次都是由于任务切换造成系统卡死的,我学了下joen牛中DdvpDbg的处理,没有对任务切换进行处理,直接GuestEIP+指令长度,然后VmResume,虚拟机照样卡死,这是我的HOST入口点的代码:

//////////////// 虚拟机处理系统 ///////
__declspec( naked ) VOID VMMEntryPoint( )
{
	_asm
	{
		CLI
			PUSHAD
			//	保存通用寄存器
			MOV GuestEAX, EAX
			MOV GuestEBX, EBX
			MOV GuestECX, ECX
			MOV GuestEDX, EDX
			MOV GuestESI, ESI
			MOV GuestEDI, EDI
			MOV GuestEBP, EBP
			
	} 
	
	ReadGuestState();
	
	GuestResumeEIP = GuestEIP + ExitInstructionLength;
	VMWRITE(GUEST_RIP,(ULONG)GuestResumeEIP );		//0x0000681E 
	

	
	if(ExitReason==0xA)				// CPUID
	{
		if(GuestEAX==0x123)
		{		
			_asm{
				POPAD
				MOV EBX,0x11111111
				MOV ECX,0x22222222
				MOV EDX,0x33333333
				JMP Resume
			} 
			
		}
		
		else{
			_asm{
				POPAD
				CPUID
				JMP Resume
			}
			
		}
		
		
	}
	
	if(ExitReason==0x12)    		// VMCALL
	{
		if(GuestEAX==0x1234)	//Unload Handler
		{
			VMXOFF();
			_asm{
				POPAD
					MOV ESP, GuestESP	
					STI
					JMP  GuestResumeEIP
			}
		}
		else
		{
			_asm
			{
				POPAD
					JMP	Resume
			}
		}
	}
	
	if(ExitReason==0x1C)			// Control Register
	{
		
		if( HandlerLogging ) Log( "Control Register Access detected.", 0 );
		
		movcrControlRegister= (ExitQualification & 0x0000000F);		// bit0-bit3
		movcrAccessType= ( (ExitQualification>>4) & 0x00000003);	// bit4-bit5
		movcrOprandType= ( (ExitQualification>>6) & 0x00000001);	// bit6
		movcrGeneralPurposeRegister= ( (ExitQualification>>8) &0xF);// bit8-bit11
		movcrLmswSourceData= ( (ExitQualification>>16) &0xFFFF); 	// bit16-bit31 
		
		if( HandlerLogging )
		{
			Log("movcrControlRegister", movcrControlRegister);
			Log("movcrAccessType", movcrAccessType);
			Log("movcrOprandType", movcrOprandType);
			Log("movcrGeneralPurposeRegister", movcrGeneralPurposeRegister);
			Log("movcrLmswSourceData", movcrLmswSourceData);
			
		}
		
		// MOV CR3,Register
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==0)
		{
			VMWRITE(GUEST_CR3, GuestEAX);
			_asm POPAD
			_asm JMP Resume
		}	
		
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==1)
		{
			VMWRITE(GUEST_CR3, GuestECX);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==2)
		{
			VMWRITE(GUEST_CR3, GuestEDX);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==3)
		{
			VMWRITE(GUEST_CR3, GuestEBX);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==4)
		{
			VMWRITE(GUEST_CR3, GuestESP);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==5)
		{
			VMWRITE(GUEST_CR3, GuestEBP);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==6)
		{
			VMWRITE(GUEST_CR3, GuestESI);
			_asm POPAD
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==0 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==7)
		{
			VMWRITE(GUEST_CR3, GuestEDI);
			_asm POPAD
			_asm JMP Resume
		}

		// MOV Register,CR3	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==0)
		{
			_asm POPAD
			_asm MOV EAX, GuestCR3
			_asm JMP Resume
		}
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==1)
		{
			_asm POPAD
			_asm MOV ECX, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==2)
		{
			_asm POPAD
			_asm MOV EDX, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==3)
		{
			_asm POPAD
			_asm MOV EBX, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==4)
		{
			_asm POPAD
			_asm MOV ESP, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==5)
		{
			_asm POPAD
			_asm MOV EBP, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==6)
		{
			_asm POPAD
			_asm MOV ESI, GuestCR3
			_asm JMP Resume
		}	
		if( movcrControlRegister==0x3 && movcrAccessType==1 &&  movcrOprandType==0 && movcrGeneralPurposeRegister==7)
		{
			_asm POPAD
			_asm MOV EDI, GuestCR3
			_asm JMP Resume
		}			
	
	}

	if(ExitReason==0x1D)			// DR
	{
		PrintGuestState();
		DispatchDrHandler();
	}
	
	if(ExitReason==0x9)
	{	
		_asm{
			POPAD
			JMP     Resume
		}
	}
	
	PrintGuestState();

	
Resume:	
		
	_asm
	{	
		STI
			_emit	0x0F	// VMRESUME
			_emit	0x01
			_emit	0xC3
	}
	
	
}

VOID ReadGuestState()
{
	HandlerLogging=0;

	ExitInstructionLength = VMREAD(VM_EXIT_INSTRUCTION_LEN);
	ExitReason = VMREAD(VM_EXIT_REASON);
	ExitQualification=VMREAD(EXIT_QUALIFICATION);
	
	GuestEIP = VMREAD(GUEST_RIP);
	GuestESP = VMREAD(GUEST_RSP);
	GuestCR3 = VMREAD(GUEST_CR3);
	GuestDR7 = VMREAD(GUEST_DR7);	
}

VOID PrintGuestState()
{
	Log( "----- VMM  Guest  -----", 0 );
	
	Log("The GuestEAX:",GuestEAX);
	Log("The GuestECX:",GuestECX);
	Log("The GuestEDX:",GuestEDX);
	Log("The GuestEBX:",GuestEBX);
	Log("The GuestESP:",GuestESP);
	Log("The GuestEBP:",GuestEBP);
	Log("The GuestESI:",GuestESI);
	Log("The GuestEDI:",GuestEDI);
	Log("The GuestDR7:",GuestDR7);
	
	Log("The ExitReason:",ExitReason);
	Log("The ExitQualification:",ExitQualification);
	


}



这是我对DR的处理:

VOID DispatchDrHandler()
{
	movdrRegister   = ( (ExitQualification) & 0x7);					// bit0-bit2
	movdrDirection  = ( (ExitQualification>>4) & 0x1);				// bit4
	movdrGeneralPurposeRegister = ( (ExitQualification>>8) & 0xF);	// bit8-bit11
	
	SourceReg=0;
	DR=0;
	
	// MOV DR, Reg
	if(movdrDirection==0){
		
		if(movdrGeneralPurposeRegister==0){
			SourceReg=GuestEAX;
		}
		if(movdrGeneralPurposeRegister==1){
			SourceReg=GuestECX;
		}	
		if(movdrGeneralPurposeRegister==2){
			SourceReg=GuestEDX;
		}	
		if(movdrGeneralPurposeRegister==3){
			SourceReg=GuestEBX;
		}
		if(movdrGeneralPurposeRegister==4){
			SourceReg=GuestESP;
		}
		if(movdrGeneralPurposeRegister==5){
			SourceReg=GuestEBP;
		}
		if(movdrGeneralPurposeRegister==6){
			SourceReg=GuestESI;
		}
		if(movdrGeneralPurposeRegister==7){
			SourceReg=GuestEDI;
		}

		if(movdrRegister==0){
			_asm{
				MOV EAX, SourceReg
				MOV DR0, EAX
				POPAD
				JMP Resume
			} 	
		}		
		if(movdrRegister==1){
			_asm{
				MOV EAX, SourceReg
				MOV DR1, EAX
				POPAD
				JMP Resume
			} 			
		}		
		if(movdrRegister==2){
			_asm{
				MOV EAX, SourceReg
				MOV DR2, EAX
				POPAD
				JMP Resume
			} 			
		}		
		if(movdrRegister==3){
			_asm{
				MOV EAX, SourceReg
				MOV DR3, EAX
				POPAD
				JMP Resume
			} 			
		}
		if(movdrRegister==4){
			_asm xchg sp, sp
		}
			
		if(movdrRegister==5){
			_asm xchg sp, sp
		}
		
		if(movdrRegister==6){
			_asm{
				MOV EAX, SourceReg
				MOV DR6, EAX
				POPAD
				JMP Resume
			} 			
		}		
		if(movdrRegister==7){
			VMWRITE(GUEST_DR7, SourceReg);
			_asm{
				POPAD
				JMP Resume
			} 			
		}		
		
	}
	
	// MOV Reg,DR
	if(movdrDirection==1){
		
		if(movdrRegister==0){
			_asm{
				MOV EAX, DR0
				MOV DR,  EAX
			}
		}
		if(movdrRegister==1){
			_asm{
				MOV EAX, DR1
				MOV DR,  EAX
			}
		}
		if(movdrRegister==2){
			_asm{
				MOV EAX, DR2
				MOV DR,  EAX
			}
		}		
		
		if(movdrRegister==3){
			_asm{
				MOV EAX, DR3
				MOV DR,  EAX
			}
		}
		
		if(movdrRegister==4){
			_asm xchg sp, sp
		}
			
		if(movdrRegister==5){
			_asm xchg sp, sp
		}
						
		if(movdrRegister==6){
			_asm{
				MOV EAX, DR6
				MOV DR,  EAX
			}
		}		
		if(movdrRegister==7){
			DR=VMREAD(GUEST_DR7);
		}	
	
		if(movdrGeneralPurposeRegister==0){
			_asm POPAD
			_asm MOV EAX, DR
			_asm JMP Resume
		}		
		
		if(movdrGeneralPurposeRegister==1){
			_asm POPAD
			_asm MOV ECX, DR
			_asm JMP Resume
		}		
		if(movdrGeneralPurposeRegister==2){
			_asm POPAD
			_asm MOV EDX, DR
			_asm JMP Resume		
		}
		
		if(movdrGeneralPurposeRegister==3){
			_asm POPAD
			_asm MOV EBX, DR
			_asm JMP Resume		
		}
				
		if(movdrGeneralPurposeRegister==4){
			_asm POPAD
			_asm MOV ESP, DR
			_asm JMP Resume		
		}
		
		if(movdrGeneralPurposeRegister==5){
			_asm POPAD
			_asm MOV EBP, DR
			_asm JMP Resume	
		}
		if(movdrGeneralPurposeRegister==6){
			_asm POPAD
			_asm MOV ESI, DR
			_asm JMP Resume			
		}
	
		if(movdrGeneralPurposeRegister==7){
			_asm POPAD
			_asm MOV EDI, DR
			_asm JMP Resume			
		}
		
	}

	//LogDrHandler();
	
Resume:	
		
	_asm
	{	
		STI
			_emit	0x0F	// VMRESUME
			_emit	0x01
			_emit	0xC3
	}		
	
}


这是我的文件 VtTest.rar

非常感谢了!!!

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 0
支持
分享
最新回复 (17)
雪    币: 99
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我最近开始也在研究vt,如果方便,pm下联系方式,可以交流交流
2015-12-18 18:34
0
雪    币: 1
活跃值: (1174)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
我没仔细看(只要把host域例如HOST_CR3弄成跟guest一样的就不用看了呵呵),瞎猜的。

任务切换是无条件陷入,按理是必须处理的。ExitReason为任务切换,说明Guest里确实出现了Task switch,
32位windows的IDT里是有任务门的,Double Fault就是。

至于具体原因,可能出在非hypervisor代码部分,#DF是很容易出的(如内核栈溢出)。当然你也可以检查下是否由VMX root代码造成。

另外这种内嵌汇编最好少用,尽可能用intrinsic,减小出错概率。
2015-12-18 19:05
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
4
加我qq吧 752392995
2015-12-18 19:46
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
5
你好,大神,你的意思是说把GUEST_CR3和HOST_CR3设置成一样的话,就能避免任务切换了麽,还有个疑问就是我在处理CR操作的时候,却没有一个任务切换发生的,一上午了,一个都没有,今天听一个大神说任务切换会造成线程切换,线程切换会对DR进行访问,所以造成了任务切换,可是我看DdvpDbg好像也是这么处理的啊
2015-12-18 19:50
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
6
问题应该出在DR处理上吧~
如果有TS那么什么时候会有TS~
2015-12-18 20:11
0
雪    币: 1
活跃值: (1174)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
把HOST的环境弄成跟Guest一样是错误的做法。

任务切换是指CPU的任务门机制,这个似乎与操作系统的线程没什么关系。可参考邓志的<x86/x64体系探索及编程>。顺便说一下x64下取消了任务门机制。

如果你实现了任务切换的模拟,很可能VMRESUME后是#DF造成的蓝屏
2015-12-18 20:18
0
雪    币: 4922
活跃值: (2345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
求指教vt过保护
2015-12-18 20:49
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
9
你好,V校大神,我对于DR的处理就是上面的那个代码,我在DR中对于DR0-DR3 DR6的读写操作是进行直接赋值的,然后直接ppopad  resume,对于DR7的读写是通过VmxRead和VmxWrite GUEST_DR7,然后POPAD RESUME的,我也是很奇怪,对于DR的拦截我是前天晚上写的,就在昨天上午,用其他驱动和OD测试驱动的时候(我用的MOV DR7,esi  MOV ESI,DR7测试的),系统还是正常的,只是有一个问题,就是无论我VMWRITE GUEST_DR7中任何值(紧接着,我用VMREAD测试,也确实成功写入GUEST_DR7了),resume后,Guest环境中的DR7都是0x400,然后我就以为是VM-ENTRY的load debug字段和VM-EXIT字段的save debug导致的,然后就又把他们清零,启动VT后,GUEST的DR7测试结果,返回值仍然是0x400,后来又恢复load debug和save debug字段,也不知道怎么了,虚拟机就卡死了,从昨天下午卡死到今晚,仍然不知道怎么回事,定位了下GuestEIP,好像是个ReleaseSpinLockFromDpclevel这种函数,V校看到后能不能给出个解决办法啊,我是实在没办法了
2015-12-18 20:53
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
10
我现在最大的怀疑就是对DR的处理了,可是我检查不下十五遍了,然后又调试,就是找不到错误,大神你12年就研究VT了,能不能帮我看下上面关于DR处理的代码呢,我看雪币可以全部给你,只要我能解决这个问题,我也是实在没辙了,唉。。。
2015-12-18 20:57
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
11
你先把DR处理的过程做成什么都不做的做测试~看看怎么死的~
这一拖内嵌汇编实在无法直视~
2015-12-18 21:27
0
雪    币: 6524
活跃值: (4316)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
12
DRX对线程的.
2015-12-18 21:48
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
VT的代码可不好调试啊
2015-12-19 04:01
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
14
你好, V校大大,我刚才做了个测试,就是在DR处理的过程中什么也不做,只进行了输出信息
       if(ExitReason==0x1D){
                PrintGuestState();

        }
PrintGuestState是输出ExitReason和ExitQualification的信息,然后我在处理任务切换的函数那里下了个断点,事实证明,半个小时过去了,系统并没有进行任务切换,而是一直进入了DR处理函数中,也就是一直显示ExitReason和ExitQualification的信息,这是不是代表我DR操作中,存在什么问题呢
2015-12-19 11:31
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
15
我也不知道怎么回事,可能是因为GUEST和HOST是同一个GDTR和CR3的缘故,现在可以在HOST_RIP处下断,也可以正常调试的
2015-12-19 11:35
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
16
嗯啊,兄台,我刚刚测试了下,DR操作中什么也不做,并没有任务切换发生,所以我现在确信是DR中出问题了,现在在一点点的排除
2015-12-19 11:36
0
雪    币: 302
活跃值: (246)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
17
感谢各位了,找出错误了,我在DR操作中是这么做的
        if(ExitReason==0x1D)                        // DR
        {
                PrintGuestState();
       
                DispatchDrHandler();
       }
我DispatchDrHandler保持在单独一个文件中,然后在进行DR赋值后就进行POPAD Resume了,可是我忘了一件很重要的事,我忘了恢复栈了,因为CALL会将下一条地址入栈,而且DispatchDrHandler函数还会进行几个PUSH操作,分别是push ebp,  push esi, push edi, 加上刚才的CALL造成的push  共有四个入栈操作,所以我用了两种办法可以解决:
    第一就是把DispatchDrHandler函数设置__declspec( naked )裸函数,然后在第一条指令进行
      _asm POP EAX,此时栈已经恢复成PUSHAD入栈时的样子,POPAD Resume后,系统正常
   第二就是直接  _asm ADD ESP, 0xC  然后POPAD Resume,系统仍然正常

总结:  以后写代码千万千万得注重细节啊,细节真的决定成败!!!@
2015-12-19 12:14
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
学习一下
2015-12-19 16:53
0
游客
登录 | 注册 方可回帖
返回
//