注释:
其实 内容很多 一般是不背 的;需要的时候来查就行;但是要大致知道是做啥的。
》 设置 Guest Area
// 设置GUEST
// 当尝试执行 VM_Guest 出错的时候,会调用VM_Exit.
Vmx_VmWrite(GUEST_CR0, Asm_GetCr0());
Vmx_VmWrite(GUEST_CR3, Asm_GetCr3());
Vmx_VmWrite(GUEST_CR4, Asm_GetCr4());
?
Vmx_VmWrite(GUEST_DR7, 0x400);
Vmx_VmWrite(GUEST_RFLAGS, Asm_GetEflags() & ~0x200);//cli
?
Vmx_VmWrite(GUEST_ES_SELECTOR, Asm_GetEs() & 0xFFF8);
Vmx_VmWrite(GUEST_CS_SELECTOR, Asm_GetCs() & 0xFFF8);
Vmx_VmWrite(GUEST_DS_SELECTOR, Asm_GetDs() & 0xFFF8);
Vmx_VmWrite(GUEST_FS_SELECTOR, Asm_GetFs() & 0xFFF8);
Vmx_VmWrite(GUEST_GS_SELECTOR, Asm_GetGs() & 0xFFF8);
Vmx_VmWrite(GUEST_SS_SELECTOR, Asm_GetSs() & 0xFFF8);
Vmx_VmWrite(GUEST_TR_SELECTOR, Asm_GetTr() & 0xFFF8);
?
Vmx_VmWrite(GUEST_ES_AR_BYTES, 0x10000);// 设置成不可用;然后进入GuestEntry 刷新
Vmx_VmWrite(GUEST_FS_AR_BYTES, 0x10000);
Vmx_VmWrite(GUEST_DS_AR_BYTES, 0x10000);
Vmx_VmWrite(GUEST_SS_AR_BYTES, 0x10000);
Vmx_VmWrite(GUEST_GS_AR_BYTES, 0x10000);
Vmx_VmWrite(GUEST_LDTR_AR_BYTES, 0x10000);
?
Vmx_VmWrite(GUEST_CS_AR_BYTES, 0xc09b);// CS 和 TR 不能 像 前面一样设置成不可用,然后进入GuestEntry 刷新;因为GuestEntry以来CS和TR
Vmx_VmWrite(GUEST_CS_BASE, 0); // 所以需要手动设置。
Vmx_VmWrite(GUEST_CS_LIMIT, 0xffffffff);
?
Vmx_VmWrite(GUEST_TR_AR_BYTES, 0x008b);
Vmx_VmWrite(GUEST_TR_BASE, 0x80042000);
Vmx_VmWrite(GUEST_TR_LIMIT, 0x20ab);
?
?
Vmx_VmWrite(GUEST_GDTR_BASE, GdtBase);
Vmx_VmWrite(GUEST_GDTR_LIMIT, Asm_GetGdtLimit());
Vmx_VmWrite(GUEST_IDTR_BASE, IdtBase);
Vmx_VmWrite(GUEST_IDTR_LIMIT, Asm_GetIdtLimit());
?
Vmx_VmWrite(GUEST_IA32_DEBUGCTL, Asm_ReadMsr(MSR_IA32_DEBUGCTL)&0xFFFFFFFF);
Vmx_VmWrite(GUEST_IA32_DEBUGCTL_HIGH, Asm_ReadMsr(MSR_IA32_DEBUGCTL)>>32);
?
Vmx_VmWrite(GUEST_SYSENTER_CS, Asm_ReadMsr(MSR_IA32_SYSENTER_CS)&0xFFFFFFFF);
Vmx_VmWrite(GUEST_SYSENTER_ESP, Asm_ReadMsr(MSR_IA32_SYSENTER_ESP)&0xFFFFFFFF);
Vmx_VmWrite(GUEST_SYSENTER_EIP, Asm_ReadMsr(MSR_IA32_SYSENTER_EIP)&0xFFFFFFFF); // KiFastCallEntry
?
Vmx_VmWrite(GUEST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x1000); //Guest 临时栈
Vmx_VmWrite(GUEST_RIP, (ULONG)GuestEntry); // 客户机的入口点
?
Vmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff);// referrence volume 3. 24.10
Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff);
》 设置 入口函数(例程)
void _declspec(naked) GuestEntry(void)
{
__asm{
mov ax, es// 刷新VM 的selector 背后的数据
mov es, ax
?
mov ax, ds
mov ds, ax
?
mov ax, fs
mov fs, ax
?
mov ax, gs
mov gs, ax
?
mov ax, ss
mov ss, ax
?
//int 3; WARNING!!! HERE CAN‘T USE INT3!!
}
Vmx_VmCall();
__asm{
//jmp g_exit
}
}
》修改 EXIT 函数以 便于查看 EXIT信息
static void VMMEntryPointEbd(void)
{
ULONG ExitReason;
ExitReason = Vmx_VmRead(VM_EXIT_REASON);
g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);
g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);
Log("g_GuestRegs.eip:",g_GuestRegs.eip);
__asm int 3;// Interrupt Here;Give the chance to view the Info
}
?
void __declspec(naked) VMMEntryPoint(void)
// 注意:1. 裸函数里面不要用局部变量;因为使用到ebp;而裸函数不维护,如果手动维护,那么和普通函数有什么却别;
// 2. 裸函数 是为了我们更好的控制进来那一刻 寄存器等的 获取、设置
// 3. 裸函数 最好不要太冗余;所以 有很多操作的话,最好 另外封装一个函数,在裸函数中调用即可。(这里封装了VMMEntryPointEbd()函数)
{
// Refresh selector -- >underneath part-- gdtinfo;
//do Exchange itself can refresh the VM TLB when selector right ;
__asm{
mov ax,fs;
mov fs,ax;
mov ax,gs;
mov gs,ax;
}
?
// Call the Func to show the ExitReason and regs!
VMMEntryPointEbd();
?
}
从开始 到现在所实现的阶段性代码 我放在了github 上面有需要的可以参考这里的代码:https://github.com/leibso/VT-learning/tree/05_Fields_Area(环境 vs 2010),
测试代码:
原文:https://www.cnblogs.com/leibso-cy/p/11746977.html