从头开始了解和使用Hypervisor(第7部分) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

从头开始了解和使用Hypervisor(第7部分)

gejigeji 技术 2020-01-04 09:30:00
758315
收藏

导语:毫不夸张地说,学习完本文,你完全可以创建自己的虚拟环境,并且可以了解VMWare,VirtualBox,KVM和其他虚拟化软件如何使用处理器的函数来创建虚拟环境。

从头开始了解和使用Hypervisor(第1部分)

从头开始了解和使用Hypervisor(第2部分)

从头开始了解和使用Hypervisor(第3部分)

从头开始了解和使用Hypervisor(第4部分)

从头开始了解和使用Hypervisor(第5部分)

从头开始了解和使用Hypervisor(第6部分)

MSR位图

这里的一切都取决于你是否设置了基于控制的第28位主处理器,在支持“使用MSR位图”vm执行控件1-设置的处理器上,vm执行控件字段包括4个连续MSR位图的64位物理地址,每个位图的大小为1-KByte。不支持该控件的1-设置的处理器上不存在此字段。

在Intel SDM中,MSR位图的定义非常清楚,因此我只是从原始手册中复制了它们,在阅读它们之后,我们将开始实现并将其放入hypervisor中。

1.读取低MSR位图(位于MSR位图地址),对于每个MSR地址,该位包含一个位,范围为00000000H至00001FFFH。该位确定执行应用于该M00000000HSR的RDMSR是否导致VM退出。

2.读取高MSR的位图(位于MSR位图地址要再加上1024),对于C0000000H toC0001FFFH范围内的每个MSR地址,它包含一个位,该位决定应用于该MSR的RDMSR的执行是否会导致VM退出。

3.为低MSR写入位图(位于MSR位图地址加2048),对于每个MSR地址,该位包含一个位,范围为00000000H至00001FFFH,该位确定执行应用于该MSR的WRMSR是否导致VM退出。

4. 为高MSR写入位图(位于MSR位图地址加3072),对于C0000000H toC0001FFFH范围内的每个MSR地址,它包含一个位,这个位决定了WRMSR的执行是否会导致VM退出。

好的,让我们执行上面的语句,如果RDMSR或WRMSR中的任何一个导致VM退出,那么我们必须手动执行RDMSR或WRMSR并将结果设置到寄存器中,因此,我们具有管理RDMSR的函数,例如:

处理MSR读取

void HandleMSRRead(PGUEST_REGS GuestRegs)
{
	MSR msr = { 0 };


	// RDMSR. The RDMSR instruction causes a VM exit if any of the following are true:
	// 
	// The "use MSR bitmaps" VM-execution control is 0.
	// The value of ECX is not in the ranges 00000000H - 00001FFFH and C0000000H - C0001FFFH
	// The value of ECX is in the range 00000000H - 00001FFFH and bit n in read bitmap for low MSRs is 1,
	//   where n is the value of ECX.
	// The value of ECX is in the range C0000000H - C0001FFFH and bit n in read bitmap for high MSRs is 1,
	//   where n is the value of ECX & 00001FFFH.

	if (((GuestRegs->rcx rcx rcx);
	}
	else
	{
		msr.Content = 0;
	}

	GuestRegs->rax = msr.Low;
	GuestRegs->rdx = msr.High;
}

你会看到,它只是检查MSR的完整性,然后执行RDMSR,最后将结果放入RAX和RDX中(因为非虚拟化的RDMSR会执行相同的操作)。

处理MSR写入

还有另一个用于处理WRMSR VM-Exit的函数:

void HandleMSRWrite(PGUEST_REGS GuestRegs)
{
	MSR msr = { 0 };

	// Check for sanity of MSR 
	if ((GuestRegs->rcx rcx rax;
		msr.High = (ULONG)GuestRegs->rdx;
		MSRWrite((ULONG)GuestRegs->rcx, msr.Content);
	}
}

该函数的功能很简单,到目前为止,你可能应该已经了解到,所有挂钩的RDMSR和WRMSR都应最终调用这些函数,但你真正值得尝试的一件事是避免在CPU_BASED_VM_EXEC_CONTROL中设置CPU_BASED_ACTIVATE_MSR_BITMAP,你将看到所有MSR读取和修改的过程将导致VM退出,其原因如下:

EXIT_REASON_MSR_READ
EXIT_REASON_MSR_WRITE

此时,你必须将所有内容传递给上述函数并记录这些VM-Exit,以便可以看到Windows在hypervisor中运行时使用的MSR,但是正如我在上文中告诉你的那样,Windows执行了大量的MSR指令,因此它会使你的系统速度变慢。

好的,让我们回到MSR位图,我们需要两个函数来设置MSR位图的位,

void SetBit(PVOID Addr, UINT64 bit, BOOLEAN Set) {

	PAGED_CODE();
	UINT64 byte = bit / 8;
	UINT64 temp = bit % 8;
	UINT64 n = 7 - temp;

	BYTE* Addr2 = Addr;
	if (Set)
	{
		Addr2[byte] |= (1 << n);
	}
	else
	{
		Addr2[byte] &= ~(1 << n);

	}

}

用于检索特殊位的另一个函数:

void GetBit(PVOID Addr, UINT64 bit) {
	UINT64 byte = 0, k = 0;
	byte = bit / 8;
	k = 7 - bit % 8;
	BYTE* Addr2 = Addr;

	return Addr2[byte] & (1 << k);
}

现在该根据上面有关MSR位图的描述将所有内容收集到一个函数中了,以下函数首先检查MSR的完整性,然后更改目标逻辑内核的MSR位图。这就是为什么我们同时持有MSR位图的物理地址和虚拟地址,VMCS字段的物理地址和虚拟地址以简化操作的原因。如果它是低MSR的读入(rdmsr),则在MSRBitmap虚拟地址中设置相应的位,如果是低MSR的写入(wrmsr),则修改MSRBitmap + 2048(如Intel手册中所述)并精确对于高MSR(在0xC0000000和0xC0001FFF之间),情况也是一样,但不要忘记删减(0xC0000000),因为0xC000nnnn不是有效位:d。

BOOLEAN SetMSRBitmap(ULONG64 msr, int ProcessID, BOOLEAN ReadDetection, BOOLEAN WriteDetection) {

	if (!ReadDetection && !WriteDetection)
	{
		// Invalid Command
		return FALSE;
	}


	if (msr <= 0x00001FFF)
	{
		if (ReadDetection)
		{
			SetBit(vmState[ProcessID].MSRBitMap, msr, TRUE);
		}
		if (WriteDetection)
		{
			SetBit(vmState[ProcessID].MSRBitMap + 2048, msr, TRUE);
		}
	}
	else if ((0xC0000000 <= msr) && (msr <= 0xC0001FFF))
	{

		if (ReadDetection)
		{
			SetBit(vmState[ProcessID].MSRBitMap + 1024, msr - 0xC0000000, TRUE);
		}
		if (WriteDetection)
		{
			SetBit(vmState[ProcessID].MSRBitMap + 3072, msr - 0xC0000000, TRUE);

		}
	}
	else
	{
		return FALSE;
	}
	return TRUE;
}

还要记住一件事,只有上述MSR范围当前在Intel处理器中有效,因此,即使任何其他RDMSR和WRMSR也会导致VM退出,但是此处的完整性检查是强制性的,因为客户可能会发送无效的MSR并导致整个系统崩溃(在VMX根模式下)!!!

关闭VMX并退出Hypervisor

现在该关闭我们的hypervisor,并将处理器状态重启为运行hypervisor之前的状态。

就像我们如何进入hypervisor(VMLAUNCH)的方式一样,我们必须将C函数与Assembly例程结合起来以保存状态,然后执行VMXOFF并释放所有先前分配的池,最后重启状态。

该例程的VMXOFF部分应在VMX Root操作中执行,你不能仅在其中一个驱动程序函数中执行__vmx_vmxoff,否则它会因为Windows关闭hypervisor,并且其所有驱动程序当前都在非root用户的VMX中执行其中的VMX指令就像VM-Exit,其原因之一是。

EXIT_REASON_VMCLEAR
EXIT_REASON_VMPTRLD
EXIT_REASON_VMPTRST
EXIT_REASON_VMREAD
EXIT_REASON_VMRESUME
EXIT_REASON_VMWRITE
EXIT_REASON_VMXOFF
EXIT_REASON_VMXON
EXIT_REASON_VMLAUNCH

要关闭hypervisor,最好使用IRP主要函数,在本例中,我们使用DrvClose,因为它总是在关闭设备的句柄时得到通知,如果你还记得上面的内容,则可以从设备中创建句柄使用CreateFile(DrvCreate),现在是时候使用DrvClose关闭句柄了。

NTSTATUS DrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
	DbgPrint("[*] DrvClose Called !\n");

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


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

	return STATUS_SUCCESS;
}

上面的函数没什么特别的,只是Terminate_VMX()。

该函数类似于执行VMLAUNCH的例程,只是它执行的是VMXOFF。

void Terminate_VMX(void) {

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

	int LogicalProcessorsCount = KeQueryActiveProcessorCount(0);

	for (size_t i = 0; i < LogicalProcessorsCount; i++)
	{
		DbgPrint("\t\t + Terminating VMX on processor %d\n", i);
		RunOnProcessorForTerminateVMX(i);

		// Free the destination memory
		MmFreeContiguousMemory(PhysicalAddress_to_VirtualAddress(vmState[i].VMXON_REGION));
		MmFreeContiguousMemory(PhysicalAddress_to_VirtualAddress(vmState[i].VMCS_REGION));
		MmFreeContiguousMemory(vmState[i].VMM_Stack);
		MmFreeContiguousMemory(vmState[i].MSRBitMap);
	}

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

}

如你所见,它在所有正在运行的逻辑内核上执行RunOnProcessorForTerminateVMX,然后使用MmFreeContiguousMemory为VMXON_REGION,VMCS_REGION,VMM_Stack和MSRBitMap释放分配的缓冲区。当然,在需要时将物理设备转换为虚拟设备。

请注意,如果你虚拟化了部分内核(不是全部),则必须自己修改此函数。

正如我告诉你的那样,在RunOnProcessorForTerminateVMX中,我们必须告诉我们的VMX根操作有关关闭hypervisor的信息,这是因为我们无法在此处执行任何VMX指令。很显然,如果没有任何机制来处理这种情况,VMX根操作可以阻止我们这个操作。

你可以使用多种方法来告知VMX根操作有关VMXOFF的信息,但在本例中,我使用了CPUID。

现在,你绝对知道执行CPUID会导致VM退出,现在在我们的CPUID退出处理程序例程中,我们管理每当执行RAX = 0x41414141和RCX = 0x42424242的CPUID时,你都必须返回true,并向调用者显示hypervisor需要关闭。

	if ((state->rax == 0x41414141) && (state->rcx == 0x42424242) && Mode == DPL_SYSTEM)
	{
		return TRUE; // Indicates we have to turn off VMX
	}

还有另一种DPL检查:

	ULONG Mode = 0;
	__vmx_vmread(GUEST_CS_SELECTOR, &Mode);
	Mode = Mode & RPL_MASK;

此检查确保具有RAX = 0x41414141和RCX = 0x42424242的CPUID在系统权限级别(内核模式)下执行,因此没有用户模式应用程序能够执行此任务。

即使执行了此检查,但没有此检查也不意味着用户模式应用程序可以关闭hypervisor,这是因为我们没有将CR3更改为目标用户模式进程,也不将当前权限级别更改为用户模式,因此,如果你希望用户模式应用程序能够执行此任务,则必须考虑这些情况。

现在,我们的RunOnProcessorForTerminateVMX在所有内核上分别执行CPUID。

BOOLEAN RunOnProcessorForTerminateVMX(ULONG ProcessorNumber)
{
	KIRQL OldIrql;

	KeSetSystemAffinityThread((KAFFINITY)(1 << ProcessorNumber));

	OldIrql = KeRaiseIrqlToDpcLevel();

	// Our routine is VMXOFF
	INT32 cpu_info[4];
	__cpuidex(cpu_info, 0x41414141, 0x42424242);

	KeLowerIrql(OldIrql);

	KeRevertToUserAffinityThread();

	return TRUE;
}

在我们的EXIT_REASON_CPUID中,我们知道如果处理程序返回true,则必须将其关闭,因此你应该考虑其他事项。例如,Windows希望每当VM退出处理程序返回时都运行客户_RIP和GUEST_RSP,因此我们必须将它们保存在某些位置,并在以后使用它们还原Windows状态。

我们还必须增加GUEST_RIP,因为我们想在CPUID之后恢复状态。

	case EXIT_REASON_CPUID:
	{
		Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not
		if (Status)
		{
			// We have to save GUEST_RIP & GUEST_RSP somewhere to restore them directly

			ULONG ExitInstructionLength = 0;
			gGuestRIP = 0;
			gGuestRSP = 0;
			__vmx_vmread(GUEST_RIP, &gGuestRIP);
			__vmx_vmread(GUEST_RSP, &gGuestRSP);
			__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);



			gGuestRIP += ExitInstructionLength;


		}
		break;
	}

