驅動開發:內核解析內存四級頁表

當今操作系統普遍採用64位架構,CPU最大尋址能力雖然達到了64位,但其實僅僅只是用到了48位進行尋址,其內存管理採用了9-9-9-9-12的分頁模式,9-9-9-9-12分頁表示物理地址擁有四級頁表,微軟將這四級依次命名爲PXE、PPE、PDE、PTE這四項。

關於內存管理和分頁模式,不同的操作系統和體系結構可能會有略微不同的實現方式。9-9-9-9-12的分頁模式是一種常見的分頁方案,其中物理地址被分成四級頁表:PXE(Page Directory Pointer Table Entry)、PPE(Page Directory Entry)、PDE(Page Table Entry)和PTE(Page Table Entry)。這種分頁模式可以支持大量的物理內存地址映射到虛擬內存地址空間中。每個級別的頁表都負責將虛擬地址映射到更具體的物理地址。通過這種層次化的頁表結構,操作系統可以更有效地管理和分配內存。

首先一個PTE管理1個分頁大小的內存也就是0x1000字節,PTE結構的解析非常容易,打開WinDBG輸入!PTE 0即可解析,如下所示,當前地址0位置處的PTE基址是FFFF898000000000,由於PTE的一個頁大小是0x1000所以當內存地址高於0x1000時將會切換到另一個頁中,如下FFFF898000000008則是另一個頁中的地址。

0: kd> !PTE 0
                                           VA 0000000000000000
PXE at FFFF89C4E2713000    PPE at FFFF89C4E2600000    PDE at FFFF89C4C0000000    PTE at FFFF898000000000
contains 8A0000000405F867  contains 0000000000000000
pfn 405f      ---DA--UW-V  not valid

0: kd> !PTE 0x1000
                                           VA 0000000000001000
PXE at FFFF89C4E2713000    PPE at FFFF89C4E2600000    PDE at FFFF89C4C0000000    PTE at FFFF898000000008
contains 8A0000000405F867  contains 0000000000000000
pfn 405f      ---DA--UW-V  not valid

由於PTE是動態變化的,找到該地址的關鍵就在於通過MmGetSystemRoutineAddress函數動態得到MmGetVirtualForPhysical的內存地址,然後向下掃描特徵尋找mov rdx,0FFFF8B0000000000h並將內部的地址提取出來。

這段代碼完整版如下所示,代碼可動態定位到PTE的內存地址,然後將其取出;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include <ntifs.h>
#include <ntstrsafe.h>

// 指定內存區域的特徵碼掃描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
	PVOID pAddress = NULL;
	PUCHAR i = NULL;
	ULONG m = 0;

	// 掃描內存
	for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
	{
		// 判斷特徵碼
		for (m = 0; m < ulMemoryDataSize; m++)
		{
			if (*(PUCHAR)(i + m) != pMemoryData[m])
			{
				break;
			}
		}
		// 判斷是否找到符合特徵碼的地址
		if (m >= ulMemoryDataSize)
		{
			// 找到特徵碼位置, 獲取緊接着特徵碼的下一地址
			pAddress = (PVOID)(i + ulMemoryDataSize);
			break;
		}
	}

	return pAddress;
}

// 獲取到函數地址
PVOID GetMmGetVirtualForPhysical()
{
	PVOID VariableAddress = 0;
	UNICODE_STRING uioiTime = { 0 };

	RtlInitUnicodeString(&uioiTime, L"MmGetVirtualForPhysical");
	VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime);
	if (VariableAddress != 0)
	{
		return VariableAddress;
	}
	return 0;
}

// 驅動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	// 獲取函數地址
	PVOID address = GetMmGetVirtualForPhysical();

	DbgPrint("GetMmGetVirtualForPhysical = %p \n", address);

	UCHAR pSecondSpecialData[50] = { 0 };
	ULONG ulFirstSpecialDataSize = 0;

	pSecondSpecialData[0] = 0x48;
	pSecondSpecialData[1] = 0xc1;
	pSecondSpecialData[2] = 0xe0;
	ulFirstSpecialDataSize = 3;

	// 定位特徵碼
	PVOID PTE = SearchMemory(address, (PVOID)((PUCHAR)address + 0xFF), pSecondSpecialData, ulFirstSpecialDataSize);
	__try
	{
		PVOID lOffset = (ULONG)PTE + 1;
		DbgPrint("PTE Address = %p \n", lOffset);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		DbgPrint("error");
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

運行如上代碼可動態獲取到當前系統的PTE地址,然後將PTE填入到g_PTEBASE中,即可實現解析系統內的四個標誌位,完整解析代碼如下所示;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include <ntifs.h>
#include <ntstrsafe.h>

INT64 g_PTEBASE = 0;
INT64 g_PDEBASE = 0;
INT64 g_PPEBASE = 0;
INT64 g_PXEBASE = 0;

PULONG64 GetPteBase(PVOID va)
{
	return (PULONG64)((((ULONG64)va & 0xFFFFFFFFFFFF) >> 12) * 8) + g_PTEBASE;
}

PULONG64 GetPdeBase(PVOID va)
{
	return (PULONG64)((((ULONG64)va & 0xFFFFFFFFFFFF) >> 12) * 8) + g_PDEBASE;
}

PULONG64 GetPpeBase(PVOID va)
{
	return (PULONG64)((((ULONG64)va & 0xFFFFFFFFFFFF) >> 12) * 8) + g_PPEBASE;
}

PULONG64 GetPxeBase(PVOID va)
{
	return (PULONG64)((((ULONG64)va & 0xFFFFFFFFFFFF) >> 12) * 8) + g_PXEBASE;
}

// 驅動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	g_PTEBASE = 0XFFFFF20000000000;

	g_PDEBASE = (ULONG64)GetPteBase((PVOID)g_PTEBASE);
	g_PPEBASE = (ULONG64)GetPteBase((PVOID)g_PDEBASE);
	g_PXEBASE = (ULONG64)GetPteBase((PVOID)g_PPEBASE);

	DbgPrint("PXE = %p \n", g_PXEBASE);
	DbgPrint("PPE  = %p \n", g_PPEBASE);
	DbgPrint("PDE  = %p \n", g_PDEBASE);
	DbgPrint("PTE  = %p \n", g_PTEBASE);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

我的系統內PTE地址爲0XFFFFF20000000000,填入變量內解析效果如下圖所示;

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