Windows記憶體管理分析(一)

Editor發表於2018-03-21

                   

筆記參考毛德操先生所著《Windows核心情景分析》,使用程式碼為ReactOS 0.4.7,相比原著更新了一些資料結構幫助理解新的資料結構。

本文基於IA-32架構,假定讀者已經瞭解IA-32架構下的MMU(具體請閱讀Intel 手冊)如何工作以及擁有良好的資料結構基礎


一、虛擬記憶體的管理

程式地址空間的資訊 由MMSUPPORT結構體所描述,每個EProcess結構體都會有一個結構為MMSUPPORT的Va成員,描述著這個程式的整個虛擬記憶體的資訊,但是它並不管理這些資訊。結構體成員如下

Windows記憶體管理分析(一)

成員WorkingSetExpansionLinks把所有的MMSUPPORT串聯在一起,連結串列頭位於全域性變數MmWorkingSetExpansionHead。PeakWorkingSetSize為工作集的峰值,PageFaultCount頁面錯誤次數。

而程式一個結構為MM_AVL_TABLE的VadRoot結果管理著所有虛擬記憶體,進行管理的成員主要是BalancedRoot,是平衡樹的根節點

Windows記憶體管理分析(一)

NumberGenericTableElements成員描述了這個結構具體有多少個元素,NodeHint是命中的結點指標,NodeFreeHint是釋放的時候指向的結點,都利於程式設計。

Windows記憶體管理分析(一)

一整塊地址空間,通常應該有許多的區域,這些區域由MEMORY_AREA描述,結構體如下

成員VadNode是MMVAD 虛擬地址描述符,type是AREA的型別,DeleteInProgress是代表被刪除。Sement和ViewOffset將在共享記憶體區進行詳細描述。MMVAD結構體如下

Windows記憶體管理分析(一)

從虛擬地址描述符中可以看出虛擬描述符是一個二叉樹,實際上是一顆平衡樹,所以MEMORY_AREA這個結構體也是一個平衡二叉樹結構,虛擬地址描述符中還描述了這個起始的虛擬頁號和終止頁號(如果只有一頁那麼這兩個值相同)。

此時讀者可能有問題,MM_AVL_TABLE表中BalancedRoot的型別為MMADDRESS_NODE,而AREA的結構體卻和此不同,如何將AREA存入AVL_TABLE表中呢。實際上MMADDRESS_NODE結構體的定義如下

Windows記憶體管理分析(一)

仔細觀察後發現MMADDRESS_NODE正是VAD的一部分,所以包含VAD描述符的結構都能存進這個AVL_TABLE表中。

MEMORY_AREA有一個RegionListHead成員,定義為

Windows記憶體管理分析(一)

即每一個MEMORY_AREA會管理著一系列的REGION,為什麼這樣設計呢,是因為一個AREA裡面的地址可能有很多不同的屬性,但是如果是一個REGION,裡面的虛擬地址就會保持同一個頁面屬性。

這裡介紹一下程式工作集的資料結構,我們知道程式結構體EPROCESS有一個型別為MMSUPPORT的Vm成員描述著虛擬記憶體,它的一個型別為PMMWSL 的VmWorkingSetList描述了工作集

Windows記憶體管理分析(一)

FirstFree是第一個空閒項,FirstDynamic是第一個可以修剪的頁面,LastEntry是最後一項,wsle是一個陣列,每個陣列的元素描述著一個有效的頁面,UsedPageTableEntries陣列成員,元素代表著這個頁面被引用了多少次,這是一個陣列,為什麼是768個呢,我們假設一個頁面是4K,乘以768就是3GB。我們知道一個程式可以是2GB,但是通過設定可以最多支援3GB,所以這也是一開始就設計好的內容。

接下來看一些函式實現來加深理解

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

MmLocateMemoryAreaByAddress函式引數是一個結構體MMSUPPORT地址空間的描述,第二個引數是一個地址,功能是返回第二個引數給定地址的AREA指標

首先是獲取這個地址空間所屬的程式,由於第一個引數賦值的時候常常是程式的Va成員,或者是核心的MiRosKernelVadRoot成員,所以如果是程式的話就可以通過Va成員在結構體的偏移來定位到EProcess得到程式結構體。這時候如果獲取到了EProcess就判定是程式,如果為空,就代表是核心的請求(核心的虛擬記憶體空間MMSUPPORT是一個全域性變數MiRosKernelVadRoot,通過這個全域性變數來管理核心所有的虛擬空間)

接下來分析MiCheckForConflictingNode

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

