-
-
[翻译]从头开始(分析)管理程序——第一部分:基本概念&配置测试环境
-
发表于: 2018-8-31 13:59 3907
-
从头开始(分析)管理程序——第一部分:基本概念&配置测试环境
大家好!
欢迎来到“从头开始(分析)管理程序”系列多部分中的第一部分。顾名思义,本课程包含了创建一个基本的基于硬件虚拟化的虚拟机的技术细节。如果你跟随本课程,你将能够创建你自己的虚拟环境,并理解VMWare,VirtualBox,KVM和其他虚拟化软件是如何使用处理器的能力来创建一个虚拟环境的。
简介
Intel和AMD在他们如今的CPU中都支持虚拟化。Intel引进(VT-x 技术),该技术早先代号为“Vanderpool”在2005年11月13号,奔腾4系列中。VT-x能力的CPU标志为“vmx”,代表虚拟机扩展(V irtual Machine eXtension)。
AMD,在另一方面,开发出它的第一代代号为“Pacifica”的虚拟化扩展,并最初发布它们称为AMD Secure Virutal Machine(SVM,安全虚拟机),但是之后销售他们使用AMD 虚拟化商标,缩写为AMD-V。
有两种类型的管理程序。类型 1 管理程序称为“裸金属管理程序(bare metal hypervisor)”或“本地(native)”因为它直接运行在裸金属物理服务器上,一个类型 1 管理程序可以直接访问硬件。使用类型 1 管理程序,没有可作为虚拟机管理程序加载的操作系统。
与类型 1 管理程序相反,类型 2 管理程序在一个操作系统中加载,就像其他应用程序一样。因为类型2管理程序必须在操作系统中运行并受操作系统管理,类型 2 管理程序(和它的虚拟机)会比类型 1 管理程序运行低效(更慢)。
尽管大多数关于虚拟化的概念是相同的,但你需要(区分)VT-x和AMD-V中的不同的细节。这些教程的其余部分主要关注于 VT-x 因为Intel的CPU更受欢迎并使用更广泛。在我看来,AMD在它的手册中描述虚拟化更清晰但Intel有时会让读者感到困惑尤其是在虚拟化文档中。
管理程序和平台
那些概念是平台独立的,我指的是你可以容易的运行同样的代码程序在Linux或Windows,从CPU中获得相同的期待的行为,但是我更喜欢使用Windows,因为它更容易调试(至少对于我而言),但我尝试在需要时给出一些Linux系统上的例子。就个人而言,Linux内核管理错误如 #GP和其他异常并尝试避免内核错误(kernel panic)并保持系统运行,所以它更适合用来测试如管理程序或任何CPU相关的东西。换句话说,无论何时你没有管理你的异常,Windows从没尝试管理任何非预期异常并因此导致蓝屏,因此,你可能会得到许多BSOD。顺便一提,你最好在两个平台上都测试(其它平台也是一样)。
最后,我可能(无疑)会犯错,比如错误的实现,或误报,或忘记提及一些重要的描述,所以,我应该提前说声抱歉,如果我犯了任何错误,我会很高兴于每一条告诉我在技术信息或误报中的我的错误的评论。
这就足够了,可以开始了!
你所需要的工具
你应该有安装了WDK的Visual Studio。你可以从这里获取Windows驱动开发工具集(WDK)。
调试Windows和任何内核内核模式事件的最佳方法是使用Windbg,它可从Windows SDK(这里)中获得。(如果你以默认的安装选项安装了WDK,你可能一起安装了WDK和SDK,所以你可以跳过此步)。
你应该能够使用Windbg调试你的OS(在本例中是Windows),更多的信息(这里)
Hex-rays IDA Pro 是这个教程中的重要部分。
OSR 驱动加载器可以从这里下载,我们使用该工具来加载我们的驱动到Windows机器中。
SysInternals DebugView 用来打印DbgPrint()结果。
创建测试环境
本教程中几乎全部的代码必须运行在内核层,你必须设置Linux内核模块或Windows驱动开发工具集(WDK)模块。由于配置VMM包含大量的汇编代码,你应该知道如何在你的内核工程中运行内联汇编。在Linux中,你不应该做任何特殊的事情,但是在Windows的情况下,WDK不再支持x64环境中的内联汇编,所以,如果你之前没有解决过这个问题,那么你可能很难创建一个示例x64内联工程,但是不用担心,我会一步一步解释,所以我强烈建议在继续这个部分剩下的之前,看一下这个帖子
现在是时候创建一个驱动程序了!
如果你想从Windows 驱动开发工具集(WDK)开始,这里有一篇好的文章。
这个驱动是这样:
#include <ntddk.h> #include <wdf.h> #include <wdm.h> extern void inline AssemblyFunc1(void); extern void inline AssemblyFunc2(void); VOID DrvUnload(PDRIVER_OBJECT DriverObject); NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath); #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, Example_Unload) NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { NTSTATUS NtStatus = STATUS_SUCCESS; UINT64 uiIndex = 0; PDEVICE_OBJECT pDeviceObject = NULL; UNICODE_STRING usDriverName, usDosDeviceName; DbgPrint("DriverEntry Called."); RtlInitUnicodeString(&usDriverName, L"\Device\MyHypervisor"); RtlInitUnicodeString(&usDosDeviceName, L"\DosDevices\MyHypervisor"); NtStatus = IoCreateDevice(pDriverObject, 0, &usDriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject); if (NtStatus == STATUS_SUCCESS) { pDriverObject->DriverUnload = DrvUnload; pDeviceObject->Flags |= IO_TYPE_DEVICE; pDeviceObject->Flags &= (~DO_DEVICE_INITIALIZING); IoCreateSymbolicLink(&usDosDeviceName, &usDriverName); } return NtStatus; } VOID DrvUnload(PDRIVER_OBJECT DriverObject) { UNICODE_STRING usDosDeviceName; DbgPrint("DrvUnload Called rn"); RtlInitUnicodeString(&usDosDeviceName, L"\DosDevices\MyHypervisor"); IoDeleteSymbolicLink(&usDosDeviceName); IoDeleteDevice(DriverObject->DeviceObject); }
AssemblyFunc1 和 AssemblyFunc2 是两个用x64内联汇编代码定义的外部函数。
我们的驱动需要注册一个设备以至于我们可以从用户模式代码(User-Mode Code)和我们的虚拟环境通讯,另一方面,我定义了 DrvUnload,它使用PnP Windows 驱动特性,你可以轻松的卸载你的驱动并移除设备后重新添加和创建新设备。
下面的代码负责创建一个新的设备:
RtlInitUnicodeString(&usDriverName, L"\Device\MyHypervisor"); RtlInitUnicodeString(&usDosDeviceName, L"\DosDevices\MyHypervisor"); NtStatus = IoCreateDevice(pDriverObject, 0, &usDriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject); if (NtStatus == STATUS_SUCCESS) { pDriverObject->DriverUnload = DrvUnload; pDeviceObject->Flags |= IO_TYPE_DEVICE; pDeviceObject->Flags &= (~DO_DEVICE_INITIALIZING); IoCreateSymbolicLink(&usDosDeviceName, &usDriverName); }
如果你使用Windows,你应该禁止驱动签名强制(Driver Signature Enforcement)来加载你的驱动,那是因为微软阻止任何没有验证的代码运行在Windows内核(Ring 0)。
要做到这点,按住shift键并重启你的电脑。你应该看到一个新窗口,随后
- 点击 高级选项(Advaned options)
- 在新的窗口中点击 开始设置(Startup Settings)
- 点击 重启(Restart)
在启动设置屏幕按 7 或 F7 来禁止驱动签名强制
我记得的最后的事情是通过注册表启用Windows调试消息,这样你可以通过 SysInternals DebugView 获得 DbgPrint() 结果。
只需执行以下步骤:
在注册表编辑器中,增加键值:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter
在这个下面,添加名为IHVDRIVER,值为0xFFF的DWORD键值。
重启机器即可。
开始前的一些想法
有一些会在剩下的系列中频繁使用的关键字,你应该知道他们(大多数定义源自 Intel软件开发手册(Intel software developer's manual),3C卷)。
虚拟机监视器(Virtual Machine Monitor):VMM扮演主机的角色,可完全控制处理器和其它的平台硬件。VMM可以保留对处理器资源,物理内存,终端管理和I/O的选择性控制。
客户软件(Guest Software):每个虚拟机(VM)是一个客户软件环境。
VMX根操作(VMX Root Operation)和VMX非根操作(VMX Non-root Operation):VMM会运行在VMX根操作(模式)中,客户软件会运行在VMX非根操作(模式)中。
VMX转换(VMX transitions):在VMX 根操作模式和VMX非根模式间的转换。
VM进入(VM entries):转换到VMX非根操作模式。
扩展页表(Extended Page Table,EPT):一种现代的机制,使用一个第二层来转换客户内存地址到宿主机内存地址。
VM退出(VM exits):从VMX非根操作模式到VMX根操作模式的转换。
虚拟机控制结构(Virtual machine control structure,VMCS):是内存中的数据结构,确切存在于每个VM中,被VMM所管理。随着每次不同VM间的执行上下文的改变,VMCS被还原到当前VM,定义了虚拟机处理器的状态并且VMM使用VMCS控制客户软件。
VMCS包含6个逻辑组:
- 客户状态区域(Guest-state area):在VM退出中,处理器状态被保存到客户状态区域并在VM进入中被加载。
- 宿主机状态区域(Host-state area):在VM退出中,处理器状态被从宿主机状态区域加载。
- VM执行控制域(VM-execution control fields):在VMX非根操作模式,域控制处理器操作。
- VM退出控制域(VM-exit control fields):控制VM退出的域。
- VM进入控制域(VM-entry control fields):控制VM进入的域。
- VM退出信息域(VM-exit information fields):用来接收VM退出中的信息的只读域,描述了VM退出的起因和性质。
我发现了一个说明VMCS的很好的作品,PDF版可从这里获得
不要担心那些域,我会在之后的部分清晰的解释它们中的大多数,只需要记住VMCS结构体在不同版本的处理器间有所不同。
VMX指令
VMX引入了以下新指令:
Intel/AMD助记符 | 英文描述 | 中文描述 |
---|---|---|
INVEPT | Invalidate Translations Derived from EPT | 使从EPT派生的翻译无效 |
INVVPID | Invalidate Translations Based on VPID | 使基于VPID的翻译无效 |
VMCALL | Call to VM Monitor | 调用到VM 监视器中 |
VMCLEAR | Clear Virtual-Machine Control Structure | 清空虚拟机控制结构 |
VMFUNC | Invoke VM function | 调用VM功能 |
VMLAUNCH | Launch Virtual Machine | 加载虚拟机 |
VMRESUME | Resume Virtual Machine | 还原虚拟机 |
VMPTRLD | Load Pointer to Virtual-Machine Control Structure | 加载指向虚拟机控制结构的指针 |
VMPTRST | Store Pointer to Virtual-Machine Control Structure | 存储指向虚拟机控制结构的指针 |
VMREAD | Read Field from Virtual-Machine Control Structure | 读取来自虚拟机控制结构的域 |
VMWRITE | Write Field to Virtual-Machine Control Structure | 写域到虚拟机控制结构 |
VMXOFF | Leave VMX Operation | 离开VMX操作 |
VMXON | Enter VMX Operation | 进入VMX操作 |
VMM软件生命周期
- 下面总结了VMM的生命周期和它的客户软件以及它们之间的交互:
- 软件通过执行VMXON指令进入VMX操作。
- 使用VM进入,VMM可以将客户在VM间转换(每次一个)。VMM使用指令VMLAUNCH和VMRESUME实现VM进入;使用VM退出重新获得控制权。
- VM退出将控制权交给由VMM制定的入口点。VMM可以采取操作适应VM退出的原因并能够使用VM进入回到VM。
- 最终,VMM可以决定关闭它自身并离开VMX操作。可以通过执行VMXOFF指令这样做。
这些概念对于现在已经足够了!
在该部分,我解释了你应该注意的一般关键词,我们创建了一个简单的实验为我们将来的测试。在下一部分,我会解释如何使用在上面我们创建的工具在你的机器上启用VMX,随后,我们在剩下的虚拟化中概观,所以,务必查看博客的下一部分。
参考
[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3
[2] Hardware-assisted Virtualization
[3] Writing Windows Kernel Driver
[4] What Is a Type 1 Hypervisor?
[6] Windows 10: Disable Signed Driver Enforcement
[7] Instruction Set Mapping » VMX Instructions
原文地址:https://rayanfam.com/topics/hypervisor-from-scratch-part-1/
翻译:看雪翻译小组sudozhange
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创]初探HG110-B家庭网关 22160
- [翻译]编写Shellcode:寻找EIP/RIP 10750
- [翻译]使用Fuzzing在闭源Windows软件中寻找漏洞 19319
- [翻译]“进程重镜像”行为检测 8404
- [翻译]黑帽2019:5G安全漏洞允许中间人针对性的攻击 8831