练习逆向XP内核2-9-9-12和10-10-12分页的MmIsAddressValid函数

前言:这篇笔记记录逆向XP内核MmIsAddressValid函数的学习

这个MmIsAddressValid函数就是判断对一个传入的线性地址是否有效,这里的有效指的就是判断可以将其转换为对应的物理地址,那么其中肯定就有对应的PDE PTE,如果都存在的话,并且P位有效等,那么也就是一个有效的地址

这个函数里面还判断了相关的PDE的PS位,是否为大页,还有PTE的PAT位,这个PAT位后面还走了一个跳转的流程,这个不知道具体是干什么的

然后还有一个重要的知识点,就是通过逆向这个函数,你会发现操作系统在找线性地址对应的物理页的时候,它的整体流程,通过C0300000页目录基址,C0000000页表基址来进行寻找

如何寻找MmIsAddressValid内核函数的方法

知识点:

1、XP系统下的2-9-9-12分页模式通过内核文件ntkrnlpa.exe来进行观察

2、XP系统下的10-10-12分页模式通过内核文件ntoskrnl.exe来进行观察

都是逆向可以用两个工具一个是ida,一个是windbg,windbg的话直接u MmIsAddressValid L40即可,如下图所示

因为当前XP是10-10-12分页下的,所以拿内核文件ntoskrnl.exe来进行分析,这里通过IDA来进行分析,ALT+T来进行全局搜索字符串MmIsAddressValid,

如下图所示,我这里用的是全局搜索字符串,或者直接在左边函数列表中Ctrl+F来搜索也可以

.text:0040AEC6 ; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress)
.text:0040AEC6                 public MmIsAddressValid
.text:0040AEC6 MmIsAddressValid proc near              ; CODE XREF: sub_40AF1C+C↓p
.text:0040AEC6                                         ; sub_4129D8+2F5B↓p ...
.text:0040AEC6
.text:0040AEC6 VirtualAddress  = dword ptr  8
.text:0040AEC6
.text:0040AEC6 ; FUNCTION CHUNK AT .text:0041DDA5 SIZE 00000007 BYTES
.text:0040AEC6 ; FUNCTION CHUNK AT .text:0044F150 SIZE 00000019 BYTES
.text:0040AEC6
.text:0040AEC6                 mov     edi, edi
.text:0040AEC8                 push    ebp
.text:0040AEC9                 mov     ebp, esp
.text:0040AECB                 mov     ecx, [ebp+VirtualAddress]
.text:0040AECE                 mov     eax, ecx
.text:0040AED0                 shr     eax, 14h
.text:0040AED3                 mov     edx, 0FFCh
.text:0040AED8                 and     eax, edx
.text:0040AEDA                 sub     eax, 3FD00000h
.text:0040AEDF                 mov     eax, [eax]
.text:0040AEE1                 test    al, 1
.text:0040AEE3                 jz      loc_41DDA5
.text:0040AEE9                 test    al, al
.text:0040AEEB                 js      short loc_40AF11
.text:0040AEED                 shr     ecx, 0Ah
.text:0040AEF0                 and     ecx, 3FFFFCh
.text:0040AEF6                 sub     ecx, 40000000h
.text:0040AEFC                 mov     eax, ecx
.text:0040AEFE                 mov     ecx, [eax]
.text:0040AF00                 test    cl, 1
.text:0040AF03                 jz      loc_41DDA5
.text:0040AF09                 test    cl, cl
.text:0040AF0B                 js      loc_44F150
.text:0040AF11
.text:0040AF11 loc_40AF11:                             ; CODE XREF: MmIsAddressValid+25↑j
.text:0040AF11                                         ; MmIsAddressValid+44298↓j
.text:0040AF11                 mov     al, 1
.text:0040AF13
.text:0040AF13 loc_40AF13:                             ; CODE XREF: MmIsAddressValid+12EE1↓j
.text:0040AF13                 pop     ebp
.text:0040AF14                 retn    4
.text:0040AF14 MmIsAddressValid endp
...
...
.text:0044F150 ; START OF FUNCTION CHUNK FOR MmIsAddressValid
.text:0044F150
.text:0044F150 loc_44F150:                             ; CODE XREF: MmIsAddressValid+45↑j
.text:0044F150                 and     eax, edx
.text:0044F152                 mov     eax, [eax-3FD00000h]
.text:0044F158                 and     ax, 81h
.text:0044F15C                 cmp     al, 81h
.text:0044F15E                 jnz     loc_40AF11
.text:0044F164                 jmp     loc_41DDA5
.text:0044F164 ; END OF FUNCTION CHUNK FOR MmIsAddressValid

10-10-12分页逆向过程


位运算的知识点:

