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
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私有頁表。
看球去啦!!~~