定制建站网站简单的网页设计作品
VT技术(编写一个VT框架)
1.VT技术介绍
1.技术介绍
1.VT技术
VT技术是Intel提供的虚拟化技术,全称为Intel Virtualization Technology。它是一套硬件和软件的解决方案,旨在增强虚拟化环境的性能、可靠性和安全性。VT技术允许在一台物理计算机上同时运行多个虚拟机,每个虚拟机都可以运行不同的操作系统和应用程序。
Intel VT(Intel Virtualization Technology)可以使单个CPU在虚拟化环境下模拟多个逻辑处理器(Virtual CPU),从而实现多个操作系统同时运行的能力。
VT技术分为软件虚拟化、容器虚拟化、虚拟化层翻译
- 软件虚拟化(Software Virtualization):这种虚拟化技术是基于软件实现的,它在一个宿主操作系统上运行虚拟化软件(如Vmware Workstation、Virtual PC),通过模拟硬件环境来创建和管理虚拟机。每个虚拟机运行的操作系统和应用程序都不需要进行修改。
- 容器虚拟化(Container Virtualization):容器虚拟化是一种轻量级的虚拟化技术,它将操作系统内核的特性,如Linux容器(LXC)或Docker容器,用于隔离应用程序和它们的运行环境。容器共享宿主操作系统的内核,因此不需要进行完全的操作系统虚拟化。
2.抽象的"Ring-1层"
在传统的计算机体系结构中,没有明确定义的"Ring -1"层。通常,计算机体系结构中的"Ring"层级是指特权级别或权限级别,用于控制对系统资源的访问。常见的层级包括Ring 0(内核态)和Ring 3(用户态)。
然而,"VT技术"Intel的虚拟化技术(Virtualization Technology),它允许在一台物理计算机上同时运行多个虚拟机。虚拟化技术引入了新的软件和硬件层级,以管理虚拟机的创建、运行和资源分配。
在虚拟化技术中,可以将主机操作系统视为Ring 0,并将虚拟机的操作系统视为Ring 3。这种情况下,可以认为虚拟化技术引入了一种抽象的"Ring -1"层,用于管理虚拟机的创建和资源分配。这个抽象的层级位于主机操作系统和虚拟机操作系统之间,可以看作是一种虚拟化管理的层级。
需要注意的是,"Ring -1"只是一种抽象概念,用于解释虚拟化技术中的层级关系,并不是传统计算机体系结构中的标准术语。实际上,不同的虚拟化技术可能采用不同的层级结构和术语,具体情况取决于所使用的虚拟化平台和技术。
2.关键词介绍
1.VMM(Virtual Machine Monitor)
虚拟机器监视器,是指在电脑上的软件,固件,或者是硬件,用来建立与执行虚拟机器
2.VMX(Virtual Machine Extensions)
处理器对虚拟化的处理器支持由一种称为VMX Opreation的处理器操作形式提供,VMX Opreation 有2种,VMX Root Opreation以及VMX Non-root Opreation,一般来说,一个VMM将在VMX Root Opreation中运行,而客户软件运行在VMX Non-root Opreation
3.VM(Virtual Machine)
VM指的是Virtual Machin 虚拟机
4.VMCS(Virtual Machine Control Structures)
逻辑处理器在执行VMX操作时,会使用虚拟机控制数据结构(VMCSs)。这些操作可以管理进出VMX Non-root Opreation的转换(VM条目和VM退出)的转换,以及VMX非根操作中的处理器行为。该结构由新的指令VMCLEAR、VMPTRLD、VMREAD和VMWRITE操作。
5.VMX Root Opreation
通常VMM将会在这种模式下运行
6.VMX Non-root Opreation
通常客户软件(虚拟机)将在这种环境下运行。两种类型的操作之间的转换称作VMX转换,从根操作模式转换到非根操作模式称作VMX进入(VMX Entry),相反从非根操作模式转换到根操作模式称作VMX退出(VMX Exit)
7.Guest software
每个虚拟机(VM)就是一个客户软件运行环境。
3.VMM软件的生命周期
以上图来描述VMM软件的生命周期
- 启用VMX:VMX操作的生命周期始于启用VMX扩展。在计算机启动时,虚拟机监视器(VMM)通过设置处理器的控制寄存器来启用VMX扩展。这个过程通常在操作系统引导期间或虚拟化软件加载时完成。
- 进入VMX操作模式:启用VMX后,处理器进入VMX操作模式。在这个模式下,处理器支持运行虚拟机和虚拟机监视器之间的切换。VMM负责管理虚拟机的创建、配置和执行。
- 虚拟机的创建和运行:在VMX操作模式下,VMM可以创建虚拟机实例并配置虚拟机的资源。虚拟机监视器通过VMCS(Virtual Machine Control Structure)来管理虚拟机的状态和控制信息。VMM在虚拟机和虚拟机监视器之间进行切换,使虚拟机可以在物理处理器上运行。
- 虚拟机监控和控制:在虚拟机运行期间,虚拟机监视器监控虚拟机的行为并提供必要的控制。它负责处理虚拟机的中断、异常和特权指令,并确保虚拟机之间和虚拟机与物理机之间的资源隔离。
- 退出VMX操作模式:当虚拟机执行完成或发生特定事件时,虚拟机监视器将控制权从虚拟机切换回虚拟机监视器。这个过程称为VMX退出。在退出过程中,虚拟机监视器可以读取和更新虚拟机的状态信息,并进行必要的处理。
- 禁用VMX:VMX操作的生命周期在虚拟化环境不再需要时结束。在关闭虚拟化软件或关闭计算机时,处理器的VMX扩展将被禁用,处理器将恢复到普通的非虚拟化模式。
总的来说,VMX Operation的生命周期涵盖了启用VMX扩展、进入VMX操作模式、虚拟机的创建和运行、虚拟机监控和控制、退出VMX操作模式以及禁用VMX扩展等关键阶段,以实现硬件虚拟化的支持和管理。
2.VT技术(二)检测CPU支持
1.使用CPUID指令检测CPU是否支持虚拟化
在进入VMX Opreation之前需要对CPU进行一系列检测,用于判断CPU硬件是否支持虚拟化技术。
CPUID指令在Ring3也可以使用,不需要进入Ring0层进行检测,以下为CPUID指令的使用详细说明
1.CPUID指令使用说明
- 语法
CPUID在执行时需要将所需要的查询编号传入eax寄存器当中
xor rax,rax
cpuid
- 功能
CPUID指令返回处理器的信息和功能支持,包括处理器厂商、处理器系列、功能位、缓存配置、支持的扩展功能等。
- 寄存器使用
- 输入:EAX寄存器用于传递查询的功能编号。
- 输出:EAX、EBX、ECX、EDX寄存器用于返回处理器的信息和功能。
2.CPUID指令参数说明
- 查询处理器厂商信息: 通过将EAX设置为0,执行CPUID指令,处理器厂商的标识符将返回在EBX、EDX和ECX寄存器中,以ASCII编码的形式表示。
- 查询扩展功能支持: 通过将EAX设置为7,执行CPUID指令,处理器支持的扩展功能信息将返回在EBX、ECX和EDX寄存器中。
- 查询缓存配置: 通过将EAX设置为2,执行CPUID指令,处理器的缓存配置信息将返回在EAX、EBX、ECX和EDX寄存器中。
- 查询CPUID最大支持功能编号: 通过将EAX设置为0x80000000,执行CPUID指令,最大支持的功能编号将返回在EAX寄存器中。
以上简单列出常用的几个指令参数,如果需要查询详细参数,可参考Intel白皮书卷二第三章(指令A-L)中的CPUID章节(书中详细介绍)
3.查询CPU是否支持VT技术
由于本文基于的系统是x64位操作系统,VS编译器中的编译器默认支持内联汇编(当然可以写asm文件),但是考虑到代码的兼容性,在代码中使用的是VS自带的内建函数,下方放出VS的内建函数查询表
VS x64 内建函数大全
VMX指令函数介绍
在汇编中执行的指令应该是
mov rax,1
cpuid
执行结束之后查询ECX第五位是否被置为1,如果被置为1,表示当前的CPU支持虚拟化技术
代码
EXTERN_C BOOLEAN Check_CPUID() {int Ecx[4];__cpuid(Ecx,1);return (Ecx[2] >> 5) & 1; //Ecx 第六位是否为1
}
2.读取MSR字段检查BIOS是否打开VT技术
MSR(Model Specific Register)是一种特殊类型的寄存器,它包含了处理器的模型特定信息和配置参数。MSR寄存器是处理器架构的一部分,由处理器制造商定义和实现。
每个处理器都可能具有不同的MSR寄存器集合,这些寄存器对应于特定的功能和配置选项。MSR寄存器通常用于控制处理器的某些特性、性能调整、电源管理和虚拟化支持等。
与通用寄存器(如通用目的寄存器)不同,MSR寄存器不可直接访问,而是通过特殊的指令(如RDMSR和WRMSR)进行读取和写入。这些指令用于将MSR的地址加载到指定寄存器(如ECX)中,然后执行相应的操作。
通过读取MSR_IA32_FEATURE_CONTROL
字段,对得到的值进行检测,检测第0位是否为0,如果为0,VMX进入保护异常,无法直接执行指令进入VMX Opreation,需要在bios中设置打开VT技术
1.RDMSR/WRMSR指令
该2条指令用于操作MSR寄存器,对MSR寄存器进行读写
RDMSR
mov eax,msr_index
rdmsr
该指令用于读取MSR的值,将MSR索引号放入eax寄存器,执行指令之后,会将MSR的高32位值存储在EDX寄存器中,低32位值存储在EAX寄存器中
WRMSR
mov eax,values
mov ecx,msr_index
wrmsr
该指令负责对MSR寄存器进行写入操作,将需要写入的数据放入eax,将msr寄存器的索引号放入ecx,执行指令即可写入
C语言代码
考虑到需要验证的结果是二进制数字,我们知道奇数的第0位一遍位1,偶数的第0位一遍位0,因此只需要判断奇偶性即可
BOOLEAN Check_CPUID() {int Ecx[4];__cpuid(Ecx,1);return (Ecx[2] >> 5) & 1; //Ecx 第六位是否为1
}
3.读取CR0与CR4检查是否已经成功进入VMX
以上工作做好之后,代表从硬件层面,已经支持进入VMX了,但是还需要打开CR4寄存器的字段锁,允许运行VT,且该锁在进入VMX之后无法更改,否则直接蓝屏,直到关闭VMX。
此处先介绍一下六个CR(Control Register)寄存器的作用,但其实只有五个,CR1寄存器在真实情况中不采用
- CR0:控制与保护模式、实模式、分页以及其他系统操作相关的设置。
- CR1:保留不采用
- CR2:存储引发页错误异常的线性地址
- CR3:存储页表的物理地址,用于地址转换和分页机制
- CR4:控制处理器的特定功能和扩展,如分页扩展、物理地址扩展等
- CR8:用于控制中断和异常处理的优先级(仅在64位操作系统下使用)
如果对CR寄存器感兴趣的师傅可以移步到这位大佬的这篇文章CR寄存器的位介绍
事实上,CR寄存器的每个位都有自己的名称,而控制是否成功进入VMX的位则是CR4寄存器的正是CR4的VMXE位,在运行VT驱动代码之前,可以先检测是否已经进入了VT,如果进入了VT,则需要避免一些不必要的操作,否则导致主机蓝屏
3.VMXON 进入VMX
在完成了2中所说的对CPU进行支持检查之后,就可以正式开始写进入VMX的代码了
1.VMXON与VMXOFF指令
进入VMX Opreation模式的方式是执行VMXON指令,退出在的指令则是VMXOFF
VMXON
在执行该指令之前需要初始化申请一段内存,该段内存大小为自然对齐,为1KB大小,被称之为"VMX_Region"
mov ecx,VMX_Region_Physical
vmxon ecx
该指令执行的要求是,需要将VMX_Region对应的物理内存地址放入寄存器中,再执行指令
PVOID VMX_Region = ExAllocatePoolWithTag(NonePagePool,0x1000,'VMX');
ULONG_PTR VMX_Region_Phy = MmGetPhysicalAddress(VMX_Region).QuadPart;
//需要设置VMX Region
__vmx_on(&VMX_Region_Phy);
VMXOFF
VMXON指令直接在退出VMX的时候执行即可
以上2条指令在VS编译器中,使用__vmx_off() 、__vmx_on()即可
2.设置VMX_Region
该段内存需要分配为NonePagePool类型的内存
该段内存的前四个字节需要写入VMCS_ID(MSR index 0x480)
通过__readmsr()读取并将其写入至该内存
* (ULONG*)VMX_Region = __readmsr(0x480);
检查错误位置
在执行__vmx_on()之后,需要对Eflags寄存器的相关位进行检测,检测eflags寄存器CF字段是否被置为1,如果被置为1,则进入VMX失败
*(ULONG_PTR*)(&eflags) = __readeflags();
if (eflags.fields.cf != 0) {DbgPrint("[CPU:%d]VMXON ON Failed", index);
}
4.设置VMCS字段
在阅读本章之前,请先阅读第五章进入虚拟化
在成功进入VMX环境之后,还需要一段VMCS(Virtual Machine Control Structure)内存,称之为"VMCS_Region",该内存主要的作用是存储和管理控制虚拟机的执行,主要用于VMCS的相关信息
1.vmwrite与vmread指令
vmwrite指令
mov ecx,VMCS_Fields
mov eax,VMCS_Data
vmwrite ecx,eax
该指令将需要写入的VMCS_Fields放入ecx,将需要写入进字段的数据放入eax,执行指令,成功将需要写入的数据放入VMCS_Fields
vmread指令
mov ecx,VMCS_Fields
vmread eax,ecx
将需要读取的VMCS_Fields放入ecx,执行指令,将读取到的数据放入ecx中
VMCS中主要需要写入的有 Guest_State
Host_State
VM Execute State
1.VM Execute State设置
- Pin Base
设置虚拟机的IA32_VMX_PINBASED_CTLS,这个寄存器控制了大多数基于引脚的虚拟机执行控制的允许设置,寄存器的位31:0指示这些控制的允许的0设置。如果MSR中的位X被清除为0,VM entry允许控制X(基于引脚的虚拟机执行控制的第X位)为0;如果MSR中的位X设置为1,如果控制X为0,VM entry将失败。
- CPU Base
用于设置 IA32_VMX_PROCBASED_CTLS,这个寄存器控制大多数基于主处理器的虚拟机执行控件的允许设置,读者如果想具体查看每个位的控制,详见Intel白皮书卷三24章第6节第2点
- VM Exit Controls
设置IA32_VMX_EXIT_CTLS 寄存器,控制大部分VM Exit 允许的控制,详见卷三24节第七节第一点
- VM Entry Controls
用于设置 IA32_VMX_ENTRY_CTLS,记录大部分VM Entry的允许设置,详见卷三24节第8节第1点
- Secondar Processor-Based
用于设置 IA32_VMX_PROCBASED_CTLS2,主要用于设置次级处理器,配置APIC EPT等,详见卷三24节第6节第2点
2.Guest_State 设置
- 寄存器部分
需要设置的有段寄存器,段选择子,段限制,AR,段基址,分别为(ES、CS、DS、FS、GS、FS、TR、GDTR、IDTR、LDTR、RIP,RSP)等等,文本会给一个文档具体的设置参数 - 非寄存器
VMCS Link pointer
3.Host_State 设置
详情请看文末的文档
对与以上提到的字段对应的索引号,通过查找Intel白皮书卷三附录B中查到
5.进入虚拟化
在设置完以上VMCS内容之前,执行vmclear vmptrld这两条指令大概流程以代码表示为
__vmx_vmclear(&VMCS_Phy);
__vmx_vmptrld(&VMCS_Phy);
SetupVmcs(); //设置VMCS
__vmx_vmlaunch(); //进入虚拟化
DbgPrint(”Vmlaunch Failed“); //如果成功执行vmlaunch,不会执行dbgprint函数
在执行__vmx_vmlaunch之后,如果程序没有发生意外,会直接进入GUEST模式,RIP/RSP变化为GUEST_RIP/GUEST_RSP地址,在发生VMX-Exit事件后,产生异常,RIP/RSP变为HOST_RIP/HOST_RSP指向的位置
如果成功执行了DbgPrint函数,请检查VM_INSTRUCTION_ERROR的错误号可以在Intel白皮书的卷三第30章第4节查到
6.处理VM Exit
在成功进入GUEST之后,RIP跳到GUEST_RIP位置,继续执行代码
期间会产生大量VMX-Exit事件,在产生VM-Exit之后,虚拟机跳到HOST_RIP位置,因此Host_RIP处需要设置一个回调函数称之为VMEXithandler
在VMExithandler函数中设置通过检测VM_EXIT_REASON的错误号,来判断产生错误的代码,以及读取GUEST_RIP以确定代码执行错误的位置,联系代码上下文对错误进行排查检测
void ExitHandler(){DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);DWORD64 GUEST_RIP = __readmsr(GUEST_RIP);__DebugBreak();
}
通过断点断下程序,获取ExitReason根据错误号进行排查(ExitReason错误号说明详见卷三附录B)
完整的流程则为
将hostrip的函数用asm文件写出将寄存器信息保存进入堆栈,将首地址指针通过call指令传入Exithandler
EXTERN_C ExitHandler:PROC //从其他文件导入函数PUSHAQ MACROpush raxpush rcxpush rdxpush rbxpush -1 push rbppush rsipush rdipush r8push r9push r10push r11push r12push r13push r14push r15
ENDMPOPAQ MACROpop r15pop r14pop r13pop r12pop r11pop r10pop r9pop r8pop rdipop rsipop rbpadd rsp, 8 pop rbxpop rdxpop rcxpop rax
ENDMExithandlerEntry PROCpushaqmov rcx,rspsub rsp,50hcall ExitHandler····//后续处理rax通过rax控制接下来的流程popaqresume //返回GUEST
ExithandlerEntry ENDP
通过switch case程序命中Exitreason 再设置函数在host模式中执行guest模式中无法执行的指令,
再通过读取VM_EXIT_INSTRUCTION_LEN,获取GUEST_RIP指向的当前指令长度通过GUEST_RIP+VM_EXIT_INSTRUCTION_LEN 重新设置GUESTRIP
void CpuidError(//接受GUEST寄存器信息){//在host模式中处理GUEST无法执行或产生异常的指令
}
void NextCode(){DWORD64 GUESTrip = __readmsr(GUEST_RIP);DWORD64 VM_EXIT_INSTRUCTION = __readmsr(VM_EXIT_INSTRUCTION_LEN);DWORD64 NextCode = GUESTrip+VM_EXIT_INSTRUCTION;__vmx_vmwrite(GUEST_RIP,NextCode);
}EXTERN_C BOOLEAN ExitHandler(//设置一个结构体获取GUEST寄存器信息){DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);DWORD64 GUESTrip = __readmsr(GUEST_RIP);switch(ExitReason):case CPUIDError: //命中函数{CpuidError();NextCode();break;}default:DbgPrint("Exit Reason %p\n",ExitReason); // 输出未处理的Exit事件__DebugBreak()// int 断点 break;return TURE; // 通过返回布尔值以确定rax是否为0
}
通过以上流程设置VMXExitHandler,处理vmexit事件
7.退出虚拟化
考虑到VT代码是以驱动加载至Windows当中的,在需要关闭VT时需要执行卸载驱动的函数,因此必须保证程序正常执行到DriverLoad函数的Return部分,因此,在进入Guest之前需要保存堆栈信息以及寄存器信息以便在成功进入GUEST之后恢复堆栈以及寄存器信息,使程序正常执行下去。以保证正常执行卸载驱动的函数
GUEST允许执行VMCALL指令,直接退出GUEST模式,在UnloadVT函数中执行该代码,通过Exithandler命中vmcall错误,vmcall可以提供参数执行,也可以不提供参数执行,通过回调函数检测rax是否为设置的特殊参数,如果是直接执行vmxon指令关闭vt
EXTERN_C void __fastcall AsmVmcall(ULONG_PTR num, ULONG_PTR param);void UnloadVt(){asmcall(vmxexit,0)
}
在asm中对ExitHandler函数的返回值进行检测
ExithandlerEntry PROCpushaqmov rcx,rspsub rsp,50hcall ExitHandler····//后续处理rax通过rax控制接下来的流程test al,al //检测返回值是否为0jz ExitVT:popaq //将修改后的寄存器回弹resume //返回GUESTjmp Error
ExitVT:popaqvmxonjz Errorjc Errorpush raxpopfq // 恢复堆栈mov rsp, rdx push rcxret
Error:int 3
ExithandlerEntry ENDP
在ExitHandler中设置
VT CloseVT(){//恢复一些段属性 限制 基址等
}
BOOLEAN vmcallhandler(GUEST寄存器信息){//如果rax为设定的预定值CloseVt();return FALSE;//如果不是 正常处理错误return TRUE;
}
EXTERN_C BOOLEAN ExitHandler(//设置一个结构体获取GUEST寄存器信息){DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);DWORD64 GUESTrip = __readmsr(GUEST_RIP);ret = TRUE;switch(ExitReason):case vmcall: //命中函数{ret = vmcallhandler();break;}default:DbgPrint("Exit Reason %p\n",ExitReason); // 输出未处理的Exit事件__DebugBreak()// int 断点 break;return ret; // 通过返回布尔值以确定rax是否为0
}
总结
代码已经上传到了Github仓库
欢迎各位大佬批评,觉得不错的师傅可以给个star
https://github.com/yifaang/VTDemo
在文章中的代码可能会有一定的问题,请以github项目源码参考
文章中需要的文档在这里!!!
Intel白皮书全卷&VMCS设置参考文档::
链接:https://pan.baidu.com/s/1cmTCIKwaT_eGlnmpO178ZQ
提取码:yftx