windows核心情景分析讀書筆記-----HYPERSPACE
主要介紹HYPERSPACE的建立對映函式
賞光看我這一系列文章的朋友最好結合毛德操老師的書來看,具體的細節我這裡就不闡述了
簡單說下這個函式功能
Windows核心有時候需要把某些物理頁面臨時對映到核心的虛存空間,用做臨時的用途
#define HYPERSPACE (Ke386Pae?0x c080 0000:0x c040 0000)
區間大小為0x0040 0000 也就是1024個頁面
其中Ke386Pae這個布林值代表系統是32位還是64位
PVOID
NTAPI
MmCreateHyperspaceMapping(PFN_TYPE Page)
{
PVOID Address;
ULONG i;
if (Ke386Pae)
{
ULONGLONG Entry;
ULONGLONG ZeroEntry = 0LL;
PULONGLONG Pte;
Entry = PFN_TO_PTE(Page) | PA_PRESENT | PA_READWRITE;
Pte = PAE_ADDR_TO_PTE(HYPERSPACE) + Page % 1024;
if (Page & 1024)
{
for (i = Page %1024; i < 1024; i++, Pte++)
{
if (0LL == ExfInterlockedCompareExchange64UL(Pte, &Entry, &ZeroEntry))
{
break;
}
}
if (i >= 1024)
{
Pte = PAE_ADDR_TO_PTE(HYPERSPACE);
for (i = 0; i < Page % 1024; i++, Pte++)
{
if (0LL == ExfInterlockedCompareExchange64UL(Pte, &Entry, &ZeroEntry))
{
break;
}
}
if (i >= Page % 1024)
{
KEBUGCHECK(0);
}
}
}
else
{
for (i = Page %1024; (LONG)i >= 0; i--, Pte--)
{
if (0LL == ExfInterlockedCompareExchange64UL(Pte, &Entry, &ZeroEntry))
{
break;
}
}
if ((LONG)i < 0)
{
Pte = PAE_ADDR_TO_PTE(HYPERSPACE) + 1023;
for (i = 1023; i > Page % 1024; i--, Pte--)
{
if (0LL == ExfInterlockedCompareExchange64UL(Pte, &Entry, &ZeroEntry))
{
break;
}
}
if (i <= Page % 1024)
{
KEBUGCHECK(0);
}
}
}
}
else
{
ULONG Entry;
PULONG Pte;
Entry = PFN_TO_PTE(Page) | PA_PRESENT | PA_READWRITE;//構建頁面表項PTE內容,設定PRESENT,WRITEREAD標誌位等
Pte = ADDR_TO_PTE(HYPERSPACE) + Page % 1024;//頁面表項的地址 講解點A
if (Page & 1024)//page第10位為1,代表虛存頁面搜尋的方向為上
{
for (i = Page % 1024; i < 1024; i++, Pte++)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{//Pte與0比較,如果相等,返回Pte。否則返回Entry
break;
}
}
if (i >= 1024)//遍歷到上邊界,則要重新返回下邊界,下邊界就是那個特殊頁面
{
Pte = ADDR_TO_PTE(HYPERSPACE);
for (i = 0; i < Page % 1024; i++, Pte++)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if (i >= Page % 1024)
{
KEBUGCHECK(0);
}
}
}
else
{
for (i = Page % 1024; (LONG)i >= 0; i--, Pte--)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if ((LONG)i < 0)
{
Pte = ADDR_TO_PTE(HYPERSPACE) + 1023;
for (i = 1023; i > Page % 1024; i--, Pte--)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if (i <= Page % 1024)
{
KEBUGCHECK(0);
}
}
}
}
Address = (PVOID)((ULONG_PTR)HYPERSPACE + i * PAGE_SIZE);
__invlpg(Address);
return Address;
}
講解點A
#define ADDR_TO_PTE(v) (PULONG)(PAGETABLE_MAP + ((((ULONG)(v) / 1024))&(~0x3)))
首先我們先看幾個巨集
#define PAGETABLE_MAP 0xC000 0000
#define HYPERSPACE 0xC040 0000
我們應當明確,虛擬地址中從PAGETABLE_MAP,也就是0xC000 0000一直到C03f ffff儲存著該程序的頁表,而從下一個單元,也就是HYPERSPACE(C040 0000)開始一直到C0C0 0000,這一段空間則是大小為4M的HYPERSPACE空間,我們知道一個頁面是4K大小,則4M就是1K個頁面大小的空間。
而頁表空間,從0xC000 0000一直到C03f ffff大小則為2^22次方,也就是4M,也就是1K個一個頁面的大小。
這個頁面空間大家要注意,我們知道,這裡的頁面對應於整個4G空間,也就是說,裡面一個32位的單元,就對應於整個4G的虛存空間的一個頁面。
這個方式比較神奇,也就是說,自己的一部分,對應於自己的整個身體。
如果我們把計算機比作一個人體,則在人體肚子上取幾個細胞,每個細胞從上到下依次對應著人的頭,胳膊,胸,腹,腿等··(當然這個腦洞開的有點大),自己映射了自己。
那麼,頁表中肯定有幾個32位的值,對應著頁表自身,同時,肯定也有幾個32位的值,對應著後面大小為4M的HYPERSPACE。
我們既然知道頁表大小為4M,而且這4M對應著整個4G空間,其中每個32位的值(也就是4B)對應著一個頁面,所以其中頁面項在頁表中的偏移就是其對應的物理頁面在整個4G空間的偏移。
在非Ke386Pae部分,我們看到了
Pte = ADDR_TO_PTE(HYPERSPACE) + Page % 1024;
ADDR_TO_PTE巨集定義如下
#define ADDR_TO_PTE(v) (PULONG)(PAGETABLE_MAP + ((((ULONG)(v) / 1024))&(~0x3)))
經過上面的敘述,這個應該不難分析了,部分對映整體。
經過計算,該巨集結果為C030 1000
整體過程分析如下:
藍色代表頁表空間,紅色代表HYPERSPACE,二者大小都是4M
而位於藍色頁表中的黃色部分,則對映著整個HYPERSPACE,也就是紅色部分。
程式首先從 ADDR_TO_PTE(HYPERSPACE) + Page % 1024位置處開始尋找(這裡假設為),如果B10位1,則往上找,到了頂點後,再返回最下邊,往上找,如果B10為0,則相反。
這裡有個比較有意思的事情,它並不是直接在紅色部分直接一個一個頁面的找自由頁面的,而是在它的對映裡(也就是黃色部分)中查詢的。
所以迴圈裡i的變化,pte的變化,一直限定在黃色部分。直到迴圈結束,i確定後,才從紅色部分基址C040 0000開始,加上i*PAGESIZE,返回的結果就是紅色部分中一個頁面的地址。