在对页表基址,页目录表基址熟练掌握后,今天来看逆向分析一个函数:MmIsAddressValid。这是一个系统函数,可以在ntoskrnl.exe的导出函数中找到它,也可以在Windbg中输入指令
1 | kd> u MmIsAddressValid |
查看。
为什么要分析这个函数呢?因为即使是系统函数,也是无法直接使用物理页的,想要去访问PDE和PTE也就一定要通过基址来访问,而今天要分析的MmIsAddressValid函数,就利用了这么一个原理,相比前一篇的基址小实验,这里对于线性地址拆分的过程更为巧妙,让我们一起来看看吧!
获取PDE属性
首先观察函数主体部分,发现代码并不长,但是有很多跳转,具体跳转内容就不作分析了,主要是分析函数主体:
1 | 804e4661 8bff mov edi,edi //hotpatch |
巧妙的与运算:
- 首先将线性地址逻辑右移20位,此时还余下12位
- 将这12位和操作数0xFFC做一个与运算,0xFFC换算成2进制就是1111 1111 1100。因此做完与运算后,刚刚经过第一步操作还剩下12位的数的低2位,置0了。熟悉移位运算的朋友们知道,这个12位的数,相当于1个10位的数逻辑左移2位得到,换句话说就是将这个10位的数乘4。而这个10位,就是PDI,因此这步操作完了以后,相当于我们获得了PDI*4的值。
- 接下来,与0x3FD00000做减法运算,作用相当于加上0xC0300000,两种方法的结果是一样的。因此,我们得到了0xC0300000 + PDI*4的值,而这个值,恰恰就是我们要找的PDE,接着只需要取出里面的值,就可以获取PDE的属性了
后续跳转
再获取PDE的属性后,会遇到两个跳转,简单的概括下:
- 首先会判断PDE下标为0的位置的值,也就是P位,当P位为0时,说明物理页无效,会跳转到一个相应的处理函数,这里就不再跟进分析
- 若物理页P位为1,就会来到第二个跳转,这里test al, al指令会修改标志寄存器,当al的最高位,也就是下标为7的位置值为1时,会被认为是负数,此时会修改EFLAG寄存器的SF位。这时,在第二个跳转的位置,js判断的就是SF的值是否为1,若为1,也就是al下标为7的位置值为1,这是对应的PDE属性PS位,说明这是一个4MB的大页,进而会跳转执行相应的处理函数。
获取PTE属性
1 | 804e4688 c1e90a shr ecx,0Ah //逻辑右移10位 |
巧妙的与运算
1 | and ecx, 3FFFFCh |
假设线性地址右移10位后的值为 0000 0000 00aa aaaa aaaa bbbb bbbb bbxx(a, b的值为0或者1,这里只是为了区分PDI和PTI)
然后我们来拆分0x3FFFFC的值:0000 0000 0011 1111 1111 1111 1111 1100
将两者进行与运算后得到结果为 0000 0000 00aa aaaa aaaa bbbb bbbb bb00
我们知道,aa aaaa aaaa应为PDI,而bb bbbb bbbb应为PTI,因此可以把得到的结果看作是这样的一个运算:
$$
aa aaaa aaaa<<12 + bb bbbb bbbb<<2
= aa aaaa aaaa * 2^12 + bb bbbb bbbb * 2^2
= aa aaaa aaaa * 1000h + bb bbbb bbbb * 4h
= PDI1000h + PTI4h
$$
因此经过这个与运算后,再通过溢出进行减法运算,获取到PTE的所在位置,便可以取出属性
后续跳转
经过最为关键的步骤,取到了PTE的属性后,还剩下两个跳转,这两个跳转的原理和上述的两个跳转完全一样,分别是取PTE下标为0的P位和下标为7的PAT位,区别仅仅在于,PDE和PTE的下标为7的位的含义不同,所以会跳转进入两个不同的处理函数,这里就不多做分析。
总结
今天的分析主要是弄清楚Windows系统函数是如何获取物理页属性的,同样是通过利用页目录表基址和页表基址,实现用线性地址访问PDE和PTE,在分析时看到,比较关键的一步是两个与运算,非常巧妙,之后再通过减法运算利用溢出达到和加法运算同样的效果,获取到PDE/PTE的值。
参考资料:Joney的笔记,张嘉杰的笔记(群内的两位大佬,由于他们没有开博,先不贴链接了)