Windows內存管理分析(三)

Windows內存管理分析(三)

三、虛擬內存的映射

虛擬內存的映射使用MmCreateVirtualMapping函數 參數是進程,虛擬地址,保護類型,頁面編號,需要映射的頁面數量。

Windows內存管理分析(三)

首先判斷物理頁面是否可以被映射,通過後使用MmCreateVirtualMappingUnsafe完成真正的映射。

Windows內存管理分析(三)

首先是安全性的檢查,如果給定進程空指針,但是映射的地址卻低於MmSystemRangeStart的話就返回嚴重錯誤,如果是用戶進程想映射內核空間,同樣返回嚴重錯誤,頁面數量映射過多也是返回錯誤

Windows內存管理分析(三)

根據參數來設置安全屬性

Windows內存管理分析(三)

Windows內存管理分析(三)

這個FOR循環遍歷所有頁面完成映射,首先是判斷頁面是否存在,否則返回嚴重錯誤。然後獲取上一個映射的頁面和本次映射的頁表項的偏移是否相同,如果相同就加1到下一個頁表項,代表著當前頁面已經在上一次循環中被映射,如果不同代表是2個不同的頁表項,取出對應的頁面項內容PT,用InterlockedExchangePte交換2個屬性,完成頁表項的設置,也就是完成了這一頁的映射。然後刷新TLB

細心的讀者可能會想到問題,我們說開啟保護模式後,訪問的內存都是虛擬內存,這些虛擬地址必須要進行MMU的地址轉譯,但是進程的KPROCESS結構裡面頁表基址記錄的卻是真實物理頁表的地址,這時候我們將新的頁表項存進這個真實物理頁表地址的時候,MMU是將它當成虛擬地址來訪問,這時候不就起衝突了嗎???這裡就可以引出超空間(Hyperspace)的使用,超空間是為了臨時訪問某一個物理頁面進行臨時的映射,實際上我們看源代碼的時候它並沒有直接訪問這個真實物理地址,而是通過超空間進行的訪問,實際上也就是訪問虛擬地址,然而這個虛擬地址指向了這個頁表。具體的代碼就在MmCreateVirtualMappingUnsafe中MmGetPageTableForProcess和MmUnmapPageTable的使用,MmGetPageTableForProcess臨時將物理頁面映射,然後返回具體PT,MmUnmapPageTable取消這個超空間的映射過程。關於超空間將於下一節分析。

我們再來分析一個解除映射的過程,有了前面映射的過程,解除估計就很好理解。

Windows內存管理分析(三)

Windows內存管理分析(三)

Windows內存管理分析(三)

這裡容易理解,是映射的完全逆過程,不再敘述

四、Hyperspace

Hyperspace前文提到是做物理頁面的臨時映射,然後進行使用。用處例如就在上文提到的頁表的基址實際上是真正的物理地址,不能直接訪問,要先映射後再進行訪問。創建超空間映射的函數是MmCreateHyperspaceMapping,返回值為虛擬地址的起始地址

Windows內存管理分析(三)

此函數是一個接口函數,真正實現映射的為MiMapPageInHyperSpace

Windows內存管理分析(三)

Windows內存管理分析(三)

MmFirstReservedMappingPte是系統保留的當前可用PTE指針,類似棧的原理,是從高地址往低地址,並且當前記錄是沒有使用的,個數一共有256個,也就是支持1M。

Windows內存管理分析(三)

系統初始化的時候會把MmFirstReservedMappingPte設置成Hyperspace的基地址對應的PTE項

我們看代碼首先取出了對應的PFN項,如果PFN項指向的物理頁號為0,就應該為MI_HYPERSPACE_PTES(宏定義是255)代表超空間的最高頁面,然後對MmFirstReservedMappingPte記錄的物理頁號進行了減一處理。然後將這個物理頁號和臨時的內存屬性結合成PTE,然後寫入頁表,完成映射。

再看刪除映射

Windows內存管理分析(三)

同樣的也是個接口,真正實現的為MiUnmapPageInHyperSpace

Windows內存管理分析(三)

實現的原理非常簡單,取出地址對應頁表項,設置為0,就取消了映射、

五、系統空間映射

我們知道每個進程擁有4GB空間,分為用戶空間和內核空間,一般情況下低2G進程擁有,高2G內核擁有,實際上內核空間所有進程共享,除了Hyperspace和PAGETABLE_MAP。Hyperspace不支持共享的原因就是因為它是特定進程的,例如前文提到頁表臨時映射時,必須是基於某個進程進行的映射,所以是不支持所有進程共享。PAGETABLE_MAP頁表也不支持共享,因為同樣每個進程的頁表都是不同的。

接下來分析PspCreateProcess關於內存部分的代碼

Windows內存管理分析(三)

再創建進程空間的時候,如果有父進程,調用MmCreateProcessAddressSpace函數

分析一下這個函數

Windows內存管理分析(三)

不討論PAE的情況,則會申請2頁的物理頁面

Windows內存管理分析(三)

接下來創建一個超空間,將內核空間的頁表MmGlobalKernelPageDirectory進行全部的拷貝,但是內核空間只有2種內存需要修改,第一個就是頁表,第二個就是超空間,設置EProcess的DirectoryTableBase結構體第一項為頁表的物理地址,第二項為超空間的物理地址。這樣就完成了進程虛擬地址的創建

如果這個進程沒有父進程,就認為是idle進程

Windows內存管理分析(三)

那麼就和idle共享所有虛擬內存

MmGlobalKernelPageDirectory內核頁表會在系統初始化的時候會由MmInitGlobalKernelPageDirectory進行初始化

Windows內存管理分析(三)

主要是通過複製頁表(虛擬地址)而來

