内存管理

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

 内存管理

1. 线性地址管理

  1)VadRoot _EPROCESS+0x11c

    每个进程的_EPROCESS+0x11c位置,都存在一个二叉树,VadRoot,之前我们有过介绍。

    该VadRoot中以二叉树的形式记录了当前线程中所申请的全部内存,以__MVD的形式保存。

    windbg中使用 !vad addr 可以遍历该内存的各种VAD属性。

  2)__MVAD

    //0x28

    bytes (sizeof) struct _MMVAD {

      ULONG StartingVpn; //0x0 开始的内存页地址

      ULONG EndingVpn; //0x4  结束的内存页地址

      struct _MMVADParent; //0x8

      struct _MMVADLeftChild; //0xc

      struct _MMVADRightChild; //0x10

      union {

        ULONG LongFlags; //0x14

        struct _MMVAD_FLAGS VadFlags; //0x14 访问属性

      } u; //0x14

      struct _CONTROL_AREAControlArea; //0x18

      struct _MMPTEFirstPrototypePte; //0x1c

      struct _MMPTELastContiguousPte; //0x20

      union {

        ULONG LongFlags2; //0x24

        struct _MMVAD_FLAGS2 VadFlags2; //0x24

      } u2; //0x24

     };

    ① 二叉树链表,指向父节点,左子树与右子树。

    ② StartingVpn EndingVpn:表示页的起始地址和结束地址,以4KB为单位,所获取结果直接乘以0x1000获取线性地址。

    ③ ControlArea - 该页映射的具体内容

      如果该值为NULL,则说明该页是物理内存;如果指向FILE_OBJECT,则说明指向文件的映射。

      一个是VirtualAlloc申请的,一个是Map映射的内存,通过这个可以区分。

    ④ VadFlags - 指明当前内存页的属性,可读可写可访问之类的,通过 __MMVAD_FLAGS可以详细查看有关位的标识。

  3)总结:

    该部分是管理线性地址,而不是物理地址的,因此获取的地址(VPN)是线性地址而不是物理地址,这个很简单的道理你需要明确。

 

2. PrivateMemory

  前面讲过内存存在两类,一类是PrivateMemory,一类是MappedMemory内存。

  PrivateMemory是通过VirtualAlloc分配内存,其第一个参数是线性地址执行的,必须在VadRoot中StartingVpn没有被占用的,这个你要明确。

  1)VirtualAlloc

    该函数的本质就是申请PrivateMemory内存,申请一块__MMVAD结构块,然后挂到有关进程的_VAD链表中。

    因此该地址的lpAddress就是对应的__MMVAD.StartVpn,如果被占用则会重新分配地址,因此这个参数不确保一定会申请出来。

    LPVOID VirtualAlloc{
      LPVOID lpAddress, // 要分配的内存区域的地址
      DWORD dwSize, // 分配的大小
      DWORD flAllocationType, // 分配的类型
      DWORD flProtect // 该内存的初始保护属性
    };

  2)malloc 与 new 内存:

    ① new 内存

    ② malloc的底层实现 HeapAlloc,从堆内存中申请内存,HeapAlloc没有进零环,

    ③ 所谓堆内存,是创建进程时操作系统提前为我们使用VirtualAlloc分配好的一块大内存,因此HeapAlloc并没有进零环,因为直接拿去分配好的内存。

 

3. MappedMemory

  1)文件映射

    ① CreateFileMapping 该函数关联物理页与对应的文件。

    ② MapViewOfFile 将物理页与对应进程的线性地址关联,可以在相关进程的Vad中搜索到该内存。

  2)DLL映射

    LoadLibrary的底层是CreateFileMapping,底层实现映射。

    其将属性设置为写拷贝,当修改时,其会自动创建一个物理页拷贝过去,而不修改原始文件。

 

4. 物理内存的管理 MmPfnDataBase 物理页数据库

  1)查看当前物理页的数量

    MmNumberOfPhysicalPages 全局变量,里面存储着当前物理页的个数。

    其中一页为4KB,转换为可以得到相应的物理内存。

    

  2)物理地址如何管理

    其存储在一个全局数组中,_MMPFN *MmPfnDataBase,其数组长度就是 MmNumberOfPhysicalPages

    其数组中的成员结构为 __MMPFN,其成员很好理解。

  3)线性地址与物理地址如何查找管理

    注意:__MMVAD没有一个指针指向__MMPFN结构,根据数组计算。

    比如一个__MMVAD.StartingVpn为X,如果要找对应的物理页索引,即 MmPfnDataBase[x],__MMPFN其大小为0x1c,因此这样很容易计算得出。

  4)物理页的属性及六个链表 物理地址属性 __MMPFN.u3

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

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

    ① MmZeroedPageListHead

    ② MmFreePageListHead

    ③ MmStandbyPageListHead

    ④ MmModifiedPageListHead

    ⑤ MmModifiednoWritePageListHead

    ⑥ MmBadPageListHead

  4)总结:

    物理页管理如下图,由一个MmPfnDataBase[]数组来管理索引的,注意_MMPFN的Flink与Blink,并不是指向前后链表,其是一个索引。

    MmPfnDataBase是一个数组,因此很容易就可以从前面找到后面的,其Flink与Blink是相同属性的物理页索引(上面介绍的四个数组)。

    

 

 

5. 缺页异常

  PTE结构中如果P位为0,如果访问该位,则会产生缺页异常。

  1)为什么会产生缺页异常?

    如果一个物理页长时间不被使用或者物理页紧缺,会将当前物理页拿出来,存放要硬盘上,这成为虚拟内存。

  2)COMMIT、RESERVED属性区别         

posted @ 2022-02-12 16:59  OneTrainee  阅读(192)  评论(0编辑  收藏  举报