Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
存儲物理頁屬性的PFN數據庫
node
1、PFN的基礎概念 數據庫
頁幀:即CPU的分頁,常見的是4KB大小的分頁。操做系統分頁即用頁幀爲最小單位。windows
咱們以前在保護模式中所學習到的PTE,其指向的就是頁地址+頁屬性。(這個頁地址是物理地址,惟一的)數組
頁幀編號:4KB,即1000h,每一個頁大小1000h,即說明每一個頁的首地址必須是1000h的整數倍。數據結構
又由於頁幀有惟一的物理地址,將該地址 x/1000h,所產生的一個惟一的編號,該編號被稱爲頁幀編號。函數
PFN數據庫:將每個頁幀的屬性地址提取出來組成一數據結構PFN(Page Frame number),將連續的PFN組織在一塊兒被稱爲頁幀數據庫。學習
這也就是說,咱們只要獲取某頁的物理地址,就能夠得知該頁的頁幀編號,經過頁幀編號能夠到PFN數據庫中搜索到PFN,最後獲得關於該頁的屬性。this
2、PFN的數據結構spa
1. 經過wrk(windows search kernel)咱們來搜索其PFN的數據結構
以下圖,這個數據結構存在不少union的聯合體。由於頁幀有不少不一樣的屬性(好比活動頁/釋放頁等,後面會介紹),因此纔有這麼不一樣的union成員。
1 typedef struct _MMPFN { 2 union { 3 PFN_NUMBER Flink; 4 WSLE_NUMBER WsIndex; 5 PKEVENT Event; 6 NTSTATUS ReadStatus; 7 8 // 9 // Note: NextStackPfn is actually used as SLIST_ENTRY, however 10 // because of its alignment characteristics, using that type would 11 // unnecessarily add padding to this structure. 12 // 13 14 SINGLE_LIST_ENTRY NextStackPfn; 15 } u1; 16 PMMPTE PteAddress; 17 union { 18 PFN_NUMBER Blink; 19 20 // 21 // ShareCount transitions are protected by the PFN lock. 22 // 23 24 ULONG_PTR ShareCount; 25 } u2; 26 union { 27 28 // 29 // ReferenceCount transitions are generally done with InterlockedXxxPfn 30 // sequences, and only the 0->1 and 1->0 transitions are protected 31 // by the PFN lock. Note that a *VERY* intricate synchronization 32 // scheme is being used to maximize scalability. 33 // 34 35 struct { 36 USHORT ReferenceCount; 37 MMPFNENTRY e1; 38 }; 39 struct { 40 USHORT ReferenceCount; 41 USHORT ShortFlags; 42 } e2; 43 } u3; 44 #if defined (_WIN64) 45 ULONG UsedPageTableEntries; 46 #endif 47 union { 48 MMPTE OriginalPte; 49 LONG AweReferenceCount; 50 }; 51 union { 52 ULONG_PTR EntireFrame; 53 struct { 54 #if defined (_WIN64) 55 ULONG_PTR PteFrame: 57; 56 #else 57 ULONG_PTR PteFrame: 25; 58 #endif 59 ULONG_PTR InPageError : 1; 60 ULONG_PTR VerifierAllocation : 1; 61 ULONG_PTR AweAllocation : 1; 62 ULONG_PTR Priority : MI_PFN_PRIORITY_BITS; 63 ULONG_PTR MustBeCached : 1; 64 }; 65 } u4; 66 67 } MMPFN,
2. PFN分類
一個物理頁面可能有這幾種狀態:活動狀態、備用狀態、已修改狀態、已修改但不寫出、轉移狀態、空閒狀態、零化狀態、壞狀態。(由於篇幅限制,就先不介紹這些狀態,之後有機會會補充)
所以,PFN就具備幾種不一樣的種類,下面介紹一下。
1)活動頁面的PFN數據結構
1> PFN中的PteAddress指向PTE所在的地址,ShareCount表示共享次數。只要ShareCount大於零,則就不能從內存中移除。
2> ReferenceCount表示其被引用的次數,好比鎖等就是經過參考這個值來決定的。
3> u3中的e1(MMPFNENTNTRY) 表示物理頁的狀態,其是一個共享屬性。(由於物理頁被分紅幾類,而相同類都具備相同的屬性,所以這個結構成員共享)
a. 這裏面表示了一些物理頁狀態,好比其是否被修改,正在讀或者正在寫。
b. 重要的成員PageLocation(3位能夠表示8種狀態),其表明了當前頁面狀態類型,也指明瞭該頁面在系統定義的哪一個鏈表中。
c. MMPFN包含了指向此頁面的PTE的原始內容,多是一個原型PTE。
其用意是將一個物理頁面分配給一個PTE,它記錄頁原來的PTE;此後當該物理頁面不爲它所用時,能夠恢復原來的PTE
4. 最後一個成員U4指向了該頁面的PTE所在的頁表中的物理頁幀編號,以及一些標誌位。
2)備用狀態或者已修改狀態的PFN數據結構
1> 對於這種狀態的物理頁面,雖然再也不有有效的PTE指向它,它頁再也不屬於任何一個工做集,但它們就加入到系統的備用鏈表或修改鏈表中。
因此,MMPFN的第一個或者第三個4字節成員直接變爲它再鏈表中的先後指針。
2>已修改但不寫出狀態的頁面也是用一樣的數據結構。
3)正在轉移狀態的PFN數據結構
a. 對於正在轉移狀態的,第一項要麼指向一個同步時間,要麼指向一個錯誤碼。
b. 若是I/0正在繼續進程中,則該事件會被通知到;若是在頁面換入過程當中產生錯誤,則ReadStatus包含了表明此I/O錯誤的狀態碼。
c.轉移狀態PFN項的用途是識別或消除衝突的頁面錯誤。
3.物理頁面的鏈表組織結構
咱們以前瞭解過PFNDatabse,裏面一個個PFN排列在一塊兒,有不一樣種類。
可是這樣存在一個弊端,搜索相同種類的PFN效率很頂不高,所以引出了其鏈表組織結構
MMLIST與MMPFNLIST數據類型
1) MMLISTS是枚舉成員,其值表明物理頁的屬性,MMPFN數據結構的u3.e1.PageLocation域保存的就是MMLIST的某個枚舉類型。
好比 PageLocatio爲001,對應的物理頁就是FreePageList類型的物理頁面。
2)全局數組 mmPageLocationList將這些鏈表按照MMLISTS枚舉值組織在一塊兒。
a. 即開闢一個佔八個元素的數據,每一個成員都是PMMPFINLIST,表示一個鏈表頭部。
b. 咱們能夠查看以前的MMPFN數據結構(Free類型),其u1表明前驅,u2表明後驅。
3)PFN數據庫組織結構圖
a.活動頁面或轉移狀態的頁面沒有造成鏈表。
b. 零化頁面或者空閒頁面僅構成單鏈表。
c. 其他的爲雙循環鏈表。
4. 物理頁面的狀態變化
內存管理器根據內存的數量以及各個進程對於內存的需求,動態地調度這些物理頁面地使用。
好比下列狀況:
a. 當一個進程須要內存時,內存管理器能夠從零化鏈表或者空閒鏈表中找到一個或多個頁面來知足進程地需求。
b. 當進程退出時,內存管理器回收該進程地頁面。
c. 當內存物理緊缺時,內存管理器按照特定地策略將有些進程中地頁面換出到外存中,從而騰出物理內存以做它用。
3、經過windbg實戰遍歷PFN鏈表(以FreePageList爲例)
1. 兩個全局變量 PFNDataBase 與 nt!MmZeroedPageListHead
首先,咱們須要獲取兩個存儲在內存中的全局變量,一個是指向 PFNDataBase 數據庫起始位置的指針,另外一個是 FreePageList 鏈表。
若是不能記住名字,能夠採用 x指令+通配符 模糊搜索的辦法
1)PFNDataBase (其是一個指針,因此真正地址是其指向的首個地址)
kd> dd nt!MmPfnDatabase
80560b68 80c36000 0000ff00 00000000 0000000f
2 )nt!MmZeroedPageListHead
kd> dd nt!MmZeroedPageListHead
805523e0 0001111b 00000000 0001808d 00013a98
根據MMPFNLIST數據結構,對應結果爲(內容與圖片更改,但屬性對應)
2. 獲取PFN數據結構的大小
由於在PFNDATABASE數據庫中這些數據連續排列,咱們要知道單個數據大小從而有效拆分。
kd> dt _MMPFN -v
nt!_MMPFN
struct _MMPFN, 6 elements, 0x18 bytes
······
其總共佔18個字節。
3. 經過公式查看鏈表
第一個地址*MMPfnDataBase+(nt!MmFreePageListHead.{Flink/Blink} * 0x18c) 獲取搜尋的
以後的地址*MMPfnDataBase+(MMPFN.{u1(Flink)/u2(Blink)}*0x18c)
kd> dd 80c36000+0001808d*0x18 l6
80e76d38 00018093 00060234 ffffffff 00003000
80e76d48 0001809d 00000000
能夠看到其由於是首個鏈表,故其Blink ffffffff(不存在)
Flink 00018093
4、經過驅動代碼來遍歷鏈表
這裏全局變量的地址應該採用內存暴力搜索找到,可是技術仍是不太成熟,就先強制定位到地址。
1 #include <ntddk.h> 2 #include <ntstatus.h> 3 #include <wdm.h> 4 5 typedef struct _MMPFNLIST { 6 ULONG Total; 7 ULONG ListName; 8 ULONG Flink; 9 ULONG Blink; 10 }MMPFNLIST, *PMMPFNLIST; 11 12 typedef struct _MMPFN { 13 ULONG u1; 14 PULONG32 PteAddress; 15 ULONG u2; 16 ULONG u3; 17 ULONG OrininalPte; 18 ULONG u4; 19 } MMPFN, *PMMPFN; 20 21 // 查找全局變量 pfn數據庫起始地址 22 // 注意: *MmPfnDataBase 爲真正的起始地址 23 ULONG32 MmPfnDatabase = (ULONG32)0x80c36000; 24 // 查找全局變量 MmFreePageListHead 25 PMMPFNLIST MmFreePageListHead = (PMMPFNLIST)0x805523f0; 26 // 驅動卸載函數 27 NTSTATUS DriverUnload(PDRIVER_OBJECT Driver) { 28 return STATUS_SUCCESS; 29 } 30 31 // 驅動入口函數 32 NTSTATUS DriverEntry(PDRIVER_OBJECT Driver, PUNICODE_STRING RegPath) { 33 PMMPFN node; // 表示鏈表中各個結點 34 UINT32 index; 35 _asm { 36 int 3 37 } 38 // 查看鏈表的個數 39 ULONG32 total = MmFreePageListHead->Total; 40 // 設置好開始部分 41 index = MmFreePageListHead->Flink; 42 // 循環遍歷鏈表 43 44 for (ULONG i = 0; i < total; i++) { 45 ULONG32 t = index * 0x18; 46 ULONG32 x = t + MmPfnDatabase; 47 // 獲取結點信息 48 node = (PMMPFN)x; 49 DbgPrint("u1:%x | PteAddress:%x | u2:%x | u3:%x | OrininalPte:%x | u4: %x" 50 , node->u1, node->PteAddress, node->u2, node->u3, node->OrininalPte, node->u4); 51 DbgPrint("%x | %x", node, index); 52 // 遍歷下一個結點 53 index = node->u1; 54 } 55 56 NTSTATUS status; 57 58 return STATUS_SUCCESS; 59 }