学习BluePill源码笔记-1

VT-X太高端霸气上档次了.....本菜本着自虐的精神~来学习BluePill的工作方式

BluePill有好几种方式启动,当然ShellCode这种高端霸气上档次的方式俺们菜菜当然用不到啦~

本菜的初学笔记,大牛勿笑,砸场可以,脏话不要,谢谢合作~


一、BluePill的内存管理

1.1驱动的入口点(newbp.c) 内存管理的起始位置

NTSTATUS DriverEntry (
  PDRIVER_OBJECT DriverObject,
  PUNICODE_STRING RegistryPath
)
{
  NTSTATUS Status;

#ifdef USE_COM_PRINTS
  PioInit ((PUCHAR) COM_PORT_ADDRESS);
#endif
  ComInit ();

  Status = MmInitManager ();
  if (!NT_SUCCESS (Status)) {
    _KdPrint (("NEWBLUEPILL: MmInitManager() failed with status 0x%08hX\n", Status));
    return Status;
  }
#ifdef USE_LOCAL_DBGPRINTS
  Status = DbgRegisterWindow (g_BpId);
  if (!NT_SUCCESS (Status)) {
    _KdPrint (("NEWBLUEPILL: DbgRegisterWindow() failed with status 0x%08hX\n", Status));
    MmShutdownManager ();
    return Status;
  }
#endif

  _KdPrint (("\r\n"));
  _KdPrint (("NEWBLUEPILL v%d.%d.%d.%d. Instance Id: 0x%02X\n",
             (NBP_VERSION >> 48) & 0xff,
             (NBP_VERSION >> 32) & 0xff, (NBP_VERSION >> 16) & 0xff, NBP_VERSION & 0xff, g_BpId));

  // We need it only for VMX
  // TODO: this should be conditionally executed only if Arch == VMX
  Status = MmInitIdentityPageTable ();
  if (!NT_SUCCESS (Status)) {
    _KdPrint (("NEWBLUEPILL: MmInitIdentifyPageTable() failed with status 0x%08hX\n", Status));
#ifdef USE_LOCAL_DBGPRINTS
    DbgUnregisterWindow ();
#endif
    MmShutdownManager ();
    return Status;
  }

  Status = MmMapGuestKernelPages ();
  if (!NT_SUCCESS (Status)) {
    _KdPrint (("BEWBLUEPILL: MmMapGuestKernelPages() failed with status 0x%08hX\n", Status));
#ifdef USE_LOCAL_DBGPRINTS
    DbgUnregisterWindow ();
#endif
    MmShutdownManager ();
    return Status;
  }
#ifdef RUN_BY_SHELLCODE
  _KdPrint (("NEWBLUEPILL: Image base: 0x%p, image size: 0x%x\n", DriverObject, (ULONG64) RegistryPath));

  Status = MmMapGuestPages (DriverObject, (ULONG) BYTES_TO_PAGES ((ULONG64) RegistryPath));
#else
  Status = MmMapGuestPages (DriverObject->DriverStart, BYTES_TO_PAGES (DriverObject->DriverSize));
#endif
  if (!NT_SUCCESS (Status)) {
    _KdPrint (("NEWBLUEPILL: MmMapGuestPages() failed to map guest NewBluePill image with status 0x%08hX\n", Status));
#ifdef USE_LOCAL_DBGPRINTS
    DbgUnregisterWindow ();
#endif
    MmShutdownManager ();
    return Status;
  }

  _KdPrint (("NEWBLUEPILL: g_PageMapBasePhysicalAddress: 0x%p\n", g_PageMapBasePhysicalAddress));

  if (!NT_SUCCESS (Status = HvmInit ())) {
    _KdPrint (("NEWBLUEPILL: HvmInit() failed with status 0x%08hX\n", Status));
#ifdef USE_LOCAL_DBGPRINTS
    DbgUnregisterWindow ();
#endif
    MmShutdownManager ();
    return Status;
  }

  if (!NT_SUCCESS (Status = HvmSwallowBluepill ())) {
    _KdPrint (("NEWBLUEPILL: HvmSwallowBluepill() failed with status 0x%08hX\n", Status));
#ifdef USE_LOCAL_DBGPRINTS
    DbgUnregisterWindow ();
#endif
    MmShutdownManager ();
    return Status;
  }
#ifndef RUN_BY_SHELLCODE
  DriverObject->DriverUnload = DriverUnload;
#endif

  _KdPrint (("NEWBLUEPILL: Initialization finished\n"));
#if DEBUG_LEVEL>1
  _KdPrint (("NEWBLUEPILL: RFLAGS = %#x, CR8 = %#x\n", RegGetRflags (), RegGetCr8 ()));
#endif
  return STATUS_SUCCESS;
}

1.2

首先,DriverEntry是进入了MmInitManager();函数(page.c)


NTSTATUS NTAPI MmInitManager (
)
{
  PVOID Pml4Page;
  NTSTATUS Status;
  PHYSICAL_ADDRESS l1, l2, l3;

  InitializeListHead (&g_PageTableList);
  KeInitializeSpinLock (&g_PageTableListLock);

  Pml4Page = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE, ITL_TAG);
  if (!Pml4Page)
    return STATUS_INSUFFICIENT_RESOURCES;
  RtlZeroMemory (Pml4Page, PAGE_SIZE);

  g_PageMapBasePhysicalAddress = MmGetPhysicalAddress (Pml4Page);

  if (!NT_SUCCESS
      (Status =
       MmSavePage (g_PageMapBasePhysicalAddress, (PVOID) PML4_BASE, Pml4Page, PAT_POOL, 1, AP_PAGETABLE | AP_PML4))) {
    DbgPrint ("MmInitManager(): MmSavePage() failed to save PML4 page, status 0x%08X\n", Status);
    return Status;
  }

  if (!NT_SUCCESS (Status = MmCreateMapping (g_PageMapBasePhysicalAddress, (PVOID) PML4_BASE, FALSE))) {
    DbgPrint ("MmInitManager(): MmCreateMapping() failed to map PML4 page, status 0x%08X\n", Status);
    return Status;
  }

  return STATUS_SUCCESS;
}

MmInitManage:初始化了LIST_ENTRY结构和自旋锁后开辟了一页大小的内存。这个内存是用来构造Pml4页表的,接着调用到重要函数MmSavePage。第一个参数是刚才开辟的内存池的物理地址,第二个参数则是PML4_BASE。

MmSavePage 第三个参数是Pml4Page,也就是刚才开辟的内存池。第四个参数类型为PAGE_ALLOCATION_TYPE,定义如下:(page.h)

typedef enum
{
  PAT_DONT_FREE = 0,
  PAT_POOL,
  PAT_CONTIGUOUS
} PAGE_ALLOCATION_TYPE;
第五个参数uNumberOfPages,前面只开辟了PAGE_SIZE,这里自然写1。第六个为flag。


1.3 看到这里,俺来自我科普一些x64的知识了。

1.3.1 PML4_BASE定义如下:(common.h)

#define	PML4_BASE	0xFFFFF6FB7DBED000
#define	PDP_BASE	0xFFFFF6FB7DA00000
#define	PD_BASE		0xFFFFF6FB40000000
#define	PT_BASE		0xFFFFF68000000000
等下,PML4_BASE是啥?

在amd64.h中可以找到似曾相识的定义:

#define PXE_BASE          0xFFFFF6FB7DBED000UI64
#define PPE_BASE          0xFFFFF6FB7DA00000UI64
#define PDE_BASE          0xFFFFF6FB40000000UI64
#define PTE_BASE          0xFFFFF68000000000UI64
查了下资料,64位中目前只用到48位地址计算页表。所以,在用工具看到XX内核地址时,地址都是0xFFFF....开头的。显然,这前面的4个F是无用的。PTE的基址约定为0xFFFFF68000000000。

根据页表换算公式(在这里列举一下:)

PDE = ((lVirtualAddress>>21)<<3) & 0x3FF8 + 0xC0600000;
PTE = ((lVirtualAddress>>12)<<3) & 0x7FFFF8 + 0xC0000000;

上述公式是在x86 开启PAE模式下推导得到的。

在x64非PAE模式下(很少有PAE模式的机子,PAE Flag位于CR4的第五个bit上),我们的计算方式如下:


PDE_BASE = ((PTE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE = 0xF68000000 * 8 + PTE_BASE = 0x7B40000000 + PTE_BASE = 0xFFFFF6FB40000000
PPE_BASE = ((PDE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE = 0xF6FB40000 * 8 + PTE_BASE = 0x7B7DA00000 + PTE_BASE = 0xFFFFF6FB7DA00000
PXE_BASE = ((PPE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE = 0xF6FB7DA00 * 8 + PTE_BASE = 0x7B7DBED000 + PTE_BASE = 0xFFFFF6FB7DBED000
等下,掩码0x0000FFFFFFFFF000是啥?显然是把刚才所说的4个F也就是16个无效位全给清除掉。

等下,PPE和PXE又是啥呢?本菜继续查资料。


1.3.2 64位地址翻译机制 

在64位中,每级页表寻址部分长度位9位。x64体系中,普通页的大小仍为4KB,然而数据表示却为64位长,因此一个4KB页在x64体系结构下只能包含512项内容。为了保证页对齐和页为单位的页表内容换入换出,x64下每级页表寻址部分长度定位9位。


学习BluePill源码笔记-1_第1张图片

学习BluePill源码笔记-1_第2张图片


这四级页表的缩写在BluePill中分别为PML4、PDP、PD和PT。说实话,我看到这里还是晕晕的,于是到msdn上再好好的论证了一下。

Intel refers to each entry as PML4-Table Entry. Internally we refer to this as the eXtended Page directory Entry (PXE).  Regardless of how you refer to these entries they contain indexes into the PDP table (Page Directory Pointer Table).

PML4 = Page map level 4 = PXE

PDP = Page Directory Pointer Table = PPE

换句话说,PXE放的东西就是PPE,PPE放的东西是PDE,PDE放的东西就是PTE。


1.3.3

我们来做一个小实验:(查看线性地址翻译过程)

kd> r cr3
cr3=0000000000187000
kd> !pte fffff800'01661860
                                           VA fffffffffffff800
PXE at FFFFF6FB7DBEDFF8    PPE at FFFFF6FB7DBFFFF8    PDE at FFFFF6FB7FFFFFF8    PTE at FFFFF6FFFFFFFFF8
contains 00000000001F4063  contains 00000000001F3063  contains 00000000001F5063  contains 0000000000000000
pfn 1f4       ---DA--KWEV  pfn 1f3       ---DA--KWEV  pfn 1f5       ---DA--KWEV  not valid

~继续将我们测试的地址0xfffff80001661860化为二进制

.formats fffff80001661860
Evaluate expression:
  Hex:     fffff800`01661860
  Decimal: -8796069554080
  Octal:   1777777600000131414140
  Binary:  11111111 11111111 11111000 00000000 00000001 01100110 00011000 01100000
  Chars:   .....f.`
  Time:    ***** Invalid FILETIME
  Float:   low 4.22618e-038 high -1.#QNAN
  Double:  -1.#QNAN


通过Binary,得到如下字段:

Sign extend               11111111 11111111

PML4 offset               111110000

PDP offset                 000000000

PD offset                    000001011

Page-Table offset         001100001

Physical Page Offset      1000 01100000

9+9+9+9+12 = 48bit (PML4+PDP+PDE+PTE+PAGE OFFSET = 48bit)

为了方便理解上面的字段,贴出IA32页表映射关系:(最常见的4KB式)

学习BluePill源码笔记-1_第3张图片

 

以及页表的结构:

学习BluePill源码笔记-1_第4张图片

学习BluePill源码笔记-1_第5张图片

参阅一些资料,x64非PAE模式下,物理地址如下计算:

PML4 Address = cr3 + PML4 offset * sizeof(PTE)

PDP Address = PML4 Address & 0xfffffffffffff000 + (PDP offset * sizeof(PTE))

PDE Address = PDP Address & 0xfffffffffffff000 + (PD offset * sizeof(PTE))

PTE Address = PDE Address & 0xfffffffffffff000 + (PT offset * sizeof(PTE))

Physical Address = PTE Address & 0xfffffffffffff000 + Physical Page Offset


1.4 执行MmSavePage函数 主要作用是保存对刚刚分配出来私有PML4级页表的描述信息。这个过程被一些文献称为“前私有页表初始化”。

static NTSTATUS NTAPI MmSavePage (
  PHYSICAL_ADDRESS PhysicalAddress,
  PVOID HostAddress,
  PVOID GuestAddress,
  PAGE_ALLOCATION_TYPE AllocationType,
  ULONG uNumberOfPages,
  ULONG Flags
)
{
  PALLOCATED_PAGE AllocatedPage;

  if (!GuestAddress)
    return STATUS_INVALID_PARAMETER;

  AllocatedPage = ExAllocatePoolWithTag (NonPagedPool, sizeof (ALLOCATED_PAGE), ITL_TAG);
  if (!AllocatedPage)
    return STATUS_INSUFFICIENT_RESOURCES;
  RtlZeroMemory (AllocatedPage, sizeof (ALLOCATED_PAGE));

  PhysicalAddress.QuadPart = PhysicalAddress.QuadPart & 0x000ffffffffff000;
  HostAddress = (PVOID) ((ULONG64) HostAddress & 0xfffffffffffff000);

  AllocatedPage->AllocationType = AllocationType;
  AllocatedPage->PhysicalAddress = PhysicalAddress;
  AllocatedPage->HostAddress = HostAddress;
  AllocatedPage->GuestAddress = GuestAddress;
  AllocatedPage->uNumberOfPages = uNumberOfPages;
  AllocatedPage->Flags = Flags;

  ExInterlockedInsertTailList (&g_PageTableList, &AllocatedPage->le, &g_PageTableListLock);

  /*
     DbgPrint("MmSavePage(): PA 0x%X, HostVA 0x%p, GuestVA 0x%p, AT %d, FL 0x%X\n",
     PhysicalAddress.QuadPart,
     HostAddress,
     GuestAddress,
     AllocationType,
     Flags);
   */
  return STATUS_SUCCESS;
}

MmSavePage进行参数检验后开辟了一块内存区域。这次开辟的是一个ALLOCATED_PAGE结构。这个结构如下:(page.h)

typedef struct _ALLOCATED_PAGE
{

  LIST_ENTRY le;

  ULONG Flags;

  PAGE_ALLOCATION_TYPE AllocationType;
  ULONG uNumberOfPages;         // for PAT_CONTIGUOUS only

  PHYSICAL_ADDRESS PhysicalAddress;
  PVOID HostAddress;
  PVOID GuestAddress;

} ALLOCATED_PAGE,
 *PALLOCATED_PAGE;
在下面这两行中:

 PhysicalAddress.QuadPart = PhysicalAddress.QuadPart & 0x000ffffffffff000;
  HostAddress = (PVOID) ((ULONG64) HostAddress & 0xfffffffffffff000);

PhysicalAddress仅取中间的40位,HostAddress只取高52位。这两个处理是根据Intel和AMD的规范。该函数执行成功后,返回MmInitManager()继续执行。

if (!NT_SUCCESS (Status = MmCreateMapping (g_PageMapBasePhysicalAddress, (PVOID) PML4_BASE, FALSE))) {
    DbgPrint ("MmInitManager(): MmCreateMapping() failed to map PML4 page, status 0x%08X\n", Status);
    return Status;
  }
接下来调用的MmCreateMapping()函数开始构建NewBluePill私有页表。


看球去啦!!~~


你可能感兴趣的:(硬件虚拟化)