存储物理页属性的PFN数据库

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 }
相关文章
相关标签/搜索