欢迎访问Sunbet官网(www.sunbet.us),Allbet欧博官网(www.ALLbetgame.us)!

首页Sunbet_安全防护正文

从头开始相识和运用Hypervisor(第3部份)

b9e08c31ae1faa592019-12-1667Web安全

从头入手下手相识和运用Hypervisor(第1部份) 从头入手下手相识和运用Hypervisor(第2部份) 假造机械掌握数据构造(VMCS) 逻辑处理器在VMX操纵中时会运用假造机掌握数据构造(VMCS),它们治理收支VMX非根操纵(VM项和VM出口)的转换,以及VMX非根操纵中的处理器行动,该构造由新指令VMCLEAR,VMPTRLD,VMREAD和VMWRITE操纵。 上图演示了VMCS地区上的生命周期VMX操纵。 初始化VMCS地区 VMM能够运用差别的VMCS地区,因而你须要设置逻辑处理器关联性并屡次运转初始化例程。 VMCS地点的位置称为“VMCS地区”。 VMCS地区的特性以下: 1、4 KB(位11:0必需为零); 2、必需与4KB边境对齐 该指针不得设置超越处理器物理地点宽度的位,软件能够经由过程在EAX中实行80000008H实行CPUID来肯定处理器的物理地点宽度,物理地点宽度以EAX的7:0位返回。 处理器中大概同时存在多个VMCS,但个中只需一个处于运动状况,而且VMLAUNCH,VMREAD,VMRESUME和VMWRITE指令仅在当前VMCS上运转。 运用VMPTRLD能够在逻辑处理器上设置当前的VMCS,VMCLEAR指令的内存操纵数也是VMCS的地点。实行该指令后,该VMCS在逻辑处理器上既不是运动的也不是当前的。假如VMCS在逻辑处理器上是最新的,则逻辑处理器不再具有当前的VMCS。 VMPTRST担任供应当前VMCS指针,假如没有当前VMCS,它将存储值FFFFFFFFFFFFFFFFH。 VMCS的启动状况肯定该VMCS应运用哪一个VM输入指令。 VMLAUNCH指令须要启动状况为“clear”的VMCS; VMRESUME指令须要启动状况为“已启动”的VMCS,逻辑处理器在响应的VMCS地区中保护VMCS的启动状况。 假如当前VMCS的启动状况为“clear”,则胜利实行VMLAUNCH指令会将启动状况变动成“launch”。 VMCLEAR指令的内存操纵数是VMCS的地点,实行指令后,该VMCS的启动状况为“clear”。 没有其他要领能够修正VMCS的启动状况(没法运用VMWRITE对其举行修正),也没法直接发明它(没法运用VMREAD对其举行读取)。 下图就是VMCS地区的内容。 以下代码担任分派VMCS地区:

BOOLEAN Allocate_VMCS_Region(IN PVirtualMachineState vmState)
{
	// at IRQL > DISPATCH_LEVEL memory allocation routines don't work
	if (KeGetCurrentIrql() > DISPATCH_LEVEL)
		KeRaiseIrqlToDpcLevel();


	PHYSICAL_ADDRESS PhysicalMax = { 0 };
	PhysicalMax.QuadPart = MAXULONG64;


	int VMCSSize = 2 * VMCS_SIZE;
	BYTE* Buffer = MmAllocateContiguousMemory(VMCSSize + ALIGNMENT_PAGE_SIZE, PhysicalMax);  // Allocating a 4-KByte Contigous Memory region

	PHYSICAL_ADDRESS Highest = { 0 }, Lowest = { 0 };
	Highest.QuadPart = ~0;

	//BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached);

	UINT64 PhysicalBuffer = VirtualAddress_to_PhysicallAddress(Buffer);
	if (Buffer == NULL) {
		DbgPrint("[*] Error : Couldn't Allocate Buffer for VMCS Region.");
		return FALSE;// ntStatus = STATUS_INSUFFICIENT_RESOURCES;
	}
	// zero-out memory 
	RtlSecureZeroMemory(Buffer, VMCSSize + ALIGNMENT_PAGE_SIZE);
	UINT64 alignedPhysicalBuffer = (BYTE*)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) &~(ALIGNMENT_PAGE_SIZE - 1));

	UINT64 alignedVirtualBuffer = (BYTE*)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) &~(ALIGNMENT_PAGE_SIZE - 1));



	DbgPrint("[*] Virtual allocated buffer for VMCS at %llx", Buffer);
	DbgPrint("[*] Virtual aligned allocated buffer for VMCS at %llx", alignedVirtualBuffer);
	DbgPrint("[*] Aligned physical buffer allocated for VMCS at %llx", alignedPhysicalBuffer);

	// get IA32_VMX_BASIC_MSR RevisionId

	IA32_VMX_BASIC_MSR basic = { 0 };


	basic.All = __readmsr(MSR_IA32_VMX_BASIC);

	DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier);


	//Changing Revision Identifier
	*(UINT64 *)alignedVirtualBuffer = basic.Fields.RevisionIdentifier;


	int status = __vmx_vmptrld(&alignedPhysicalBuffer);
	if (status)
	{
		DbgPrint("[*] VMCS failed with status %d\n", status);
		return FALSE;
	}

	vmState->VMCS_REGION = alignedPhysicalBuffer;

	return TRUE;
}
除了__vmx_vmptrld而不是__vmx_on以外,以上代码与VMXON地区完整雷同,__ vmx_vmptrld是VMPTRLD指令的固有函数。 在VMCS中,我们还应当从MSR_IA32_VMX_BASIC中找到订正标识符,并在实行VMPTRLD之前写入VMCS 地区。 MSR_IA32_VMX_BASIC定义以下。
typedef union _IA32_VMX_BASIC_MSR
{
	ULONG64 All;
	struct
	{
		ULONG32 RevisionIdentifier : 31;   // [0-30]
		ULONG32 Reserved1 : 1;             // [31]
		ULONG32 RegionSize : 12;           // [32-43]
		ULONG32 RegionClear : 1;           // [44]
		ULONG32 Reserved2 : 3;             // [45-47]
		ULONG32 SupportedIA64 : 1;         // [48]
		ULONG32 SupportedDualMoniter : 1;  // [49]
		ULONG32 MemoryType : 4;            // [50-53]
		ULONG32 VmExitReport : 1;          // [54]
		ULONG32 VmxCapabilityHint : 1;     // [55]
		ULONG32 Reserved3 : 8;             // [56-63]
	} Fields;
} IA32_VMX_BASIC_MSR, *PIA32_VMX_BASIC_MSR;
VMXOFF 设置完上述地区以后,如今当用户形式应用顺序不再保护驱动顺序的句柄时,该斟酌一下DrvClose了。此时,我们应当停止VMX并开释之前分派的每一个内存。 以下函数担任实行VMXOFF,然后挪用MmFreeContiguousMemory以开释分派的内存:
void Terminate_VMX(void) {

	DbgPrint("\n[*] Terminating VMX...\n");

	KAFFINITY kAffinityMask;
	for (size_t i = 0; i < ProcessorCounts; i++)
	{
		kAffinityMask = ipow(2, i);
		KeSetSystemAffinityThread(kAffinityMask);
		DbgPrint("\t\tCurrent thread is executing in %d th logical processor.", i);


		__vmx_off();
		MmFreeContiguousMemory(PhysicalAddress_to_VirtualAddress(vmState[i].VMXON_REGION));
		MmFreeContiguousMemory(PhysicalAddress_to_VirtualAddress(vmState[i].VMCS_REGION));

	}

	DbgPrint("[*] VMX Operation turned off successfully. \n");

}
请记着将VMXON和VMCS地区转换为假造地点,因为MmFreeContiguousMemory接收VA,不然会致使BSOD。 测试VMM 如今,让我们为代码建立一个测试案例,起首是一个经由过程一切逻辑处理器初始化VMXON和VMCS地区的函数。
PVirtualMachineState vmState;
int ProcessorCounts;

PVirtualMachineState Initiate_VMX(void) {

	if (!Is_VMX_Supported())
	{
		DbgPrint("[*] VMX is not supported in this machine !");
		return NULL;
	}

	ProcessorCounts = KeQueryActiveProcessorCount(0);
	vmState = ExAllocatePoolWithTag(NonPagedPool, sizeof(VirtualMachineState)* ProcessorCounts, POOLTAG);


	DbgPrint("\n=====================================================\n");

	KAFFINITY kAffinityMask;
	for (size_t i = 0; i < ProcessorCounts; i++)
	{
		kAffinityMask = ipow(2, i);
		KeSetSystemAffinityThread(kAffinityMask);
		// do st here !
		DbgPrint("\t\tCurrent thread is executing in %d th logical processor.", i);

		Enable_VMX_Operation();	// Enabling VMX Operation
		DbgPrint("[*] VMX Operation Enabled Successfully !");

		Allocate_VMXON_Region(&vmState[i]);
		Allocate_VMCS_Region(&vmState[i]);


		DbgPrint("[*] VMCS Region is allocated at  ===============> %llx", vmState[i].VMCS_REGION);
		DbgPrint("[*] VMXON Region is allocated at ===============> %llx", vmState[i].VMXON_REGION);

		DbgPrint("\n=====================================================\n");
	}
}
应当从IRP MJ CREATE挪用上述函数,因而让我们将DrvCreate修正成:
NTSTATUS DrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{

	DbgPrint("[*] DrvCreate Called !");

	if (Initiate_VMX()) {
		DbgPrint("[*] VMX Initiated Successfully.");
	}

	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return STATUS_SUCCESS;
}
并将DrvClose修正成:
NTSTATUS DrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
	DbgPrint("[*] DrvClose Called !");

	// executing VMXOFF on every logical processor
	Terminate_VMX();

	Irp->IoStatus.Status = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return STATUS_SUCCESS;
}
如今,在建立句柄的状况下运转代码,能够看到我们胜利地分派了地区。 从用户形式挪用CloseHandle的状况以下: 本部份所说的源代码可在GitHub上找到。 在这一部份中,我们相识了差别范例的IOCTL调理,然后在Windows中看到了用于治理hypervisorVMM的差别函数,并初始化了VMXON地区和VMCS地区,然后停止了它们。 接下来,我们将重点引见VMCS和可在VMCS地区中实行的种种操纵,以掌握客户软件。 起首,你还应当对分页机制以及页表的事情道理有基础的相识,这里有一篇关于分页表的好文章,你能够相识一下。该教程的完整源代码可在GitHub上找到。 二级地点转换(SLAT)或嵌套分页是分页机制中的扩大层,用于将基于硬件的假造化假造地点映照到物理内存中。 自第三代Opteron处理器和微架构代号Barcelona推出以来,AMD经由过程称为嵌套页表(NPT)的疾速假造索引(RVI)手艺实行了SLAT。自从引入微体系构造代码称号Nehalem及其称为扩大页表(EPT)以来,Intel还在Intel®VT-x手艺中完成了SLAT,并在Core i9,Core i7,Core i5和Core i3处理器中运用。 有两种要领,第一种是“影子页表”,第二种是“扩大页表”。 软件辅佐分页(影子页表) hypervisor运用影子页表来跟踪物理内存的状况,在该状况中,客户以为其能够接见物理内存,然则在实际天下中,硬件会阻挠其接见硬件内存,不然它将掌握主机和它不是它想要的。 在这类状况下,VMM保护影子页表,这些影子页表将客户假造页面直接映照到机械页面,而且任何客户对V-> P表的修正都同步到VMM V-> M影子页表。 趁便说一句,本文不发起运用影子页表,因为它总是会致使VMM圈套(这会致使大批VM退出),而且因为每台交流机上的TLB革新而致使机能丧失,另一个正告是因为客户页表的影子复制而致使的内存开支。 硬件辅佐分页(扩大页表)

