x64內核內存空間結構

0x00 前言數據庫

本文主要是討論Windows 7 x64下的內核虛擬地址空間的結構,能夠利用WiinDBG調試的擴展命令"!CMKD.kvas"來顯示x64下的內核虛擬地址空間的總體佈局。瞭解內核的地址佈局在某些狀況下是頗有的,好比說在研究New Blue Pill的源碼和虛擬化的時候。數組

0x01 基本結構session

X64的CPU的地址爲64位,但實際上只支持48位的虛擬地址空間供軟件使用。虛擬地址的高16位在用戶模式下老是被設置爲0000,而在內核模式下全置爲FFFF。數據結構

所以用戶模式的地址空間範圍爲0x00000000~00000000——0x0000FFFF~ffffffff,內核模式的地址空間範圍爲0xFFFF0000~00000000——0xFFFFffff~ffffffff,因此對操做系統可見的內核虛擬地址空間的大小爲256TB。Windows操做系統將整個內核地址空間劃分爲若干個有特定用途的大小固定的虛擬地址空間。下表是關於Windows對於虛擬地址空間具體的劃分:app

       起始地址函數

                結束地址佈局

內存大小spa

         用途操作系統

FFFF0800`00000000線程

         FFFFF67F`FFFFFFFF

238TB

未使用

FFFFF680`00000000

         FFFFF6FF`FFFFFFFF

512GB

PTE內存空間

FFFFF700`00000000

         FFFFF77F`FFFFFFFF

512GB

Hyper內存空間

FFFFF780`00000000

         FFFFF780`00000FFF

4KB

系統共享空間

FFFFF780`00001000

         FFFFF7FF`FFFFFFFF

512GB-4K

系統cache工做集

FFFFF800`00000000

         FFFFF87F`FFFFFFFF

512GB

初始化映射區

FFFFF880`00000000 

         FFFFF89F`FFFFFFFF

128GB

系統PTE區域

FFFFF8a0`00000000

         FFFFF8bF`FFFFFFFF

128GB

分頁池區域

FFFFF900`00000000

          FFFFF97F`FFFFFFFF

512GB

會話空間

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項中的一些字段也包含關於物理內存和虛擬地址映射的信息。

struct _IO_SPACE_MAPPING_TRACKER {
    LIST_ENTRY Link;
    PHYSICAL_ADDRESS  Pfn;
    ULONGLONG  Pages;
    PVOID Va;
    . . . 
}

 

 

會話空間  (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類型的內存。

相關文章
相關標籤/搜索