深入理解Windows頁幀號(一)
Windows頁幀號(Page Frame NuMer ,PFN)介紹
Windows以及幾乎所有操作系統都會使用PFN資料庫 ( PFN DataBase ) ,以便跟蹤虛擬分配的頁面,進一步管理哪些頁面要被寫入或移出,哪些頁面需要緩存頁面等。
虛擬分配頁面的管理過程都是通過一個名為頁幀號(PFN)的列表進行管理的,關於每個物理和虛擬分配頁面的狀態及其相應屬性都有一長串解釋。
本文,我將通過大量實際的案例,來解釋Windows PFN的實現。
如果你熟悉non-PAE模式和PAE模式系統,那麼就應該注意到,在non-PAE模式下,每個PFN結構需要24個位元組,而在PAE模式系統中,需要25個位元組,因此如果你的頁面是4096個位元組,那麼就分配大約24個位元組以保持對每個頁面的追蹤。
在non-PAE模式下,每個物理頁大小是4K,對於每一個物理頁,PFN資料庫會使用一個24位元組長的結構來保存它的相關信息,比如該物理頁是否已經被使用,這是170:1的比率。而在PAE模式下,每個物理頁大小是4K,對於每一個物理頁,PFN資料庫會使用一個28位元組長的結構來保存它的相關信息,這是146:1的比率。這意味著PFN資料庫中,每1G大小的物理內存,都需要大約6M或7M來描述其中的信息。但是如果你有一個具有16G物理內存的32位系統,那麼它只需要大約112M的2G內核虛擬地址空間來處理隨機存取存儲器(RAM)。這也是為什麼擁有16G或更大物理內存的系統不允許使用3G模式(也稱為expaneuserva)的原因,因為該模式會將用戶虛擬地址空間增加到3G並將32位系統的內核虛擬地址空間減少到1G,擴展頁面的好處之一(例如每頁2M)需要更少的MMPFN結構。
在開始深入了解PFN之前,請記住術語「Page」主要用於操作系統級概念,而「Frame」用於CPU級概念,因此「Page」表示虛擬頁面,「Page Frame」表示物理頁面。
PFN列表
PFN由描述某些頁面狀態的列表組成,比如活動列表(Active List)顯示活動頁面(例如,在working sets中);待機列表(Standby List)表示先前在磁碟中備份的列表,頁面本身可以在不產生磁碟IO的情況下清空和重用;修改列表(Modified List )顯示該頁面先前已被修改,並且必須以某種方式寫入磁碟;移出列表(Freed List),顧名思義,它顯示不再需要維護的頁面,可以被移出;最後是零列表(Zero List),描述一個包含全零(0)的頁面。
下圖顯示了PFN資料庫列表之間的相互關聯關係:
這些列表是用於管理內存中「頁面錯誤」狀態的,每次發生「頁面錯誤」後,Windows都試圖找到一個可用的表單頁,如果列表為空則是零列表,然後零列表則從移出列表中獲取可用的表單頁,另外,如果移出列表也為空,則移出列表從待機列表獲取可用的表單頁,並將頁面用全零(0)表示。
零頁面線程
在Windows中,有一個優先順序為0的線程,負責在系統空閑時將內存歸零,並且是整個系統中唯一優先順序為0的線程。這是可用的最低優先順序,因為用戶線程至少是1,該線程儘可能清除移出列表。此外,Windows中有一個名為RtlSecureZeroMemory()的函數,它可以安全地移出一個位置,但是在以下的內核透視圖中,nt!KeZeroPages負責移出頁面。
下圖顯示了零頁面線程的整個過程:
此時,零線程(Zero Thread)就可以看得清清楚楚。知道它來自系統進程,且它的優先順序為0,這應該就足夠了,不再需要更多的信息了。首先嘗試查找系統的nt!_eprocess:
!process 0 System
現在我們可以看到系統的線程,我的目標線程(零線程)的細節如下:
THREAD ffffd4056ed00040 Cid 0004.0040 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (WrFreePage) KernelMode Non-Alertable
fffff8034637f148 NotificationEvent
fffff80346380480 NotificationEvent
Not impersonating
DeviceMap ffff99832ae1b010
Owning Process ffffd4056ec56040 Image: System
Attached Process N/A Image: N/A
Wait Start TickCount 4910 Ticks: 4 (0:00:00:00.062)
Context Switch Count 21023 IdealProcessor: 3
UserTime 00:00:00.000
KernelTime 00:00:01.109
Win32 Start Address nt!MiZeroPageThread (0xfffff80346144ed0)
Stack Init ffffe700b7c14c90 Current ffffe700b7c14570
Base ffffe700b7c15000 Limit ffffe700b7c0f000 Call 0000000000000000
Priority 0 BasePriority 0 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr Call Site
ffffe700`b7c145b0 fffff803`46016f8a nt!KiSwapContext+0x76
ffffe700`b7c146f0 fffff803`46016951 nt!KiSwapThread+0x16a
ffffe700`b7c147a0 fffff803`46014ba7 nt!KiCommitThreadWait+0x101
ffffe700`b7c14840 fffff803`461450b7 nt!KeWaitForMultipleObjects+0x217
ffffe700`b7c14920 fffff803`460bba37 nt!MiZeroPageThread+0x1e7
ffffe700`b7c14c10 fffff803`46173456 nt!PspSystemThreadStartup+0x47
ffffe700`b7c14c60 00000000`00000000 nt!KiStartSystemThread+0x16
正如你所看到的,它的起始地址是nt!MiZeroPageThread,且它的優先順序是0,如果你看到調用棧,那麼就可以看到之前調用的 nt!MiZeroPageThread。
更多細節,請訪問《隱藏的內存分配成本》一文。
PFN的Windows結構是nt!_MMPFN,如下圖所示:
如你所見,_MMPFN需要28個位元組。
PFN記錄會根據其物理地址順序存儲在存儲器中,這意味著你可以在PFN的幫助下計算出物理地址。
Physical Address = PFN * page size(e.g 4096 Byte) + offset
PFN資料庫的地址位於nt!MmPfnDatabase,你可以使用以下所示的代碼段在Windbg中獲取PFN資料庫地址。
2: kd> x nt!MmPfnDatabase
fffff800`a2a76048 nt!MmPfnDatabase =
!memusage
windbg中另一個非常有用的命令是!memusage,這個命令幾乎提供了內存布局中關於PFN和頁面的幾乎所有內容及其相關細節,例如文件、字體、系統驅動程序、DLL模塊、可執行文件(包括它們的名稱和它們的分頁位修改) 。
!memusage命令從物理內存角度顯示內存統計信息,無數個頁面信息將被列印出來,可以說是「最內存」的信息。此命令會查看所有的頁幀,所以運行時會非常的耗時。
該命令的簡要介紹如下所示:
2: kd> !memusage
loading PFN database
loading (100% complete)
Compiling memory usage data (99% Complete).
Zeroed: 9841 ( 39364 kb)
Free: 113298 ( 453192 kb)
Standby: 105520 ( 422080 kb)
Modified: 7923 ( 31692 kb)
ModifiedNoWrite: 0 ( 0 kb)
Active/Valid: 286963 ( 1147852 kb)
Transition: 45 ( 180 kb)
SLIST/Bad: 567 ( 2268 kb)
Unknown: 0 ( 0 kb)
TOTAL: 524157 ( 2096628 kb)
Dangling Yes Commit: 140 ( 560 kb)
Dangling No Commit: 37589 ( 150356 kb)
Building kernel map
Finished building kernel map
(Master1 0 for 80)
(Master1 0 for 580)
(Master1 0 for 800)
(Master1 0 for 980)
Scanning PFN database - (97% complete)
(Master1 0 for 7d100)
Scanning PFN database - (100% complete)
Usage Summary (in Kb):
Control Valid Standby Dirty Shared Locked PageTables name
ffffffffd 11288 0 0 0 11288 0 AWE
ffffd4056ec4c460 0 112 0 0 0 0 mapped_file( LeelUIsl.ttf )
ffffd4056ec4c8f0 0 160 0 0 0 0 mapped_file( malgun.ttf )
ffffd4056ec4d6b0 0 108 0 0 0 0 mapped_file( framd.ttf )
.....
ffffd4057034ecd0 328 148 0 0 0 0 mapped_file( usbport.sys )
ffffd4057034f0e0 48 28 0 0 0 0 mapped_file( mouclass.sys )
ffffd4057034f7d0 32 28 0 0 0 0 mapped_file( serenum.sys )
ffffd405703521a0 0 20 0 0 0 0 mapped_file( swenum.sys )
.....
-------- 0 20 0 ----- ----- 0 session 0 0
-------- 4 0 0 ----- ----- 0 session 0 ffffe700b8b45000
-------- 4 0 0 ----- ----- 0 session 1 ffffe700b8ead000
-------- 32520 0 84 ----- ----- 1324 process ( System ) ffffd4056ec56040
-------- 2676 0 0 ----- ----- 304 process ( msdtc.exe ) ffffd405717567c0
-------- 4444 0 0 ----- ----- 368 process ( WmiPrvSE.exe ) ffffd405718057c0
-------- 37756 0 60 ----- ----- 1028 process ( SearchUI.exe ) ffffd405718e87c0
.....
-------- 8 0 0 ----- 0 ----- driver ( condrv.sys )
-------- 8 0 0 ----- 0 ----- driver ( WdNisDrv.sys )
-------- 52 0 0 ----- 0 ----- driver ( peauth.sys )
-------- 24744 0 0 ----- 0 ----- ( PFN Database )
Summary 1147852 422260 31692 129996 204428 25156 Total
.....
b45b 64 0 0 60 0 0 Page File Section
b56b 4 0 0 4 0 0 Page File Section
b7ec 84 0 0 64 0 0 Page File Section
b905 12 0 0 0 0 0 Page File Section
bf5c 4 0 0 0 0 0 Page File Section
.....
有關這些頁面的更多信息,Windbg幫助文檔是這樣解釋的:
你可以使用!vm擴展命令分析虛擬內存使用情況,這個擴展通常比!memusage更有用。有關內存管理的更多信息,請參閱Mark Russinovich和David Solomon撰寫的Microsoft Windows Internals。!pfn擴展命令可用於在PFN資料庫中顯示特定頁幀條目。
在下一節,我會介紹如何更詳細的查看這些命令。
※通過電子郵件遠程獲取NTLM哈希
※狩獵俄羅斯APT28——對其一系列新的惡意軟體分析
TAG:嘶吼RoarTalk |