为了下降影子页表的庞杂性并防止过量的假造机退出并削减TLB革新次数,EPT是一种硬件辅佐分页战略,旨在进步机能。 依据VMware评价报告: “ EPT在MMU密集型基准测试中可将机能提拔高达48%,在MMU密集型微基准测试中可将机能提拔高达600%”。 EPT还完成了另一页表条理构造,以将客户假造地点映照到在主内存中有用的客户物理地点。 在EPT中: 1.客户操纵体系保护一个页表,该页表用于生成客户物理地点。 2.另一个页表由VMM保护,用于将客户物理地点映照到主机物理地点。 因而,关于每一个内存接见操纵,EPT MMU都邑直接从客户页表中猎取客户物理地点,然后经由过程VMM映照表自动猎取主机物理地点。 扩大页表与影子页表 EPT: 1.方便任何请求的地点; 1.1适用于在实行时有大批页表丧失的顺序; 1.2退出假造机的时机更少(上下文切换更少); 2.两层EPT: 2.1意味着每次接见都须要遍历两张表;
如何使用iCloud钥匙串管理苹果设备上的密码 iCloud钥匙串可以作为iPhone和iPad的基本密码管理器。通过icloud钥匙串,您可以保存网站和应用程序的密码,然后在需要重新登录这些网站和应用程序时找到密码。您的凭证通过iCloud以加密格式存储和同步,因此您可以在iPhone、iPad和Mac上使用相同的凭证。您还可以编辑和管理您保存的密码。您可以通过“钥匙串”更改密码并删除不再需要的帐户。下面我们就来了解具体操作: 要在iPhone或iPad上启用钥匙串,请首先找到“设置”。点击屏幕上方的姓名,然后选择iCloud。在iCloud界面上,点击“钥匙串”的设置,然后打开其开关(图A)。 图A 接下来,回到“设置”界面,然后点击“密码和帐户”选项。确保“自动填充密码”条目已打开(图B)。 图B 现在,打开一个受密码保护的应用程序。使用用户名和密码登录后,将弹出提示,询问您是否要
3.更轻易开发 3.1很多特别的寄存器; 3.2硬件协助客户操纵体系关照VMM SPT: 1.仅在SPT进入未掷中时运转: 1.1适用于仅常常接见某些地点的顺序; 1.2VMM大概会阻拦每次接见(很多圈套) 2一个参考 2.1点击页面时疾速便利; 3.难以生长 3.1两层构造; 3.2庞杂的反向图; 3.3权限模仿; 检测对EPT,NPT的支撑 假如要在不运用顺序集(CPUID)的状况下检察体系是不是支撑Intel处理器上的EPT或AMD处理器上的NPT,则能够从Sysinternals下载coreinfo.exe,然后运转它。末了一即将显现你的处理器是不是支撑EPT或NPT。 EPT转换 EPT定义了一个地点转换层,可加强线性地点的转换。 扩大页表机制(EPT)是一项可用于支撑物理内存假造化的函数,运用EPT时,一般会被视为物理地点(并用于接见内存)的某些地点将被视为客户物理地点。经由过程遍历一组EPT分页构造来转换客户物理地点,以生成用于接见内存的物理地点。 EPT在“启用EPT” VM实行控件为1时运用,它将转换VMX非根操纵中运用的客户物理地点以及VM项用于事宜注入的客户物理地点。 EPT转换与通例分页转换完整一样,但有一些纤细的差别。在分页中,处理器将假造地点转换为物理地点,而在EPT转换中,你要将客户假造地点转换为主机物理地点。 假如你熟习分页,则第3个掌握寄存器(CR3)是PML4表的基地点(在x64处理器中或更普各处,它指向根分页目次),在EPT 客户假造机中不知道EPT转换,因而它具有CR3,然则此CR3用于将客户假造地点转换为客户物理地点。只需你找到目标客户物理地点,它都是EPT机制,将客户物理地点视为假造地点,而且EPTP是CR3。 因而,你的目标物理地点应分为4部份,前9位指向EPT PML4E(请注重PML4基址在EPTP中),后9位指向EPT PDPT项(PDPT的基址来自EPT PML4E ),后9位指向EPT PD项(PD的基地点来自EPT PDPTE),客户物理地点的后9位指向EPT PT表中的项(PT的基地点来自EPT PDE ),如今EPT PT项指向响应页面的主机物理地点。 一个简朴的假造到物理地点的转换涉及到接见4个物理地点,会发作什么呢? 答案是处理器在内部逐一转换一切表的物理地点,这就是为何客户软件中的分页和接见内存比通例地点转换要慢的缘由。下图说清楚明了从客户假造地点到主机物理地点的操纵: 假如你想斟酌x86 EPT假造化,比方,假定CR4.PAE = CR4.PSE =0。则32位线性地点的转换以下: 1.线性地点的位31:22在客户页面目次中的CR3客户物理地点中挑选一个项,客户页面目次项(PDE)的客户物理地点经由过程EPT转换,以肯定客户PDE的物理地点。 2.线性地点的位21:12在客户PDE中的客户物理地点处的客户页表中挑选一个项,客户页表项(PTE)的客户物理地点经由过程EPT转换,以肯定客户PTE的物理地点。 3.线性地点的位11:0是位于客户PTE中客户物理地点处的页面框架中的偏移量,由该偏移量肯定的客户物理地点经由过程EPT转换,以肯定原始线性地点转换为的物理地点。 请注重,PAE代表物理地点扩大,它是x86体系构造的内存治理函数,用于扩大地点空间,PSE代表页面大小扩大,它是指x86处理器的函数,该函数许可页面大于传统的4 KiB大小。 除了将客户物理地点转换为主机物理地点以外,EPT还指定接见该地点时许可软件的特权,尝试举行不许可的接见称为EPT违规,并致使VM退出。 请记着,在没有接见权限的状况下,地点永久不会经由过程EPT转换。在接见(读取或写入)内存中的该位置之前,永久不会运用你的客户物理地点。 实行扩大页表(EPT) 此时,我们应当将(VMWRITE)EPTP或扩大页表指针写入VMCS。 EPTP构造以下所述:

从头开始相识和运用Hypervisor(第3部份)  Web安全 第1张

能够运用以下构造来形貌以上表格:
// See Table 24-8. Format of Extended-Page-Table Pointer
typedef union _EPTP {
	ULONG64 All;
	struct {
		UINT64 MemoryType : 3; // bit 2:0 (0 = Uncacheable (UC) - 6 = Write - back(WB))
		UINT64 PageWalkLength : 3; // bit 5:3 (This value is 1 less than the EPT page-walk length) 
		UINT64 DirtyAndAceessEnabled : 1; // bit 6  (Setting this control to 1 enables accessed and dirty flags for EPT)
		UINT64 Reserved1 : 5; // bit 11:7 
		UINT64 PML4Address : 36;
		UINT64 Reserved2 : 16;
	}Fields;
}EPTP, *PEPTP;
一切EPT表中的每一个项均为64位长。 EPT PML4E和EPT PDPTE和EPT PD雷同,但EPT PTE略有差别。 一个EPT项是如许的: 第一个表是PML4,下表显现了EPT PML4项(PML4E)的花样。

从头开始相识和运用Hypervisor(第3部份)  Web安全 第2张

PML4E能够是以下的构造:
// See Table 28-1. 
typedef union _EPT_PML4E {
	ULONG64 All;
	struct {
		UINT64 Read : 1; // bit 0
		UINT64 Write : 1; // bit 1
		UINT64 Execute : 1; // bit 2
		UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
		UINT64 Accessed : 1; // bit 8
		UINT64 Ignored1 : 1; // bit 9
		UINT64 ExecuteForUserMode : 1; // bit 10
		UINT64 Ignored2 : 1; // bit 11
		UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
		UINT64 Reserved2 : 4; // bit 51:N
		UINT64 Ignored3 : 12; // bit 63:52
	}Fields;
}EPT_PML4E, *PEPT_PML4E;
只需我们要举行4级分页,第二个表就是EPT Page-Directory-Pointer-Table (PDTP),下图说清楚明了PDPTE的花样:

