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;
}
首先,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;
}
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
.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
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;
}
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);
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私有页表。
看球去啦!!~~