MainVMExitHandler又被称为VMExitHandler(来自VMExitHandler.asm的组装函数),让我们详细了解一下。

首先,我们得把以前定义的一些变量代入。

EXTERN gGuestRIP:QWORD
EXTERN gGuestRSP:QWORD

现在我们的VMExitHandler像这样工作,每当发生VM退出时,我们的逻辑内核都会按照HOST_RIP中定义的方式执行VMExitHandler,并且我们的RSP设置为HOST_RSP,那么我们必须保存所有寄存器,这意味着我们必须为此创建一个结构目的,使我们能够以类似C结构读取和修改寄存器。

typedef struct _GUEST_REGS
{
	ULONG64 rax;                  // 0x00         // NOT VALID FOR SVM
	ULONG64 rcx;
	ULONG64 rdx;                  // 0x10
	ULONG64 rbx;
	ULONG64 rsp;                  // 0x20         // rsp is not stored here on SVM
	ULONG64 rbp;
	ULONG64 rsi;                  // 0x30
	ULONG64 rdi;
	ULONG64 r8;                   // 0x40
	ULONG64 r9;
	ULONG64 r10;                  // 0x50
	ULONG64 r11;
	ULONG64 r12;                  // 0x60
	ULONG64 r13;
	ULONG64 r14;                  // 0x70
	ULONG64 r15;
} GUEST_REGS, *PGUEST_REGS;

只需按_GUEST_REGS顺序推入所有寄存器,并将RSP作为第一个参数推入MainVMExitHandler(Fastcall RCX),然后删减一些阴影空间。

VMExitHandler如下所示:

VMExitHandler PROC

    push r15
    push r14
    push r13
    push r12
    push r11
    push r10
    push r9
    push r8        
    push rdi
    push rsi
    push rbp
    push rbp	; rsp
    push rbx
    push rdx
    push rcx
    push rax	


	mov rcx, rsp		; Fast call argument to PGUEST_REGS
	sub	rsp, 28h		; Free some space for Shadow Section

	call	MainVMExitHandler

	add	rsp, 28h		; Restore the state

	; Check whether we have to turn off VMX or Not (the result is in RAX)
	CMP	al, 1
	JE		VMXOFFHandler

    ;Restore the state
    pop rax
    pop rcx
    pop rdx
    pop rbx
    pop rbp		; rsp
    pop rbp
    pop rsi
    pop rdi 
    pop r8
    pop r9
    pop r10
    pop r11
    pop r12
    pop r13
    pop r14
    pop r15


	sub rsp, 0100h ; to avoid error in future functions
	JMP VM_Resumer
	

VMExitHandler ENDP

从上面的代码中,当我们从MainVMExitHandler返回时,我们必须检查MainVMExitHandler的返回结果(在RAX中)是否告诉我们关闭hypervisor还是继续执行hypervisor。

如果需要继续,则重启寄存器状态并跳转到我们的VM_Resumer函数。

VM_Resumer只执行_vmx_vmresume,处理器把RIP设为GUEST_RIP。

