zoukankan      html  css  js  c++  java
  • Windows内存布局 / MmPfnDataBase页帧数据库

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

    Windows内存布局 / MmPfnDataBase页帧数据库

    1. Windows操作系统在X86下的内存布局如下图所示

      

     

    2. 进程工作集 (Hyperspace and process working set list)

      如上图所示,在c0400000地址处存在一个进程工作集,其在EProcess+0x1f8 vm 表示,其是一个 _SUPPORT结构。

      

    3. nt!MmNumberOfPhysicalPages 查看存在多少物理页
      这是一个全局变量,表明操作系统当前可用的物理页个数,我们在10-10-12分页模式下一个页以4KB为单位,我们查看该变量:

      dd nt!MmNumberOfPhysicalPages 0001ff6c

      我们从任务管理器中查看其可用的内存

      

       很容易发现满足关系 0001ff6c*4 =  523696,因此可以验证我们的说法:该变量代表当前进程中可用的物理页数。

    4. 页帧数据库 PfnDataBase

      1) mmpfndatabase 全局变量

        该全局变量以数组形式存储对应的__MMPFN数据结构,整个可以看做 __MMPFN[x],x表示第x个物理页,这是数组一次排序。

        其__MPFN数据结构如下(其中涉及大量的Union类型),其单个长度 0x14。(注意,其长度不确定,具体看详细版本的长度)

        

       2) 物理页的八种状态(实际只有六个,其存在各自的链表)

        物理页本来应该存在八种状态,但其中只有六个可用,其存储在 __MMPFN.u3

        u3中存在一个 __MMPFNENTRY,里面描述了有关该页所处的状态(空闲状态,零化状态,未分配状态·····)。

        其根据物理页的状态,制作不同的链表,将相同属性的物理页串联在一起,如下六个全局变量:

        ① MmZeroedPageListHead  // 可以使用并清零的内存

        ② MmFreePageListHead // 被释放的内存,定期会检测是否有数据,如果有就清空并且挂到第一个链表中

        ③ MmStandbyPageListHead  // 分页内存,备份到虚拟文件中,如果①②用完其才会用到这个,紧急使用

        ④ MmModifiedPageListHead // 修改过属性的页

        ⑤ MmModifiednoWritePageListHead // 修改但是没有写出的链表

        ⑥ MmBadPageListHead // 坏页的链表

       具体的存储在 MmPageLocationList 这个数组中,我们在WRK中可以看到该数组的定义,我们可以看到,八个中确实只用到了六个。

        

        typedef enum _MMLISTS {
            ZeroedPageList,
            FreePageList,
            StandbyPageList,  //this list and before make up available pages.
            ModifiedPageList,
            ModifiedNoWritePageList,
            BadPageList,
            ActiveAndValid,
            TransitionPage
        } MMLISTS;

       3)__MMPFN的中存在 FlinkBlink 两个成员,但其并不是指针而是ULONG类型,前面说过内存物理页是依次排列的,那么这两个成员有什么用呢?

        前面我们说过其存在六个链表,虽然物理页的前后是确定的,但是相同属性的物理页却是通过 Flink 与 Blink 连接起来的,其并不是连接而是在页帧数据库中的位置,这个很好理解。

        

        而 MmPageLocationList数组中存储的各个属性物理页头部的地址,这样整个页帧数据库体系就很容易搭建起来了。

      4)Windbg 遍历pfn 单个 __MMPFN数据结构

      使用windbg的 !pfn x 命令可以查看其存在多少对应的PFN对应的物理页的属性:

      

    5. 驱动遍历某一链表

      结合我们上面所讲,其Pfn在内存中的布局如下,如果上面都看懂了,这部分应该很好理解,但其中MmPageLocationList与MmPfnDataBase并不是导出变量,我们通过IDA特征码定位即可。

      

    #include <ntifs.h>
    #include <ntimage.h>
    #include <intrin.h>
    #include "tools.h"
    
    typedef struct _MMPFN {
        ULONG Flink; // 前一个节点索引
        ULONG PteAddress; // 对应的pte地址
        ULONG Blink; // 后一个节点索引
        ULONG u3[3]; // 占位,总共0x14个字节
    }MMPFN,*PMMPFN;
    
    typedef enum _MMLISTS {
        ZeroedPageList,
        FreePageList,
        StandbyPageList,  //this list and before make up available pages.
        ModifiedPageList,
        ModifiedNoWritePageList,
        BadPageList,
        ActiveAndValid,
        TransitionPage
    } MMLISTS;
    
    typedef struct _MMPFNLIST {
        PFN_NUMBER Total;
        MMLISTS ListName;
        PFN_NUMBER Flink;
        PFN_NUMBER Blink;
    } MMPFNLIST, *PMMPFNLIST;
    
    
    PULONG MmPageLocationList; // 
    PULONG MmPfnDataBase; // 页帧数据库
    
    VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
    {
        DbgPrint("卸载完成
    ");
    }
    
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegsiterPath)
    {
    
        pDriverObject->DriverUnload = DriverUnload;
        
        // 定位 MmPageLocationList 
        FindCode findcodes[1] = { 0 };
        initFindCodeStruct(&findcodes[0],
            "8B******8B48*FF088B56*894D*8B0E83**894D*0F*****8B*****8D0C498954**8B5D*8B4D*",
            0, 0);
        PUCHAR f = (PUCHAR)FindAddressByCode(findcodes, 1);
        MmPageLocationList = *(PULONG) ((PUCHAR)f + 3);
    
        // 定位 MmPfnDataBase 
        FindCode findcodes2[1] = { 0 };
        initFindCodeStruct(&findcodes2[0],
            "8B*****8D044956578D34C28B7E*A1****C1**83**85C0895D*894D*0F*****83******0F*****8B46*",
            0, 0);
        f = (PUCHAR)FindAddressByCode(findcodes2, 1);
        PULONG  MmPfnDataBase = *(PULONG)(*(PULONG)((PUCHAR)f + 2));
    
        // 查询空闲链表数据成员
        MMLISTS PfnIndex = FreePageList;
        PMMPFNLIST ZeroedPageList = MmPageLocationList[PfnIndex];
        DbgPrint("零化链表的总数为:%x,前一个节点为:%x,后一个节点为:%x
    ", ZeroedPageList->Total,ZeroedPageList->Flink,ZeroedPageList->Blink);
    
        // 开始遍历PfnDataBase页帧数据库 
        //DbgPrint("MmPfnDataBase地址为:%p
    ", MmPfnDataBase);
        ULONG index = ZeroedPageList->Flink; // 获取第一个节点索引
        PMMPFN p; 
        for (ULONG i = 0; i < ZeroedPageList->Total; i++) {
            p = (PMMPFN)((PUCHAR)MmPfnDataBase + sizeof(MMPFN) * index);  // 获取结点
            DbgPrint("Current PteAddress:%x
    ", p->PteAddress);
            index = p->Flink; // 更新下一个结点
        }
        return STATUS_SUCCESS;
    }
  • 相关阅读:
    Linux环境下MySQL报Table 'xxx' doesn't exist错误解决方法
    mysql主主复制(双主复制)配置步骤
    MHA安装手记
    MySQL MHA配置
    innobackupex 还原和备份实例
    scp命令
    给想进入餐饮业新手一些建议
    mysql mha 主从自动切换 高可用
    mysql innobackupex xtrabackup 大数据量 备份 还原
    MySQL主从复制、半同步复制和主主复制概述
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11730451.html
Copyright © 2011-2022 走看看