首页
社区
课程
招聘
[翻译]从头开始(分析)管理程序——第一部分:基本概念&配置测试环境
发表于: 2018-8-31 13:59 3907

[翻译]从头开始(分析)管理程序——第一部分:基本概念&配置测试环境

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-xAMD-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);
}

AssemblyFunc1AssemblyFunc2 是两个用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键并重启你的电脑。你应该看到一个新窗口,随后

  1. 点击 高级选项(Advaned options)
  2. 在新的窗口中点击 开始设置(Startup Settings)
  3. 点击 重启(Restart)
  4. 在启动设置屏幕按 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?

 

[5] Intel / AMD CPU Internals

 

[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期)

最后于 2018-9-4 09:46 被sudozhange编辑 ,原因: 修改名词解释
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//