這個函式引數分別是起始虛擬頁號,終止頁號,MM_AVL_TABLE地址,輸出為MMADDRESS_NODE節點,簡單來說就是給定一個範圍的虛擬地址(起始虛擬頁號和終止虛擬頁號給定)在MM_AVL_TABLE中找出有這個地址的節點,那麼就有三種情況了,一種是完全重合,一種是部分重合,最後就是整個區域中都沒有這個地址。我們來看看是程式碼是怎麼處理這種情況的。首先是通過搜尋二叉樹的演算法開始計算,我們看一下這段程式碼

Windows記憶體管理分析(一)

Else成立的條件是當這個起始虛擬頁號和終止虛擬頁號完全被包含在這個節點中就會返回。

如果不是完全包含或者是完全不存在,已經是遍歷完畢時

Windows記憶體管理分析(一)

如果說起始頁號地址比這個節點的終止虛擬頁號大,應該插入在NodeOrParent的右邊,否則就是應該插入在左邊。

返回到剛才分析的函式,如果呼叫了此函式返回了TableInsertAsRight、TableInsertAsLeft或者TableEmptyTree的話,就會返回空,代表當前並沒有此地址。如果有的話,就返回這個節點

分析完了AREA尋找函式再看AREA插入函式

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

引數為一個地址空間MMSUPPORT和一個要插入的AREA,首先和查詢一樣,判斷是核心還是程式,如果是程式就插入到程式的VadRoot,如果是核心就插入到核心的MiRosKernelVadRoot,沒什麼好分析的了,剩下的都是資料結構的內容。刪除函式

MiRemoveNode同理。

MmFindGap會從VadRoot或者MiRosKernelVadRoot中遍歷出一個引數能夠支援的間隙並且粒度對齊大小的一塊虛擬地址空間。具體的查詢方法也是資料結構的內容,不予贅述。

接下來我們來分析一個由AREA進行管理的REGION連結串列,我們知道,REGION內所有地址安全屬性一致,如果此時想要更改REGION內部部分的安全屬性就必須進行切割。MmSplitRegion負責進行這樣的工作

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

這段程式碼引數要求為InitialRegion是給定要改變的REGION,InitialBaseAddress是REGION的基地址(因為REGION結構中並沒有儲存基地址的成員),要改變屬性的起始地址StartAddress,改變的長度Length,新REGION的型別NewType,新保護屬性NewProtect,MMSUPPORT結構和AlterFunc函式指標,AlterFunc函式是為了讓實際地址安全屬性發生真的改變,是體系相關的內容,改變了REGION屬性後就會回撥這個函式對體系的記憶體屬性進行修改。分離出2個REGION後這2個再進行連線。MmFindRegion函式對這個連結串列進行引數RegionListHead(一般是AREA的RegionListHead設定)遍歷,具體的程式碼就不用說了,資料結構相關知識。

學習完AREA和REGION我們來看一下MmCreateMemoryArea函式,引數依次是PMMSUPPORT AddressSpace、ULONG Type,PVOID *BaseAddress,  ULONG_PTR Length, ULONG Protect,  PMEMORY_AREA *Result, ULONG AllocationFlags, ULONG Granularity),意思為在一個地址空間中申請一個BaseAddress為基址的,大小為Length,保護屬性為Protect的一個AREA,結構儲存在Result中

分段來進行閱讀

Windows記憶體管理分析(一)

如果參數列明需要靜態記憶體,就可以到系統預先分配好的MiStaticMemoryAreas結構中直接獲取,優點就是速度快,已經提前安排好,缺點就是容量有限。MiStaticMemoryAreaCount記錄了下一次可用下標。

Windows記憶體管理分析(一)

初始化後,如果沒有給定基址,那麼就用MmFindGap查詢到一個間隙,找到後就設定根據這個間隙來設定AREA的起始Vpn和終止Vpn;

Windows記憶體管理分析(一)

Windows記憶體管理分析(一)

如果給定了基址就要判斷之前有沒有AREA已經在此,如果有的話就返回錯誤。沒有的話就同樣設定AREA的起始VPN和終止VPN,然後插入到地址空間之中。MmInsertMemoryArea已經分析過,不予贅述。

至此,我們已經看到了一個大概的虛擬記憶體管理的模型,首先是程式結構體的Va描述著他們自己的記憶體空間,由程式的VadRoot或者MiRosKernelVadRoot(核心)進行管理,VadRoot是一個平衡二叉樹,裡面存放著由AREA構成的節點,而每個AREA又管理著一個REGION連結串列,REGION裡面的地址空間保證是一個屬性,AREA則無此要求。

當觀察原始碼的時候,我們可以看到申請虛擬記憶體時並沒有對映實體記憶體的程式碼,實際上就是如此,程式的VadRoot管理的一系列虛擬記憶體中不一定對映到實體記憶體,只有在使用的時候才會對映。


本文由看雪論壇 黑色書卷 原創  轉載請註明來自看雪社群

關注看雪學院公眾號:ikanxue

更多幹貨等著你~

相關文章