主要介绍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,返回的结果就是红色部分中一个页面的地址。