學習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中分別爲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式)


 

以及頁表的結構:



參閱一些資料,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私有頁表。


看球去啦!!~~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章