VOID VM_Resumer(VOID)
{

	__vmx_vmresume();

	// if VMRESUME succeed will never be here !

	ULONG64 ErrorCode = 0;
	__vmx_vmread(VM_INSTRUCTION_ERROR, &ErrorCode);
	__vmx_off();
	DbgPrint("[*] VMRESUME Error : 0x%llx\n", ErrorCode);

	// It's such a bad error because we don't where to go !
	// prefer to break
	DbgBreakPoint();
}

但是,如果需要将其关闭呢?

然后基于AL,它跳转到另一个名为VMXOFFHandler的函数。这是一个简单的函数,它执行VMXOFF并关闭hypervisor(在当前的逻辑内核中),然后将寄存器重启到它们先前的状态,就像我们将它们保存在VMExitHandler中一样。

我们唯一要做的就是将堆栈指针更改为GUEST_RSP(我们保存在gGuestRSP里),然后跳转到GUEST_RIP(保存在gGuestRIP里)。

VMXOFFHandler PROC

	; Turn VMXOFF
	VMXOFF

	;INT		3
	;Restore the state
	pop rax
    pop rcx
    pop rdx
    pop rbx
    pop rbp		; rsp
    pop rbp
    pop rsi
    pop rdi 
    pop r8
    pop r9
    pop r10
    pop r11
    pop r12
    pop r13
    pop r14
    pop r15

	; Set guest RIP and RSP
	MOV		RSP, gGuestRSP

	JMP		gGuestRIP

VMXOFFHandler ENDP

现在一切都完成了,我们执行了常规的Windows(驱动程序)例程,我的意思是从RunOnProcessorForTerminateVMX执行的最后一个CPUID之后开始执行,但是现在我们不在VMX操作中。

VM退出处理程序

将以上所有代码放在一起,现在我们必须管理不同类型的VM-Exit,因此我们需要修改前面说明的MainVMExitHandler。

我们需要管理的第一件事是检测在VMX非根操作中执行的每条VMX指令,这可以使用以下代码完成:

	// 25.1.2  Instructions That Cause VM Exits Unconditionally
	// The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC,
	// INVD, and XSETBV. This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID, 
	// VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON.

	case EXIT_REASON_VMCLEAR:
	case EXIT_REASON_VMPTRLD:
	case EXIT_REASON_VMPTRST:
	case EXIT_REASON_VMREAD:
	case EXIT_REASON_VMRESUME:
	case EXIT_REASON_VMWRITE:
	case EXIT_REASON_VMXOFF:
	case EXIT_REASON_VMXON:
	case EXIT_REASON_VMLAUNCH:
	{
		DbgPrint("\n [*] Target guest tries to execute VM Instruction ,"
			"it probably causes a fatal error or system halt as the system might"
			" think it has VMX feature enabled while it's not available due to our use of hypervisor.\n");

		DbgBreakPoint();
		ULONG RFLAGS = 0;
		__vmx_vmread(GUEST_RFLAGS, &RFLAGS);
		__vmx_vmwrite(GUEST_RFLAGS, RFLAGS | 0x1); // cf=1 indicate vm instructions fail
		break;
	}

正如我在DbgPrint中告诉你的那样,执行此类VMX指令最终将导致BSOD,因为在我们的hypervisor出现之前可能会对hypervisor的存在进行某些检查,因此执行这些指令的例程(当然,它来自内核)可能认为可以执行这些指令,如果对它们的管理不善(很常见),则会看到BSOD,因此必须找出调用此类指令的原因并手动将其禁用。

如果你配置了任何基于CPU的控件,或者你的处理器支持任何CR Access Exit控件的1-设置,则可以使用上文所述的函数进行管理。

		case EXIT_REASON_CR_ACCESS:
	{
		HandleControlRegisterAccess(GuestRegs);

		break;
	}

如果你未设置任何MSR位(因此导致每个RDMSR和WRMSR退出),或者你在MSRBitMaps中设置了任何位,则MSR访问也是如此:那么你必须使用以下RDMSR函数来管理它们:

		case EXIT_REASON_MSR_READ:
	{
		ULONG ECX = GuestRegs->rcx & 0xffffffff;
		DbgPrint("[*] RDMSR (based on bitmap) : 0x%llx\n", ECX);
		HandleMSRRead(GuestRegs);

		break;
	}

