无处不在的页异常

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

无处不在的页异常

1. 无效PTE的情况

  之前我们在保护模式中分析了PTE的情况,但是其是有效的,如果无效则其数据结构是不同的。

  如果要区分是否有效,关键是看PTE中的P位,如果P位为1,则为有效,否则就为无效,无效分各种情况。

  

  1)磁盘中存在许多个 pagefile.sys文件,该文件就是所谓的虚拟内存,当内存不足时其会存放在pagefile.sys文件中,并且将pte修改为(a),原来的物理页被别处使用,

    当访问pte时,遇到这种情况,其会从[1-4]位中找到其所在的pagefile.sys位置,然后将其映射到磁盘上。

  2)当进程需要一个零页面,其可以直接访问(b)这种PTE格式的内存,操作系统发现就会通过缺页异常为其分配一个零页面的,这很方面。

  3)当该页面正在转移到磁盘上,或从磁盘转移到内存中,就会是(c)这样子,这个很好理解。

  4)最后一种就是未知原因,如果遇到这种情况,就需要检查VAD树,看看该页是否在当前的进程中,再做下一步处理,如果能查找,则分配内存,如果不存在,则会报c000005错误。

    注意:只有在三环才存在这种错误,因为在零环是没有Vad树的,这个很容易理解。

 

2. __MMPTE

  __MMPTE是一个联合体,如下存在几种PTE种类,其有效和各种无效的。

  kd> dt _MMPTE -r1
    nt!_MMPTE
       +0x000 u                : __unnamed
          +0x000 Long             : Uint4B
          +0x000 Hard             : _MMPTE_HARDWARE
          +0x000 Flush            : _HARDWARE_PTE // 正常使用的PTE
          +0x000 Proto            : _MMPTE_PROTOTYPE // 原型PTE
          +0x000 Soft             : _MMPTE_SOFTWARE
          +0x000 Trans            : _MMPTE_TRANSITION
          +0x000 Subsect          : _MMPTE_SUBSECTION
          +0x000 List             : _MMPTE_LIST

  1)原型PTE的用法

    当进程第一次引用一个已被映射的内存区对象的视图中的页面中,内存管理器利用原型PTE中的信息来填充页表中实际的PTE。

    原型PTE常用在共享内存中,比如映射文件,共享节区等,所谓原型PTE,其本质就是“状态的描述”,其会帮助修正物理指向。

    

    其原型PTE如上图所示,进程A B共享一个物理页面,但该页面换入虚拟内存中,此时进程AB共同指向内存段对象的原型PTE:

    ① 当A第一次访问时,其触发页异常错误,将页面加载到内存并且A的PTE从段对象转而指向该物理页P2,并且将段对象中的原型PTE也指向P2;

    ② 此时B也访问页面P2,其指向原型PTE,当触发页异常时,页异常处理程序发现存在原型PTE,其会自动去解析原型PTE,发现其指向P2,此时其会将B进程的PTE直接指向P2。

    如果你理解了这种,你会发现原型PTE就是所谓的“记录状态”,其为啥在共享中常用呢?因为其多个PTE共同指向原型PTE,这样可以提高效率。

    如果不用原型PTE,则A进程修复完之后还要去B、C、···D共同将页面置换(注意,不同进程存在自己的页目录表基址,即存在自己独立的PTE),而共同指向一个原型PTE,其存储在MmPfnDataBase中。

    如下图,其指向原型PTE的指针,其中“原型位”这个概念你一定要明确,现在再来看很好理解了。

    

  2)写拷贝原理

    PTE各种属性为中并没有写拷贝的概念,可写拷贝的页面,其PTE中的W位为0,此时如果强写会触发0E号页异常;

    此时其会去从VAD中查看该页的属性,发现其是写拷贝属性,则重新复制一份,之后修改其PTE,这样就实现了写拷贝了。

 

3.MDL内存映射

  1)内存中存在两种映射:

    ① MmIoSpace,其传入物理地址,其返回一个虚拟地址;

    ② Mdl,其传入一个虚拟地址,其再帮你映射一份。

    使用Mdl就会一个物理地址存在两个虚拟地址,如果一个虚拟地址修改了,就直接更改物理地址了。

  2)MDL用法:

    ①  IoAllocateMdl 创建一个MDL,写入映射的地址和大小,其MDL是一个连续的链表数据结构;

    ②  MmProAndLockPages  NoChange置为1,防止需要映射的内存置换出,否则发生页面置换,映射时为空则会出现蓝屏;

    ③  MmMapLockedPages 将MDL在内存中再映射一份,在这里就实现映射,其返回一个内存地址,我们就可以直接对该内存地址进行读写;

    ④  MmUnmapLockedPages 解除映射,此时就原来的虚拟地址对应物理地址;

    ⑤  IoFreeMdl 将该mdl从mdl链表中释放出去。

    其中注意在第③步时才会发生映射,即一个物理地址存在两份虚拟地址,MDL只是一个相关数据结构,驱动中可以常见,我们之后会来分析有关数据结构。

 

posted @ 2020-04-23 21:20  OneTrainee  阅读(453)  评论(0编辑  收藏  举报