天天看點

x64核心記憶體空間結構

0x00 前言

本文主要是讨論Windows 7 x64下的核心虛拟位址空間的結構,可以利用WiinDBG調試的擴充指令"!CMKD.kvas"來顯示x64下的核心虛拟位址空間的整體布局。了解核心的位址布局在某些情況下是很有的,比如說在研究New Blue Pill的源碼和虛拟化的時候。

0x01 基本結構

X64的CPU的位址為64位,但實際上隻支援48位的虛拟位址空間供軟體使用。虛拟位址的高16位在使用者模式下總是被設定為0000,而在核心模式下全置為FFFF。

是以使用者模式的位址空間範圍為0x00000000~00000000——0x0000FFFF~ffffffff,核心模式的位址空間範圍為0xFFFF0000~00000000——0xFFFFffff~ffffffff,是以對作業系統可見的核心虛拟位址空間的大小為256TB。Windows作業系統将整個核心位址空間劃分為若幹個有特定用途的大小固定的虛拟位址空間。下表是關于Windows對于虛拟位址空間具體的劃分:

       起始位址

                結束位址

記憶體大小

         用途

FFFF0800`00000000

         FFFFF67F`FFFFFFFF

238TB

未使用

FFFFF680`00000000

         FFFFF6FF`FFFFFFFF

512GB

PTE記憶體空間

FFFFF700`00000000

         FFFFF77F`FFFFFFFF

Hyper記憶體空間

FFFFF780`00000000

         FFFFF780`00000FFF

4KB

系統共享空間

FFFFF780`00001000

         FFFFF7FF`FFFFFFFF

512GB-4K

系統cache工作集

FFFFF800`00000000

         FFFFF87F`FFFFFFFF

初始化映射區

FFFFF880`00000000 

         FFFFF89F`FFFFFFFF

128GB

系統PTE區域

FFFFF8a0`00000000

         FFFFF8bF`FFFFFFFF

分頁池區域

FFFFF900`00000000

          FFFFF97F`FFFFFFFF

會話空間

FFFFF980`00000000

          FFFFFa70`FFFFFFFF

1TB

核心動态虛拟空間

FFFFFa80`00000000

  *nt!MmNonPagedPoolStart-1

6TB Max

PFN 資料

*nt!MmNonPagedPoolStart

    *nt!MmNonPagedPoolEnd

512GB Max

不分頁記憶體池

FFFFFFFF`FFc00000

          FFFFFFFF`FFFFFFFF

4MB

HAL和加載器映射區

 Windows作業系統用了一些特定的資料結構,比如說Push Locks,Ex Fast Referenced Pointers和Interlocked Slists,對這些資料結構的操作都需要CPU對同一個虛拟位址的數字執行兩遍原子操作。是以雖然64位處理器的虛拟位址是64位,卻必須要有128位長的CMPXCHG指令。但是在早期的64位處理器中是沒有這樣的指令的,在使用上述的資料結構的時候就會引發故障。64位CPU已經将虛拟位址有效位限制為48位,而Windows作業系統則進一步将虛拟位址有效位限制為44位,實際上可以用來存儲上述資料結構的虛拟位址空間大小就是2^44,即64位虛拟位址空間的高8TB的空間,也就是0xFFFFF80000000000 - 0xFFFFFFFFFFFFFFFF。例如之前的”未使用空間”,“PTE記憶體空間”,“ Hyper記憶體空間”和“系統cache工作集”都超出了44位虛拟位址的限制,都無法存儲這些特定的資料結構。這些限制也會影響到使用者空間,将使用者空間可用虛拟記憶體大小限制到了8TB,即0x00000000`00000000 - 0x000007FF`FFFFFFFF,核心空間可用虛拟虛拟記憶體大小也為8TB,即0xFFFFF000`00000000 - 0xFFFFFFFF`FFFFFFFF。需要說明的一點就是,由Windows作業系統使用的不在FFFF0800`00000000 - FFFFF7FF`FFFFFFFF範圍内的虛拟記憶體,也并不是都會配置設定和儲存上述的特定資料結構。

64位處理器實體頁大小是4KB,CPU使用PTEs(Page Table Entry頁表項)來完成從虛拟位址到實體位址的映射,是以每個PTE映射4K大小的實體頁。64位處理器下的PTE占64位,也就是8個位元組為了相容更大的實體位址和PFNs(Page Frame Number,頁幀号)。是以單個頁表的實體頁可以容納512個PTE,所有的PTE可以映射2MB(512*4KB)的虛拟位址。同樣的,因為PDEs(Page Directory Entries,頁目錄項)指向頁表的實體頁,是以單個的PDE可以映射2MB的虛拟位址空間。

0x02 核心虛拟空間組成

下面說明核心位址空間的具體組成部分,及其作用。

未使用的 (Unused System Space)

由nt!MmSystemRangeStart開始,這部分在Windows 7 X64下并未使用

PTE空間  (PTE Space)

這部分包含了x64下使用者空間和核心空間的虛拟位址映射的4級頁表。X64下不同頁表頁的映射範圍如下:

PTE Pages FFFFF680`00000000