.text:0040AEED                 shr     ecx, 0Ah
.text:0040AEF0                 and     ecx, 3FFFFCh

对于这两条,我一开始看不懂,为什么这样的位运算可以使得PDI*4096+PTI*4,然后我自己想了下,应该是获取10-10-12的 10 和10的时候,此时 >> 10 ,相当于少右移了2位,那么整体多左移了2位,那么对于PDI的话,其实就是右移了12位也就是2^(10+2)那么是*4096,而对于PTI的话就是右移了2位也就是2^(0+2)次,那么*4

思考

从这个函数的入口可以看到mov edi,edi的指令,好像没什么用,因为下面都没看到有使用该寄存器,实际上是用到的:

参考文章:https://blog.csdn.net/swanabin/article/details/17550897

很明显,两个字节的MOV EDI,EDI指令什么事情也不做?

那么,就有两个问题:

第一,为什么不直接从函数体开始而要从这条什么都不做的指令开始呢?

第二,即使需要在函数一开始空出两个字节,为什么不直接使用两条NOP指令,而要使用这条MOV指令呢?

在网上查阅一些资料后,得到了答案:

对于第一个问题,答案是为了实现hot-patching技术,即运行时修改一个函数的行为。修改过程如下:把MOV EDI, EDI修改为一条短跳转指令(一条短跳转指令恰好两个字节),把MOV EDI, EDI上面的五个NOP修改为一条长跳转指令(一条长跳转指令恰好五个字节),短跳转指令跳到长跳转指令上,长跳转指令跳到修改后的函数体上。

这个指令可以用在与0环交互的时候,比如中断门就需要两个字节,刚刚好满足条件,后面驱动篇章实现去写拷贝监控的项目中就会用到

参考文章: https://www.cnblogs.com/zpchcbd/p/15949802.html

对于第二个问题,答案是为了提高效率。执行一条MOV指令比执行两条NOP指令花费更少的时间。

注意点

逆向的时候可以看到,偏移有些写的是sub ecx, 40000000hsub eax, 3FD00000h,实际上转换下为:

sub ecx, 40000000h -> add ecx, C0000000h

sub eax, 3FD00000h -> add eax, C0300000h

2-9-9-12分页逆向过程

这个是后面学了2-9-9-12以后补上的

2-9-9-12分页模式通过内核文件ntkrnlpa.exe来进行观察

同样的还是用IDA来进行分析

.text:0043CA48 ; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress)
.text:0043CA48                 public MmIsAddressValid
.text:0043CA48 MmIsAddressValid proc near              ; CODE XREF: sub_41ADD4+2F↑p
.text:0043CA48                                         ; sub_41AE26+29↑p ...
.text:0043CA48
.text:0043CA48 var_8           = dword ptr -8
.text:0043CA48 var_4           = dword ptr -4
.text:0043CA48 VirtualAddress  = dword ptr  8
.text:0043CA48
.text:0043CA48                 mov     edi, edi        ; hot-patching技术
.text:0043CA4A                 push    ebp
.text:0043CA4B                 mov     ebp, esp
.text:0043CA4D                 push    ecx
.text:0043CA4E                 push    ecx
.text:0043CA4F                 mov     ecx, [ebp+VirtualAddress] ; ecx = VirtualAddress 也就是压入的线性地址
.text:0043CA52                 push    esi             ; 保存原来的esi,后面需要用到esi作为计算
.text:0043CA53                 mov     eax, ecx        ; eax = VirtualAddress
.text:0043CA55                 shr     eax, 12h        ; VirtualAddress = VirtualAddress >> 18
.text:0043CA58                 mov     esi, 3FF8h      ; esi = 0x3FF8
.text:0043CA5D                 and     eax, esi        ; VirtualAddress = VirtualAddress & 0x3FF8 相当于获取的是PDPTEI*8*512+PDI*8
.text:0043CA5F                 sub     eax, 3FA00000h  ; eax = PDPTEI*8*512+PDI*8+0xC0600000,相当于拿到了PDE的地址
.text:0043CA64                 mov     edx, [eax]      ; 获取PDE结构地址中存储的值
.text:0043CA66                 mov     eax, [eax+4]    ; 获取当前PDE结构中得下一个PDE结构地址中存储的值
.text:0043CA69                 mov     [ebp+var_4], eax ; 把第二个获取的PDE结构地址中的值存储到堆栈中
.text:0043CA6C                 mov     eax, edx        ; eax = 第一个获取的PDE结构地址中存储的值
.text:0043CA6E                 push    edi
.text:0043CA6F                 and     eax, 1          ; 判断是否第一个获取的PDE结构地址中存储的值,就可以判断当前PDE结构P位是否有效
.text:0043CA72                 xor     edi, edi
.text:0043CA74                 or      eax, edi        ; 判断是否当前PDE结构中的值是否为空
.text:0043CA76                 jz      short loc_43CAD9 ; 如果为空则结束
.text:0043CA78                 mov     edi, 80h
.text:0043CA7D                 and     edx, edi        ; 判断PDE是否为大页,也就是判断PS位,第7位
.text:0043CA7F                 push    0
.text:0043CA81                 mov     [ebp+var_8], edx ; 判断PS位的结果存储到堆栈中
.text:0043CA84                 pop     eax             ; 清空eax
.text:0043CA85                 jz      short loc_43CA8B ; 如果不是大页,PS位不为1,那跳转到0x43CA8B进行处理
.text:0043CA87                 test    eax, eax
.text:0043CA89                 jz      short loc_43CADD ; 如果走到这里,那么就肯定会跳转到43CADD,意味着这个函数的结束
.text:0043CA8B
.text:0043CA8B loc_43CA8B:                             ; CODE XREF: MmIsAddressValid+3D↑j
.text:0043CA8B                 shr     ecx, 9          ; 下面是PDE PS位0 物理页为4kb的情况的处理,VirtualAddress = VirtualAddress >> 9
.text:0043CA8E                 and     ecx, 7FFFF8h    ; ecx相当于获取的是PDPTEI*512*512*8+PDI*8*512+PTI*8
.text:0043CA94                 mov     eax, [ecx-3FFFFFFCh]
.text:0043CA9A                 sub     ecx, 40000000h  ; ecx = C0000000+PDPTEI*512*512*8+PDI*8*512+PTI*8
.text:0043CAA0                 mov     edx, [ecx]      ; 获得指定的PTE结构地址中的值
.text:0043CAA2                 mov     [ebp+var_4], eax
.text:0043CAA5                 push    ebx
.text:0043CAA6                 mov     eax, edx        ; edx = PTE结构地址中的值
.text:0043CAA8                 xor     ebx, ebx
.text:0043CAAA                 and     eax, 1          ; 判断有效位P
.text:0043CAAD                 or      eax, ebx        ; 判断当前PTE结构中的值有没有
.text:0043CAAF                 pop     ebx
.text:0043CAB0                 jz      short loc_43CAD9 ; 如果没有值的话,那么函数结束
.text:0043CAB2                 and     edx, edi        ; 判断PTE的PAT位,第7位
.text:0043CAB4                 push    0
.text:0043CAB6                 mov     [ebp+var_8], edx
.text:0043CAB9                 pop     eax
.text:0043CABA                 jz      short loc_43CADD ; 如果PAT位为0则跳转
.text:0043CABC                 test    eax, eax        ; 这个不知道再判断什么,但是这里的会正常来说不会跳转,一直往下走
.text:0043CABE                 jnz     short loc_43CADD
.text:0043CAC0                 and     ecx, esi        ; PDE的地址 & 3FF8 看不太懂
.text:0043CAC2                 mov     ecx, [ecx-3FA00000h] ; (PDE的地址 & 3FF8) + 0xC0600000,这个不知道获得的是什么结构体,老师没讲过
.text:0043CAC8                 mov     eax, 81h
.text:0043CACD                 and     ecx, eax
.text:0043CACF                 xor     edx, edx
.text:0043CAD1                 cmp     ecx, eax        ; 同样也会判断第7位和第0位
.text:0043CAD3                 jnz     short loc_43CADD
.text:0043CAD5                 test    edx, edx
.text:0043CAD7                 jnz     short loc_43CADD
.text:0043CAD9
.text:0043CAD9 loc_43CAD9:                             ; CODE XREF: MmIsAddressValid+2E↑j
.text:0043CAD9                                         ; MmIsAddressValid+68↑j
.text:0043CAD9                 xor     al, al
.text:0043CADB                 jmp     short loc_43CADF
.text:0043CADD ; ---------------------------------------------------------------------------
.text:0043CADD
.text:0043CADD loc_43CADD:                             ; CODE XREF: MmIsAddressValid+41↑j
.text:0043CADD                                         ; MmIsAddressValid+72↑j ...
.text:0043CADD                 mov     al, 1
.text:0043CADF
.text:0043CADF loc_43CADF:                             ; CODE XREF: MmIsAddressValid+93↑j
.text:0043CADF                 pop     edi
.text:0043CAE0                 pop     esi
.text:0043CAE1                 leave
.text:0043CAE2                 retn    4
.text:0043CAE2 MmIsAddressValid endp
posted @ 2022-02-20 13:23  zpchcbd  阅读(347)  评论(0编辑  收藏  举报