windows内核(2)——页属性实验
来具体学习一下页属性
看前必读
本文所述的第 x 位均表示下标从 0 开始的计数制。
1 | 0000100010001 |
例如上面星号所指示的位置表示第 1 位。
有效属性
可以关注内核函数 MmIsAddressValid 实现原理,取出虚拟机 C:\Windows\System32\ntoskrnl.exe
内核文件,找到该函数,F5可得以下逻辑
1 | bool __fastcall sub_48DCB8(unsigned int a1) |
看到第一个表达式 (a1 >> 20) & 0xFFC)
,可以认为是将地址右移了 22 位(取高10位),再左移了 2 位(*4
),然后将该值减去 0x3FD00000,其实转换成加法就是 +0xC0300000
,也就是我们的页目录表的线性地址。
如果该地址的最低位为 0,则返回 0(p位为0,无效)。
如果该地址的第7位为 1,说明是个大页,那么直接返回 1,整个页都是有效的,否则进行后续判断。
这里就是判断 PDE 的最低为是否为 0,若为 0 则还是无效。
否则返回第7位是否为 1 (这里存疑,不明白为什么PAT位为1才表示有效)。
总体,该函数的实现就是通过两个关键的线性地址 0xC0000000
和 0xC0300000
,检查页表的属性来实现的功能。
读写属性
我是这么认为的:一个虚拟页,只要挂上了对应的物理页且有效,它必定可读,是否可写根据第 1 位标志位确定。
举个例子
1 |
|
尝试向字符串常量指向的地址进行写入则必定会出一个写入错误,那么在中间下个断点,然后修改挂的物理页。
拆分得到 PDI,PTI分别为 1,3,页内偏移为 0x12C
可以发现 PTE 的第一位为 0,即该页不可写。
同时也可以验证一下该物理页的正确性
将该物理页设为可写,使用命令 !ed a6fab00C 0b8e2027
。
可以发现这个字符修改成功了
特权位
当我们处于三环的权限,我们无法访问 U/S 位为 0 的页,通常,32位地址下,我们无法访问 80 开头的地址,因为只有 0 环态下可以访问。
例如,我修改 gdt 所属的页,让gdt变得三环状态可读,参考以下步骤:
- 通过 gdtr 找到 gdt 所在的页。
- 拆分地址找到对应的物理页对应的 PTE。
- 将物理页的 U/S 位改为 1。
这里我的 gdtr 为 80b93800
1 | PDI 1000000010 |
按照同样的方法,找到了 gdt 的物理页。
图中反的主要原因时物理页输出是 dd,虚拟页输出是 dq,会小端序的反转一下。
我们看到物理页的 PTE,结果是 00b93163
,很明显第二位,也就是 U/S 位为 0,表明是 0 环才可以访问的,那么将它改为三环可访问,使用命令 !ed 0018a000+0x393*4 00b93167
。
结果发现好像还是不可读,是为什么呢?
对啦,原来PDE对应的U/S位也要改,因为它们是与的关系,所以还需要再增加一个命令 !ed 0000000000185000+0x202*4 0018a067
,做完这些,再验证一下三环程序能否读到 gdt 表。
1 |
|
这里需要注意的是,PTE基本最后映射的都是同一个,但是PDE不一样,因此需要这么操作才能实验成功:
- 运行程序,中断。
- 找到该程序 CR3 的值,得到 PDE,将PDE的U/S位改为1(每次重新运行必须做这件事)
- 根据PDE找到对应的PTE,将PTE的U/S位改为 1(只需要改一次就可以)
可以看到,程序能够成功读取gdt表的四个字节。
访问位
这个属性不太好做实验验证,只需要知道:访问(读或写)过了则为 1,否则为0。
脏位
写过了则为 1,否则为0,且只有PTE具有这个属性。
其余位
其余位都跟缓存相关,实验自己不太会设计了,咕咕吧,等后面有能力了再来验证。