从头开始相识和运用Hypervisor(第3部份)  Web安全 第3张

PDPTE的构造以下:
// See Table 28-3
typedef union _EPT_PDPTE {
	ULONG64 All;
	struct {
		UINT64 Read : 1; // bit 0
		UINT64 Write : 1; // bit 1
		UINT64 Execute : 1; // bit 2
		UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
		UINT64 Accessed : 1; // bit 8
		UINT64 Ignored1 : 1; // bit 9
		UINT64 ExecuteForUserMode : 1; // bit 10
		UINT64 Ignored2 : 1; // bit 11
		UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
		UINT64 Reserved2 : 4; // bit 51:N
		UINT64 Ignored3 : 12; // bit 63:52
	}Fields;
}EPT_PDPTE, *PEPT_PDPTE;
关于第三个分页表,我们应当完成EPT页面目次项(PDE),以下所述:

从头开始相识和运用Hypervisor(第3部份)  Web安全 第4张

PDE的构造:
// See Table 28-5
typedef union _EPT_PDE {
	ULONG64 All;
	struct {
		UINT64 Read : 1; // bit 0
		UINT64 Write : 1; // bit 1
		UINT64 Execute : 1; // bit 2
		UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
		UINT64 Accessed : 1; // bit 8
		UINT64 Ignored1 : 1; // bit 9
		UINT64 ExecuteForUserMode : 1; // bit 10
		UINT64 Ignored2 : 1; // bit 11
		UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
		UINT64 Reserved2 : 4; // bit 51:N
		UINT64 Ignored3 : 12; // bit 63:52
	}Fields;
}EPT_PDE, *PEPT_PDE;
末了一页是EPT,以下所述。 PTE将是: 请注重,除了上述页面以外,你另有EPTMemoryType,IgnorePAT,DirtyFlag和SuppressVE。
// See Table 28-6
typedef union _EPT_PTE {
	ULONG64 All;
	struct {
		UINT64 Read : 1; // bit 0
		UINT64 Write : 1; // bit 1
		UINT64 Execute : 1; // bit 2
		UINT64 EPTMemoryType : 3; // bit 5:3 (EPT Memory type)
		UINT64 IgnorePAT : 1; // bit 6
		UINT64 Ignored1 : 1; // bit 7
		UINT64 AccessedFlag : 1; // bit 8	
		UINT64 DirtyFlag : 1; // bit 9
		UINT64 ExecuteForUserMode : 1; // bit 10
		UINT64 Ignored2 : 1; // bit 11
		UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
		UINT64 Reserved : 4; // bit 51:N
		UINT64 Ignored3 : 11; // bit 62:52
		UINT64 SuppressVE : 1; // bit 63
	}Fields;
}EPT_PTE, *PEPT_PTE;
另有其他范例的完成页面走(2或3级分页),假如你设置PDPTE的第7位(图1 GB)或7的PDE(图2 MB)而不是完成4层分页(像我们想做的主题)设置这些位但记着响应的表是差别的。Alex Ionescu的SimpleVisor就是如许完成的一个例子。 须要注重的是,险些一切上述构造都具有36位物理地点,这意味着我们的hypervisor仅支撑4级分页。这是因为每一个页表(和每一个EPT页表)都由512个项构成,这意味着你须要9位才挑选一个项,而且只需我们有4个级别表,我们就不能运用凌驾36个(4 * 9)位。在一切重要的操纵体系(比方Windows或Linux)中都未完成另一种具有更广泛地点局限的要领。我将在本主题的稍后部份扼要形貌EPT PML5E,但因为它尚不盛行,因而我们不会在hypervisor中完成它。 趁便说一下,N是处理器支撑的物理地点宽度,在EAX中80000008H的CPUID供应了在EAX位7:0中支撑的宽度。 让我们看一下其他的代码,以下代码是Initialize_EPTP函数,该函数担任分派和映照EPTP。 请注重,PAGED_CODE()宏可确保挪用线程以足够低的IRQL运转以许可分页。
UINT64 Initialize_EPTP()
{
	PAGED_CODE();
        ...
起首,分派EPTP并将其置零。
	// Allocate EPTP
	PEPTP EPTPointer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);

	if (!EPTPointer) {
		return NULL;
	}
	RtlZeroMemory(EPTPointer, PAGE_SIZE);