然后这段代码用于管理WRMSR:

		case EXIT_REASON_MSR_WRITE:
	{
		ULONG ECX = GuestRegs->rcx & 0xffffffff;
		DbgPrint("[*] WRMSR (based on bitmap) : 0x%llx\n", ECX);
		HandleMSRWrite(GuestRegs);

		break;
	}

如果要检测I/O指令执行,则:

		case EXIT_REASON_IO_INSTRUCTION:
	{
		UINT64 RIP = 0;
		__vmx_vmread(GUEST_RIP, &RIP);

		DbgPrint("[*] RIP executed IO instruction : 0x%llx\n", RIP);
		DbgBreakPoint();

		break;
	}

如果要使用上述函数,请不要忘记设置足够的基于CPU的控制字段。

对我们来说重要的最后一件事是CPUID Handler,它调用HandleCPUID(如上所述),如果结果为true,则保存GUEST_RSP和GUEST_RIP,以便在我们的内核中执行VMXOFF之后可以使用这些值来恢复状态。

	case EXIT_REASON_CPUID:
	{
		Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not
		if (Status)
		{
			// We have to save GUEST_RIP & GUEST_RSP somewhere to restore them directly

			ULONG ExitInstructionLength = 0;
			gGuestRIP = 0;
			gGuestRSP = 0;
			__vmx_vmread(GUEST_RIP, &gGuestRIP);
			__vmx_vmread(GUEST_RSP, &gGuestRSP);
			__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);



			gGuestRIP += ExitInstructionLength;


		}
		break;
	}

让我们测试一下!

现在是时候测试我们的hypervisor了。

虚拟化所有内核

首先,我们必须加载驱动程序。

50.png

运行驱动程序

然后调用了DriverEntry,因此我们必须运行我们的用户模式应用程序以虚拟化所有内核。

51.png

来自 Scratch App的hypervisor

你会看到,如果你按任意键或关闭此窗口,则会调用DrvClose并重启状态(VMXOFF)。

52.png

驱动日志

现在,所有内核都位于hypervisor。

使用Hypervisor更改CPUID

现在让我们测试一下hypervisor的存在,在本例中,我使用了Immunity Debugger通过自定义EAX执行CPUID。不过,你可以使用任何其他调试器或任何自定义应用程序。

53.png

将0x40000001设置为RAX

你必须将EAX手动设置为HYPERV_CPUID_INTERFACE(0x40000001),然后执行CPUID。

54.png

HVFS在RAX中

如你所见,HVFS(0x48564653)在EAX上,因此我们使用hypervisor成功挂钩了CPUID执行。

55.png

在没有hypervisor的情况下测试HYPERV_CPUID_INTERFACE

现在,你必须关闭用户模式应用程序窗口,以便在所有内核上执行VMXOFF,让我们再次测试上面的示例。

56.png

在没有hypervisor的情况下测试CPUID

此时,你可以看到原始结果出现了。

检测MSR读写(MSRBitmap)

为了测试MSR位图,我创建了一个本地内核调试器(使用Windbg)。在Windbg中,你可以执行RDMSR和WRMSR来读取和写入MSR,就像使用系统驱动程序执行RDMSR和WRMSR一样。

在我们的VirtualizeCurrentSystem函数中,添加了以下行。

SetMSRBitmap(0xc0000082, ProcessorID, TRUE, TRUE);

57.png

Windbg本地调试器(RDMSR和WRMSR)

在远程调试器系统中,你可以看到如下结果:

58.png

检测到远程内核调试器RDMSR执行!

可以看出,检测到RDMSR的执行。

总结

在这7部分中,我们看到了如何通过为每个逻辑内核分别配置VMCS字段来虚拟化已经运行的系统。然后,我们使用hypervisor来更改CPUID指令的结果,并监控对控制寄存器或MSR的每次访问,在这些操作以后我们的hypervisor准备完成,就可以使用扩展页表了。我个人认为,hypervisor中最有趣的工作都可以使用EPT来完成,因为它具有特殊的日志记录机制,例如页面读写访问检测。

本文翻译自:https://rayanfam.com/topics/hypervisor-from-scratch-part-6/如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2024 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务