PDE Pages FFFFF6FB`40000000

PPE Pages FFFFF6FB`7DA00000

PXE Pages FFFFF6FB`7DBED000

Hyper空間 (HyperSpace)

映射程序的工作集。對每一個程序的EPROCESS.Vm.VmWorkingSetList 中包含的位址

0xFFFFF700`01080000就會映射到這片空間。這片空間包括MMWSL(Memory Manager Working Set List)結構和MMWSLE(Memory Manager Working Set List Entry)的數組結構,包括程序工作集的每個實體頁。

需要注意的是雖然函數MiMapPageInHyperSpaceWorker()支援映射實體頁到Hyper空間的虛拟位址,但實際上是将實體頁映射到了PTE空間,而不是真正的Hyper空間。

共享系統頁 (Shared System Page)

這4K大小的頁是由使用者空間和核心空間共享的,主要是用來在使用者層和核心層之前快速的傳遞資訊,共享資料的資料結構就是nt!_KUSER_SHARED_DATA。

系統cache工作集(System Cache Working Set)

包含系統cache的虛拟位址的工作集(Working Set)和工作集連結清單項(Working Set List Entries)。

核心變量nt!MmSystemCacheWs指向系統cache工作集的資料結構(即nt!_MMSUPPORT)。想要顯示系統cache的工作集連結清單項可以使用WinDBG指令

"!wsle 1 @@(((nt!_MMSUPPORT *) @@(nt!MmSystemCacheWs))->VmWorkingSetList)"。而這些項會被用來修剪(trim)系統cache的虛拟記憶體的實體頁。

初始化加載映射區 (Initial Loader Mappings)

Ntoskrnl,HAL和核心調試DLL(KDCOM,KD1394,KDUSB)都會被加載到這片區域。除此之外,這片空間包含idle線程的線程棧,DPC的棧,KPCR和idle線程的資料結構。

分頁池區域 (Paged Pool Area)

分頁池的結束位址儲存在變量nt!MmPagedPoolEnd中。而分頁池的大小儲存在變量nt!MmSizeOfPagedPoolInBytes。當調用MiVaPagePool()時,MiObtainSystemVa()函數就會從這片區域配置設定記憶體,分頁池的記憶體配置設定方式由變量nt!MiPagedPoolVaBitMap按位(bit)決定。

PFN資料庫(PFN Database)

對于系統的每個實體頁在PFN中都有對應的項(nt!MmHighestPossiblePhysicalPage+1)。可以在WinDBG中輸入指令'? poi(nt!MmNonPagedPoolStart) - poi(nt!MmPfnDatabase)'來獲得”PFN Database”的大小。也可以使用指令

 '?(poi(nt!MmNonPagedPoolStart) - poi(nt!MmPfnDatabase))/ @@(sizeof(nt!_MMPFN))'來獲得PFN中項的總數。而這片區域的起始位址儲存在nt!MmPfnDatabase中。

不分頁記憶體池(Non-Paged Pool)

不分頁記憶體池的區域直接跟在PFN Database後面。不分頁記憶體池的起始位址儲存在nt!MmNonPagedPoolStart中。當調用MiVaNonPagedPool()時,MiObtainSystemVa()就會在這片區域配置設定記憶體。記憶體的配置設定方式由變量nt!MiNonPagePoolVaBitmap按位決定。

硬體抽象層和加載映射區(HAL and Loader Mappings)

核心全局變量nt!MiLowHalVa包含這片區域的起始位址,即0xFFFFFFFFFFC00000。結束位址和X64核心虛拟位址空間結束位址一緻,為0xFFFFFFFFFFFFFFFF。這片區域僅用于系統啟動時,也就是在MmInitSystem()函數中,這片區域中的記憶體在啟動初始化完畢以後就不可以再被使用了。

在系統初始化函數MmInitSystem()的結尾處調用函數MiAddHalIoMappings()來掃描這片虛拟位址空間來判斷是否有I/O映射到了這片空間,如果有,将會調用函數MiInsertIoSpaceMap()加入到由系統維護的I/O隊列中。而對于每一個I/O映射區域,MiInsertIoSpaceMap()都會用池标簽”Io space mapping trackers“建立一個tracker項,然後将其加入到頭為nt!MmIoHeader的雙向連結清單中,其中的每一項都表示的虛拟記憶體塊都已經映射了實體位址,而tracker項中的一些字段也包含關于實體記憶體和虛拟位址映射的資訊。

會話空間  (Session Space)

關于會話(session)的資料結構,會話池和會話映像都會加載到這片區域。

會話映像包括驅動映像比如Win32k.sys(Windows Manager),CDD.dll(Canonical Display Driver),TSDDD.dll(Frame Buffer Display Driver),DXG.sys(DirectX Graphics Driver)等等。

對于任意一個程序,其EPROCESS->Session指向的MM_SESSION_SPACE就是其所屬的會話結構,而會話池的範圍由MM_SESSION_SPACE->PagesPoolStart 和MM_SESSION_SPACE->PagesPoolEnd指定。

系統PTE (Sys PTEs)

這片區域包括映射的View,MDL,adapter記憶體,驅動程式的映像和核心棧。當使用MiVaSystemPtes()時,就會調用函數MiObtainSystemVa()在這片區域配置設定記憶體。

核心動态虛拟空間(Dynamic Kernel VA Space)

這片區域由系統cache的view,特定的分頁記憶體池和特定不分頁記憶體池組成。nt!MiSystemAvailableVa儲存動态核心虛拟空間可用的2MB的區域數量。

調用MiObtainSystemVa()的參數是MiVaSystemCache,MiVaSpecialPoolPaged或MiVaSpecialPoolNonpaged時,将會從這片區域配置設定記憶體。

0x03  核心虛拟記憶體的配置設定

記憶體管理器使用函數MiObtainSystemVa()來動态的從不同的核心虛拟位址空間配置設定不同的2MB的記憶體。當調用MiObtainSystemVa()函數時,調用者需要指定配置設定的PDE項的總數和系統虛拟記憶體的配置設定類型(nt!_MI_SYSTEM_VA_TYPE),而對于此函數有效的類型為MiVaPagedPool,MiVaNonPagedPool,MiVaSystemPtes,MiVaSystemCache,MiVaSpecialPoolPaged,MiVaSpecialPoolNonPaged 。

MiObtainSystemVa()可以滿足不同的核心虛拟空間的配置設定請求。例如,MiVaPagedPool要求配置設定分頁池區域(Paged Pool region),MiVaNonPagedPool要求配置設定不分頁池區域(non-paged pool region),MiVaSystemPtes則配置設定系統PTE區域(System PTE region),而其他類型的配置設定請求則是直接配置設定系統動态虛拟記憶體(Dynamic System VA region)。記憶體的釋放則是由函數MiReturnSystemVa()完成。

一個動态記憶體配置設定的例子就是MiExpandSystemCache()調用MiObtainSystemVa()來擷取系統cache的view。MiExpandSystemCache()調用MiObtainSystemVa(MiVaSystemCache)來申請存放Cache Manager VACB(Virtual Address Control Block)資料結構的虛拟記憶體

0x04 系統PTE管理 (SysPTE Management)

由MiObtainSystemVa()從SysPTE區域配置設定的記憶體會由MiReservePtes()按照配置設定要求(nt!MiKernelStackPteInfo和nt!MiSystemPteInfo)進一步的劃分為兩類,其目的就是為了防止虛拟記憶體的碎片化。因為核心棧記憶體(尤其是system和服務程序的線程)生命期是很長的,而其他的類型配置設定,例如MDL的生命周期相對短很多。

兩種結構類型nt!MiKernelStackPteInfo和nt!MiSystempteInfo都是屬于nt!_MI_SYSTEM_PTE_TYPE,而這些結構體都是由函數MiInitializeSystemPtes()産生,他們的每一位包含的資訊可以影響SysPTE區域的128GB的空間。而函數MiReservePtes()在被調用時需要這些結構體其中一個作為參數來申請SysPTE區域以外的記憶體,申請的記憶體由MiReleasePtes()進行釋放。

當虛拟記憶體位址被nt!MiKernelStackPteInfo和nt!MiSystemPteInfo覆寫時,則已經耗盡了通過調用MiExpandPtes()(實際調用MiObtainSystemVa(MiVaSystemPtes))擴充的記憶體區域。

函數MmAllocateMappingAddress()和MmCreateKernelStack()都是申請nt!MiKernelStackPteInfo類型的記憶體,而函數MiVaildateLamgePfn()和MiCreateImageFileMap(),MiRelocateImagePfn(),MiRelocateImageAgain()申請nt!MiSystemPteInfo類型的記憶體。

jpg改rar

繼續閱讀