如今,我们的EPT PML4表须要一个空白页。
	//	Allocate EPT PML4
	PEPT_PML4E EPT_PML4 = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
	if (!EPT_PML4) {
		ExFreePoolWithTag(EPTPointer, POOLTAG);
		return NULL;
	}
	RtlZeroMemory(EPT_PML4, PAGE_SIZE);
而PDPT的另一个空白页:
//	Allocate EPT Page-Directory-Pointer-Table
	PEPT_PDPTE EPT_PDPT = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
	if (!EPT_PDPT) {
		ExFreePoolWithTag(EPT_PML4, POOLTAG);
		ExFreePoolWithTag(EPTPointer, POOLTAG);
		return NULL;
	}
	RtlZeroMemory(EPT_PDPT, PAGE_SIZE);
固然,有关页面目次表的状况也是云云。
	//	Allocate EPT Page-Directory
	PEPT_PDE EPT_PD = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);

	if (!EPT_PD) {
		ExFreePoolWithTag(EPT_PDPT, POOLTAG);
		ExFreePoolWithTag(EPT_PML4, POOLTAG);
		ExFreePoolWithTag(EPTPointer, POOLTAG);
		return NULL;
	}
	RtlZeroMemory(EPT_PD, PAGE_SIZE);
末了一张表是EPT页表的空白页。
	//	Allocate EPT Page-Table
	PEPT_PTE EPT_PT = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);

	if (!EPT_PT) {
		ExFreePoolWithTag(EPT_PD, POOLTAG);
		ExFreePoolWithTag(EPT_PDPT, POOLTAG);
		ExFreePoolWithTag(EPT_PML4, POOLTAG);
		ExFreePoolWithTag(EPTPointer, POOLTAG);
		return NULL;
	}
	RtlZeroMemory(EPT_PT, PAGE_SIZE);
如今我们已经有了一切页面,如今让我们一连分派两页(2 * 4096),因为我们须要一个页面来启动RIP,另一个页面来启动客栈(RSP)。以后,我们须要两个具有实行,读取和写入权限的EPT页表项(PTE)。物理地点应除以4096(PAGE_SIZE),因为假如我们将十六进制数除以4096(0x1000),则从右侧入手下手的12位数字(为零)将消逝,而且这12位数字用于在4096个字节之间举行挑选。 趁便说一句,我们也让客栈能够实行,这是因为在通例VM中,我们应当将RWX放到一切页面上,因为这是内部页表担任设置或消灭NX位的义务。为了特别目标,我们须要从EPT表中变动它们(比方,截取特定页面的指令)。从EPT表中举行变动将致使EPT违规,如许我们就能够阻拦这些事宜。 实际须要两个页面,然则我们须要在客户软件中构建页表,因而我们最多分派了10个页面。 本文翻译自:https://rayanfam.com/topics/hypervisor-from-scratch-part-3/ 与 https://rayanfam.com/topics/hypervisor-from-scratch-part-4/

网友评论