Windows內存管理分析(三)

我們來分析NtAllocateVirtualMemory的實現

我們略過前面的參數檢查,直接分析核心代碼

Windows內存管理分析(三)

這段代碼獲取目標進程結構體,如果不是當前進程是通過句柄來引用,然後Attach到目標進程中

Windows內存管理分析(三)

創建一個VAD描述符然後插入到進程的VadRoot中,表明申請了內存

Windows內存管理分析(三)

如果之前是Attach的就可以Detach目標進程了

最核心的就是這些代碼,接下來同樣是進行一系列檢查。

六、共享映射區Section

共享映射區指的是不同進程共享同一段物理內存的情景,功能之一是可以進行進程間的通信,第二個功能因為這個物理頁面可以被倒換到磁盤中,進行Section的操作相當於間接的進行了文件IO,很是方便。

創建Section的方法Ring3下可以使用CreateFileMapping,實際上也是調用NtCreateSection,NtCreateSection會通過MmCreateSection創建Section。

來分析MmCreateSection

如果是要映射文件的情況

Windows內存管理分析(三)

首先製作內存屬性掩碼

Windows內存管理分析(三)

Windows內存管理分析(三)

這段代碼獲取具體的文件對象,如果沒有提供文件句柄或者對象,然而卻勾選了SEC_IMAGE屬性就返回錯誤

Windows內存管理分析(三)

Windows內存管理分析(三)

這裡進行的讀寫文件主要是註釋說明主要是建立緩衝機制

Windows內存管理分析(三)

Windows內存管理分析(三)

然後判斷參數需要的類型,調用不同的類型對應的函數來完成Section的創建。映像文件中可能有很多段,比如數據段代碼段,所以是單獨一種情況考慮。如果有文件,如果NEWCC宏進行了定義就執行MmCreateDataFileSection,否則是MmCreateCacheSection。如果沒指定文件就用換頁的文件作為目標文件。

我們分析這些個函數的實現,先分析MmCreateDataFileSection

Windows內存管理分析(三)

首先創建了這個對象,對象類型為

Windows內存管理分析(三)

FileObject執行對應的文件對象,下面的聯合體因為要麼就是映像Image Section,要麼就是認為是Segment Section

Windows內存管理分析(三)

這些就是一個Section 區的各項屬性,例如是否支持寫時複製,區的保護類型,對應文件的FileOffset,特徵字,這些段還用鏈表串在了一起。PageTable是頁表,其類型為

Windows內存管理分析(三)

通過TableRoot結點可以看出實際上是一顆伸展樹(這個優化對程序來說非常好,因為程序有局部性訪問的原理,所以伸展樹某段時間伸展完畢後,應該可以保證一段時間不進行變形,如果穩定的話甚至時間複雜度可以保持在O(1),並且對空間的節省相對於數組來說也是很不錯的)。PageTable實際上是二級的結構,通過文件的偏移來找到第二級。第二級的結構是一個CACHE_SECTION_PAGE_TABLE

Windows內存管理分析(三)

文件的偏移的對齊值為CACHE_SECTION_PAGE_TABLE裡面的頁表項數組PageEntries的個數ENTRIES_PER_ELEMENT乘以頁面大小,這個宏一般為256。同時,FileOffset還記錄了它對應這個頁面具體位移,Segment執行了它是哪個區,RefCount為引用計數。

返回代碼

Windows內存管理分析(三)

接下來對這個新申請的Section對象進行初始化

Windows內存管理分析(三)

查詢文件信息

Windows內存管理分析(三)

如果指定Section最大長度就用這個長度,否則就用文件的長度

Windows內存管理分析(三)

如果文件的大小沒有指定的Section大小大,就對文件進行擴充。鎖住文件

Windows內存管理分析(三)

Windows內存管理分析(三)

如果文件之前沒有進行映射過,申請一個映射區SECTION_SEGMENT,進行初始化,對於Imge.VirtualAddress因為是數據文件所以直接是0,分析MiInitializeSectionPageTable對頁表的初始化。

對於內核GenericTable的使用必須提供比較,申請,釋放函數。這裡伸展樹初始化完畢後為空樹。

Windows內存管理分析(三)

如果文件之前映射過判斷文件之前的映射區的大小,如果沒有指定要求的大就要做擴容處理

Windows內存管理分析(三)

最後完成Section的創建 並返回

分析MmCreateImageSection的結果起始和MmCreateDataFileSection其實大部分相同

Windows內存管理分析(三)

觀察IMAGE_SECTION和普通的SEGMENT_SECTION就可以知道區別大體在哪,一般來說鏡像文件會有多個segment,nrsegments說明了區數量,而Segments就是一個數組,對於每一個段都必須進行初始化賦值,具體為每一個段賦值在ExeFmtpCreateImageSection實現

創建了Section之後,並沒有進行映射,映射通過NtMapViewOfSection來完成,然後會調用MmMapViewOfSection完成映射

由於映像文件和普通文件大體上一樣,區別就是映像要為多個段進行映射,我們先分析普通文件的映射

計算出實際大小Viewsize後

Windows內存管理分析(三)

調用MmMapViewOfSegment進行單一段映射,MmMapViewOfSegment這個函數也可用於映像文件多個段的映射

Windows內存管理分析(三)

建立映射的過程也就只是把創建一個Area,將聯合體Data的Section字段進行設置,這樣就完成了映射。這裡非常奇怪,沒有建立映射到物理頁的過程,那究竟是怎麼樣進行訪問的呢?答案就是在頁面異常的缺頁異常進行處理。而取消映射的過程和映射的過程相反,就是把Area從VadRoot中進行刪除

明天繼續第四部分~


分享到:


相關文章: