本文讲解vmcs控制区中Guest state fields和Host state fields字段的填充方法,vm-control fields的相关填充方法将会在下一篇文章中讲解。
在intel白皮书的24.3章中,详细描述了VMCS控制区的字段。如上一篇文章所说,需要设置的vmcs字段为
vm-control fields
除这5个区域之外,vmcs还有一个区域是vm exit信息区域。这个区域是只读的,存储的是vmx指令失败后失败代码的编号。
在《处理器虚拟化技术》3.4章节中,展示了需要填充的字段以及其对应的ID。对于guest区域和host区域,需要填充的字段如下:
长度为16位的字段:
长度为64位的字段
长度为32位的字段
可以看到基本上都是进入guest区域之后的寄存器。其中对段寄存器的填充尤其麻烦,需要分别填充base、attribute、limit、selector。因此需要手动获取段寄存器并将其进行拆分然后进行相应的填充。根据保护模式的知识,段寄存器的结构大致如下
在填充时需要将其拆分并填充,此步骤较为繁琐。
填充guest和host区域时还有四个重要的字段需要获取。分别是进入guest区域之后的rip和rsp以及从guest返回到host区域之后的rip rsp。这里我们要使得进入guest区域之后系统仍然正常进行,还是从原来的地方往下跑。因此要通过函数获取需要返回的上一层函数的返回地址以及rsp。而对于返回host区域之后的host eip,由于从虚拟机中返回一定是发生了vmexit事件,需要对该事件进行处理,因此从虚拟机中返回之后的rip一定要设置为vmexit事件的处理函数。而rsp则需要重新开辟一块内存区域供vmexit事件处理函数使用。如果还是使用guest返回之前的堆栈,则会破坏堆栈中的内容,导致无法预知的结果。
接下来开始具体字段的填充讲解。
和设置vmon区域类似,首先也是申请内存区域然后填充IA32_VMX_BASIC。而在进行基本ID的填充之后,要通过vmclear初始化内存并通过vmptrld选择vmcs区域。这两步操作对应的是前一篇文章说的拔电源以及选中机器。在完成之后,便是最复杂的vmcs字段的填充。这里对于每一块vmcs字段,分别封装一个函数进行初始化。本篇文章讨论的是guest区域和host区域的初始化,分别对应的是 VmxInitGuest
和 VmxInitHost
函数。
对于guest相关的字段,需要传入的是guesteip和guestesp,以用来确定在进入guest虚拟机之后guest从哪里开始跑。其他的字段全部根据当前状态填入。首先需要填入的便是gdt表中各个段寄存器的base、limit、attribute、selector。在对其ID进行观察后可以发现,这些字段的id都是连在一起的,id的值相差2。而对于这些段寄存器,分离base、limit、attribute、selector的方法也非常类似。因此可以考虑将填写段寄存器的属性的方法封装成一个函数。这里将其封装成 fillGdtDataItem
函数。对于各个属性的分离,依照下图来进行。具体分离的细节不赘述,建议仔细读懂代码中切割bit的方法。
对于tr寄存器的gdt表项,并不能像其他寄存器一样进行填充。因为在64位下,tr寄存器的gdt表项是128位的。因此需要单独设置。64位下tr寄存器的gdt表项格式在intel白皮书的7.2.3章节中有解析。其结构如下
因此其需要用另外一套代码进行设置,具体代码如下。思路与其他gdt表项的设置思路相同,都是取出对应的位填入vmcs区域中。
int
VmxInitVmOn()
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys,heiPhy;
lowphys.QuadPart
=
0
;
heiPhy.QuadPart
=
-
1
;
pVcpu
-
>VmxOnAddr
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxOnAddr)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxOnAddr,
0
, PAGE_SIZE);
pVcpu
-
>VmxOnAddrPhys
=
MmGetPhysicalAddress(pVcpu
-
>VmxOnAddr);
/
/
填充
ID
ULONG64 vmxBasic
=
__readmsr(IA32_VMX_BASIC);
*
(PULONG)pVcpu
-
>VmxOnAddr
=
(ULONG)vmxBasic;
/
/
CR0,CR4
ULONG64 vcr00
=
__readmsr(IA32_VMX_CR0_FIXED0);
ULONG64 vcr01
=
__readmsr(IA32_VMX_CR0_FIXED1);
ULONG64 vcr04
=
__readmsr(IA32_VMX_CR4_FIXED0);
ULONG64 vcr14
=
__readmsr(IA32_VMX_CR4_FIXED1);
ULONG64 mcr4
=
__readcr4();
ULONG64 mcr0
=
__readcr0();
mcr4 |
=
vcr04;
mcr4 &
=
vcr14;
mcr0 |
=
vcr00;
mcr0 &
=
vcr01;
/
/
__writecr0(mcr0);
__writecr4(mcr4);
int
error
=
__vmx_on(&pVcpu
-
>VmxOnAddrPhys.QuadPart);
if
(error)
{
/
/
释放内存,重置CR4
mcr4 &
=
~vcr04;
__writecr4(mcr4);
MmFreeContiguousMemorySpecifyCache(pVcpu
-
>VmxOnAddr, PAGE_SIZE, MmCached);
pVcpu
-
>VmxOnAddr
=
NULL;
pVcpu
-
>VmxOnAddrPhys.QuadPart
=
0
;
}
return
error;
}
int
VmxInitVmOn()
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys,heiPhy;
lowphys.QuadPart
=
0
;
heiPhy.QuadPart
=
-
1
;
pVcpu
-
>VmxOnAddr
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxOnAddr)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxOnAddr,
0
, PAGE_SIZE);
pVcpu
-
>VmxOnAddrPhys
=
MmGetPhysicalAddress(pVcpu
-
>VmxOnAddr);
/
/
填充
ID
ULONG64 vmxBasic
=
__readmsr(IA32_VMX_BASIC);
*
(PULONG)pVcpu
-
>VmxOnAddr
=
(ULONG)vmxBasic;
/
/
CR0,CR4
ULONG64 vcr00
=
__readmsr(IA32_VMX_CR0_FIXED0);
ULONG64 vcr01
=
__readmsr(IA32_VMX_CR0_FIXED1);
ULONG64 vcr04
=
__readmsr(IA32_VMX_CR4_FIXED0);
ULONG64 vcr14
=
__readmsr(IA32_VMX_CR4_FIXED1);
ULONG64 mcr4
=
__readcr4();
ULONG64 mcr0
=
__readcr0();
mcr4 |
=
vcr04;
mcr4 &
=
vcr14;
mcr0 |
=
vcr00;
mcr0 &
=
vcr01;
/
/
__writecr0(mcr0);
__writecr4(mcr4);
int
error
=
__vmx_on(&pVcpu
-
>VmxOnAddrPhys.QuadPart);
if
(error)
{
/
/
释放内存,重置CR4
mcr4 &
=
~vcr04;
__writecr4(mcr4);
MmFreeContiguousMemorySpecifyCache(pVcpu
-
>VmxOnAddr, PAGE_SIZE, MmCached);
pVcpu
-
>VmxOnAddr
=
NULL;
pVcpu
-
>VmxOnAddrPhys.QuadPart
=
0
;
}
return
error;
}
PULONG64 retAddr
=
(PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp
=
retAddr
+
1
;
ULONG64 guestEip
=
*
retAddr;
PULONG64 retAddr
=
(PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp
=
retAddr
+
1
;
ULONG64 guestEip
=
*
retAddr;
int
VmxInit(ULONG64 hostEip)
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
pVcpu
-
>cpuNumber
=
KeGetCurrentProcessorNumberEx(NULL);
PULONG64 retAddr
=
(PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp
=
retAddr
+
1
;
ULONG64 guestEip
=
*
retAddr;
int
error
=
VmxInitVmOn();
if
(error)
{
DbgPrintEx(
77
,
0
,
"[db]:vmon 初始化失败 error = %d,cpunumber %d\r\n"
, error, pVcpu
-
>cpuNumber);
return
error;
}
error
=
VmxInitVmcs(guestEip, guestEsp, hostEip);
if
(error)
{
DbgPrintEx(
77
,
0
,
"[db]:vmcs 初始化失败 error = %d,cpunumber %d\r\n"
, error, pVcpu
-
>cpuNumber);
VmxDestory();
return
error;
}
return
0
;
}
int
VmxInit(ULONG64 hostEip)
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
pVcpu
-
>cpuNumber
=
KeGetCurrentProcessorNumberEx(NULL);
PULONG64 retAddr
=
(PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp
=
retAddr
+
1
;
ULONG64 guestEip
=
*
retAddr;
int
error
=
VmxInitVmOn();
if
(error)
{
DbgPrintEx(
77
,
0
,
"[db]:vmon 初始化失败 error = %d,cpunumber %d\r\n"
, error, pVcpu
-
>cpuNumber);
return
error;
}
error
=
VmxInitVmcs(guestEip, guestEsp, hostEip);
if
(error)
{
DbgPrintEx(
77
,
0
,
"[db]:vmcs 初始化失败 error = %d,cpunumber %d\r\n"
, error, pVcpu
-
>cpuNumber);
VmxDestory();
return
error;
}
return
0
;
}
int
VmxInitVmcs(ULONG64 GuestEip,ULONG64 GuestEsp, ULONG64 hostEip)
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys, heiPhy;
lowphys.QuadPart
=
0
;
heiPhy.QuadPart
=
-
1
;
pVcpu
-
>VmxcsAddr
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxcsAddr)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxcsAddr,
0
, PAGE_SIZE);
pVcpu
-
>VmxcsAddrPhys
=
MmGetPhysicalAddress(pVcpu
-
>VmxcsAddr);
pVcpu
-
>VmxHostStackTop
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE
*
36
, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxHostStackTop)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxHostStackTop,
0
, PAGE_SIZE
*
36
);
pVcpu
-
>VmxHostStackBase
=
(ULONG64)pVcpu
-
>VmxHostStackTop
+
PAGE_SIZE
*
36
-
0x200
;
/
/
填充
ID
ULONG64 vmxBasic
=
__readmsr(IA32_VMX_BASIC);
*
(PULONG)pVcpu
-
>VmxcsAddr
=
(ULONG)vmxBasic;
/
/
加载VMCS
__vmx_vmclear(&pVcpu
-
>VmxcsAddrPhys.QuadPart);
__vmx_vmptrld(&pVcpu
-
>VmxcsAddrPhys.QuadPart);
VmxInitGuest(GuestEip, GuestEsp);
VmxInitHost(hostEip);
}
int
VmxInitVmcs(ULONG64 GuestEip,ULONG64 GuestEsp, ULONG64 hostEip)
{
PVMXCPUPCB pVcpu
=
VmxGetCurrentCPUPCB();
PHYSICAL_ADDRESS lowphys, heiPhy;
lowphys.QuadPart
=
0
;
heiPhy.QuadPart
=
-
1
;
pVcpu
-
>VmxcsAddr
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxcsAddr)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxcsAddr,
0
, PAGE_SIZE);
pVcpu
-
>VmxcsAddrPhys
=
MmGetPhysicalAddress(pVcpu
-
>VmxcsAddr);
pVcpu
-
>VmxHostStackTop
=
MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE
*
36
, lowphys, heiPhy, lowphys, MmCached);
if
(!pVcpu
-
>VmxHostStackTop)
{
/
/
申请内存失败
return
-
1
;
}
memset(pVcpu
-
>VmxHostStackTop,
0
, PAGE_SIZE
*
36
);
pVcpu
-
>VmxHostStackBase
=
(ULONG64)pVcpu
-
>VmxHostStackTop
+
PAGE_SIZE
*
36
-
0x200
;
/
/
填充
ID
ULONG64 vmxBasic
=
__readmsr(IA32_VMX_BASIC);
*
(PULONG)pVcpu
-
>VmxcsAddr
=
(ULONG)vmxBasic;
/
/
加载VMCS
__vmx_vmclear(&pVcpu
-
>VmxcsAddrPhys.QuadPart);
__vmx_vmptrld(&pVcpu
-
>VmxcsAddrPhys.QuadPart);
VmxInitGuest(GuestEip, GuestEsp);
VmxInitHost(hostEip);
}
void fillGdtDataItem(
int
index,short selector)
{
GdtTable gdtTable
=
{
0
};
AsmGetGdtTable(&gdtTable);
selector &
=
0xFFF8
;
ULONG limit
=
__segmentlimit(selector);
PULONG item
=
(PULONG)(gdtTable.Base
+
selector);
LARGE_INTEGER itemBase
=
{
0
};
itemBase.LowPart
=
(
*
item &
0xFFFF0000
) >>
16
;
item
+
=
1
;
itemBase.LowPart |
=
(
*
item &
0xFF000000
) | ((
*
item &
0xFF
) <<
16
);
/
/
属性
ULONG attr
=
(
*
item &
0x00F0FF00
) >>
8
;
if
(selector
=
=
0
)
{
attr |
=
1
<<
16
;
}
__vmx_vmwrite(GUEST_ES_BASE
+
index
*
2
, itemBase.QuadPart);
__vmx_vmwrite(GUEST_ES_LIMIT
+
index
*
2
, limit);
__vmx_vmwrite(GUEST_ES_AR_BYTES
+
index
*
2
, attr);
__vmx_vmwrite(GUEST_ES_SELECTOR
+
index
*
2
, selector);
}
void fillGdtDataItem(
int
index,short selector)
{
GdtTable gdtTable
=
{
0
};
AsmGetGdtTable(&gdtTable);
selector &
=
0xFFF8
;
ULONG limit
=
__segmentlimit(selector);
PULONG item
=
(PULONG)(gdtTable.Base
+
selector);
LARGE_INTEGER itemBase
=
{
0
};
itemBase.LowPart
=
(
*
item &
0xFFFF0000
) >>
16
;
item
+
=
1
;
itemBase.LowPart |
=
(
*
item &
0xFF000000
) | ((
*
item &
0xFF
) <<
16
);
/
/
属性
ULONG attr
=
(
*
item &
0x00F0FF00
) >>
8
;
if
(selector
=
=
0
)
{
attr |
=
1
<<
16
;
}
__vmx_vmwrite(GUEST_ES_BASE
+
index
*
2
, itemBase.QuadPart);
__vmx_vmwrite(GUEST_ES_LIMIT
+
index
*
2
, limit);
__vmx_vmwrite(GUEST_ES_AR_BYTES
+
index
*
2
, attr);
__vmx_vmwrite(GUEST_ES_SELECTOR
+
index
*
2
, selector);
}
GdtTable gdtTable
=
{
0
};
AsmGetGdtTable(&gdtTable);
ULONG trSelector
=
AsmReadTR();
trSelector &
=
0xFFF8
;
ULONG trlimit
=
__segmentlimit(trSelector);
LARGE_INTEGER trBase
=
{
0
};
PULONG trItem
=
(PULONG)(gdtTable.Base
+
trSelector);
/
/
读TR
trBase.LowPart
=
((trItem[
0
] >>
16
) &
0xFFFF
) | ((trItem[
1
] &
0xFF
) <<
16
) | ((trItem[
1
] &
0xFF000000
));
trBase.HighPart
=
trItem[
2
];
/
/
属性
ULONG attr
=
(trItem[
1
] &
0x00F0FF00
) >>
8
;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
GdtTable gdtTable
=
{
0
};
AsmGetGdtTable(&gdtTable);
ULONG trSelector
=
AsmReadTR();
trSelector &
=
0xFFF8
;
ULONG trlimit
=
__segmentlimit(trSelector);
LARGE_INTEGER trBase
=
{
0
};
PULONG trItem
=
(PULONG)(gdtTable.Base
+
trSelector);
/
/
读TR
trBase.LowPart
=
((trItem[
0
] >>
16
) &
0xFFFF
) | ((trItem[
1
] &
0xFF
) <<
16
) | ((trItem[
1
] &
0xFF000000
));
trBase.HighPart
=
trItem[
2
];
/
/
属性
ULONG attr
=
(trItem[
1
] &
0x00F0FF00
) >>
8
;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(
7
));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
__vmx_vmwrite(VMCS_LINK_POINTER,
-
1
);
__vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(IA32_MSR_DEBUGCTL));
__vmx_vmwrite(GUEST_IA32_PAT, __readmsr(IA32_MSR_PAT));
__vmx_vmwrite(GUEST_IA32_EFER, __readmsr(IA32_MSR_EFER));
__vmx_vmwrite(GUEST_FS_BASE, __readmsr(IA32_FS_BASE));
__vmx_vmwrite(GUEST_GS_BASE, __readmsr(IA32_GS_BASE));
__vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(
0x174
));
__vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(
0x175
));
__vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(
0x176
));
/
/
IDT GDT
GdtTable idtTable;
__sidt(&idtTable);
__vmx_vmwrite(GUEST_GDTR_BASE, gdtTable.Base);
__vmx_vmwrite(GUEST_GDTR_LIMIT, gdtTable.limit);
__vmx_vmwrite(GUEST_IDTR_LIMIT, idtTable.limit);
__vmx_vmwrite(GUEST_IDTR_BASE, idtTable.Base);
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(
7
));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
__vmx_vmwrite(VMCS_LINK_POINTER,
-
1
);
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-3-17 21:21
被smallzhong_编辑
,原因: