首页
社区
课程
招聘
[原创]X64下MmIsAddressValid的逆向及内存寻址解析
发表于: 2015-10-21 20:03 18990

[原创]X64下MmIsAddressValid的逆向及内存寻址解析

2015-10-21 20:03
18990

在内核编程时,经常会用到MmIsAddressValid来检测地址是否有效,结合之前学过的虚拟地址到物理地址之间的转化,所以发一篇对该函数的逆向以及代码还原,x86的资料论坛以及网络有很多了,所以这里楼主只谈一下Win7 x64下的MmIsAddressValid过程分析

  要检测地址是否有效以及逆向MmIsAddressValid,还是得先从 x64下线性地址的格式入手



为了方便起见,楼主只说一下最普通的4k分页的情况,其他的可以举一反三
从这张图上,可以看到虽然线性地址有64位,但实际只用了48位,高16位其实是符号拓展位, 根据这高16位也可以做一个简单的内存地址是否有效判断(后面逆向会谈到)

从上面这张图,可以看到线性地址被拆开成了五个部分, 还有四张表分别是:
PML4T : 大小为4k ,每项8 字节 ,每一项指向PDPT的首地址
PDPT  : 大小为4k ,每项8 字节 , 每一项指向PD的首地址
PD  : 大小为4k ,每项8 字节 , 每一项指向PT的首地址
PT: 大小为4k ,每项8 字节 , 每一项存放一个物理页

如果从编程的角度来说,这四张表其实就是四个数组,数组的每一项可以看做是一个指针
线性地址被拆开成了五个部分,前四个部分都可以看做是数组的下标

举个例子,比如线性地址的39-47位,可以看做是一个Pml4t的下标Index,那么在代码里相当于
Pml4t[Index * 8]这样的寻址
其他的数组的访问依此类推

最后在PT的数组里找到物理页后,再加上线性地址的低12位,便是最终的物理地址



上面这张图是每一张表项的结构图,其中比较重要的是第0位,也就是P位,用于检测所指的页面或页表当前是否加载到了物理存储器中 ,p = 0即为无效

了解了理论,再来手工解析一下
随便写一个程序如下:


字符串地址在0000000140092000
字符串内容是NoteBook

首先,对该虚拟地址做一个格式转化
0     101       000000000        010010010       000000000000
0      5                    0                      0x92                          0
由低到高分成了五个部分,前四个是四个数组的下标,最后的0是偏移

第一步,找到该进程的CR3的值,也就是第一个表PML4T的首地址


地址在4e37b000 处   ,注意,该地址是物理地址 ,并且根据格式,低十二位需要清0

第二步,找到该进程PDPT 的首地址,根据之前的下标,得到在PML4T[0]处:

对齐后,PDPT的首地址在4d1cc000处

第三步,找到该进程PDT的首地址,根据之前的下标,得到在PDPT [5]处:


对齐后,PDT的首地址在4d8cd000处

第四步,找到该进程PT的首地址,根据之前的下标,得到在PDT [0]处:


对齐后,PT的首地址在4de4e000处

第五步,找到对应的物理页,根据之前的下标,得到在PT[0x92]处:


对齐后,数据物理页在4cdfa000处

最后一步,得到完整的物理地址:


和程序中的字符串内容一样

说了这么多,进入逆向MmIsAddressValid

.text:00000001400AD930 _MmIsAddressValid proc near             .text:000000014000FB6E
.text:00000001400AD930                                         
.text:00000001400AD930                 mov     rax, rcx
.text:00000001400AD933                 sar     rax, 48                               
                                                 ;取得线性地址的   高16位
.text:00000001400AD937                 inc     rax
.text:00000001400AD93A                 cmp     rax, 1
.text:00000001400AD93E                 ja      loc_1400AD9D3   
                                 ; 高16位要么全0, 要么全1 ,加一后大于1则不合法,直接返回false
.text:00000001400AD944                 mov     rax, rcx
.text:00000001400AD947                 mov     rdx, 0FFFFF6FB7DBED000h
                                                     ; PML4T 的虚拟地址
.text:00000001400AD951                 shr     rax, 39        
                                                   ; 将虚拟地址右移39位
.text:00000001400AD955                 and     eax, 1FFh      
                                                   ; 拿到pml4数组的下标
.text:00000001400AD95A                 test    byte ptr [rdx+rax*8], 1
                                                 ; 检测PML4T项p位
.text:00000001400AD95E                 jz      short loc_1400AD9D3
                                                  ; p=0 则直接返回false
.text:00000001400AD960                 mov     rax, rcx
.text:00000001400AD963                 mov     rdx, 0FFFFF6FB7DA00000h
.text:00000001400AD96D                 shr     rax, 27         
                                                ; 右移30位 ,再 乘 8 ,相当于右移27位
.text:00000001400AD971                 and     eax, 1FFFF8h   
                                                ; 8字节对齐 得到PDPT的偏移
.text:00000001400AD976                test    byte ptr [rax+rdx], 1
                                               ; 检测PDPT项的p位 rax+rdx=PDPT的首地址
.text:00000001400AD97A                jz      short loc_1400AD9D3
.text:00000001400AD97C                 mov     rdx, -0FFFFF6FB40000000h
.text:00000001400AD986                 mov     rax, rcx
.text:00000001400AD989                 shr     rax, 18        
                                                  ; 右移21位,再乘8 ,相当于右移18位
.text:00000001400AD98D                 and     eax, 3FFFFFF8h       
                                                   ;得到PDT的偏移
.text:00000001400AD992                 sub     rax, rdx      
                                                 ; rax = rax + 0FFFFF6FB40000000h  
.text:00000001400AD995                 mov     rdx, [rax]               
                                                ;此时rax指向PDT中的 某一项
.text:00000001400AD998                 test    dl, 1         
                                                  ; 检测PDT项p位
.text:00000001400AD99B                 jz      short loc_1400AD9D3
.text:00000001400AD99D                 test    dl, dl
.text:00000001400AD99F                 js      short loc_1400AD9D6
                                                     ; 是否开启PSE,是的话直接返回真
.text:00000001400AD9A1                 shr     rcx, 9         
                                                    ; 右移12位,再乘 8 ,相当于右移9位
.text:00000001400AD9A5                 mov     rax, 7FFFFFFFF8h ; 8字节对齐
.text:00000001400AD9AF                 and     rcx, rax               
.text:00000001400AD9B2                 mov     rax, -0FFFFF68000000000h
.text:00000001400AD9BC                 sub     rcx, rax
.text:00000001400AD9BF                 mov     rax, [rcx]       
                                                   ;此时RCX指向PT的的某一项
.text:00000001400AD9C2                 test    al, 1
.text:00000001400AD9C4                 jz      short loc_1400AD9D3
                                                   ; 检测PT项的P位
.text:00000001400AD9C6                 mov     r8b, 80h
.text:00000001400AD9C9                 and     al, r8b
.text:00000001400AD9CC                 cmp     al, r8b
.text:00000001400AD9CF                 setnz   al            
                                                  ; 检测PT项的PAT位是否存在,不存在返回真
.text:00000001400AD9D2                 retn
.text:00000001400AD9D3 ; ---------------------------------------------------------------------------
.text:00000001400AD9D3
.text:00000001400AD9D3 loc_1400AD9D3:                          
.text:00000001400AD9D3                                         
.text:00000001400AD9D3                 xor     al, al
.text:00000001400AD9D5                 retn
.text:00000001400AD9D6 ; ---------------------------------------------------------------------------
.text:00000001400AD9D6
.text:00000001400AD9D6 loc_1400AD9D6:                         _
.text:00000001400AD9D6                 mov     al, 1
.text:00000001400AD9D8                 retn
.text:00000001400AD9D8 _MmIsAddressValid endp

可以看出,函数也是将线性地址进行了拆分,取得表中每一项的值后,依次进行判断 ,主要是针对P位

将以上代码做一个等价还原后如下:
bool _MmIsAddressValid(void *pAddress)
{
        BYTE* pMl4e = (BYTE *)0x0FFFFF6FB7DBED000;
        BYTE* pDPTE = (BYTE *)0x0FFFFF6FB7DA00000;
        BYTE* pDE = (BYTE *)0x0FFFFF6FB40000000;
        BYTE* pTE = (BYTE *)0x0FFFFF68000000000;
        BYTE* curItem;

        ULONG_PTR ulAddress = (ULONG_PTR)pAddress;

        ulAddress = ((LONG_PTR)ulAddress>>48) + 1;

        if (ulAddress <= 1)                        //只要是合法地址,必然不会超过1
        {

                ulAddress = (ULONG_PTR)pAddress;
                ulAddress = ulAddress >> 39;
                ulAddress = ulAddress & 0x1ff;

                if ((pMl4e[ulAddress * 8] & 1) == 0)  //检测pMl4T项的P位
                        return false;

                ulAddress = (ULONG_PTR)pAddress;
                ulAddress = (ulAddress >> 30) << 3;
                ulAddress = ulAddress & 0x1FFFF8;
                if ((pDPTE[ulAddress] & 1) == 0)  //检测PDPT项的P位
                        return false;

                ulAddress = (ULONG_PTR)pAddress;
                ulAddress = (ulAddress >> 21) << 3;
                ulAddress = ulAddress & 0x3FFFFFF8;
                curItem = pDE + ulAddress;

                if ((*(LONG_PTR*)curItem & 1) == 0)  //检测PDT项的P位
                        return false;
                if ((*(LONG_PTR*)curItem & 0xff) >= 0)    //PDT的PSE被置位,直接返回TRUE (2mb分页)
                {
                        ulAddress = (ULONG_PTR)pAddress;
                        ulAddress = (ulAddress >> 12) << 3;
                        ulAddress = ulAddress & 0x7FFFFFFFF8;
                        curItem = pTE + ulAddress;
                        if ((*(LONG_PTR*)curItem & 1) == 0)  //检测PT项的P位
                                return false;

                        char itemTmp = (char)*(LONG_PTR*)curItem;
                        itemTmp = itemTmp & 0x80;
                        return (itemTmp == (char)0x80) ? false : true; //检测PT项的PAT位
                }else
                        return true;
        }
                return false;
}       

语言粗糙,有的是个人感悟,并不是官方解释,更多是从编程的角度去理解,
发出来和大家共享一下心得


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 8
支持
分享
最新回复 (15)
雪    币: 96
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
留名,下班看,精华必看
2015-10-21 23:51
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
先留名,以后慢慢看
2015-10-22 07:33
0
雪    币: 276
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
好文章,学习加支持
2015-10-22 14:09
0
雪    币: 272
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
谢谢分享
2015-10-22 15:28
0
雪    币: 6526
活跃值: (3661)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
精华文章, 技术文, 感谢分享
2015-10-22 17:46
0
雪    币: 96
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
小菜初学,没学过内核,但是通过Lz的文章也搞明白了线性地址转换成物理地址原理
看完后有几个问题:
1. “是否开启PSE,是的话直接返回真”,为什么开启pse后就不检查P位的了,直接返回真了?
2. 关于楼主用的windbg显示出来的数据很奇怪(小菜没有用过windbg),比如PML4T内存中的数据,
   都不是4K对齐的,而是需要人工去对齐下,os为什么不自动对齐了,有一个867的偏移?
3. 按照楼主所说,其实64位系统寻址范围不是2^64而是512*512*512*512 *4096?512 = 4096/8
4. 谢谢楼主,之前没见过64位汇编,涨姿势了
2015-10-22 21:57
0
雪    币: 581
活跃值: (215)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
8
1.如果PDE中的PSE标志位置位1,则意味着低21位地址空间的所有地址作为OFFSET去运算。PSE位的意思就是Page Size Extension, 如果默认0,以Page Size大小计算偏移,为1,则可以扩展到更大的大小。
2.再说这个windbg命令,那个!dq取出来的只是个值,用来计算地址的,它的低n为有具体的标志位意义,也就是上图那张表中的含义。
2015-10-23 10:44
0
雪    币: 96
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
谢谢懂了
2015-10-23 10:56
0
雪    币: 22
活跃值: (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
留名,留名
2015-10-23 14:52
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢分享
2015-10-28 23:05
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
留名,慢慢看
2015-11-3 01:21
0
雪    币: 620
活跃值: (372)
能力值: (RANK:150 )
在线值:
发帖
回帖
粉丝
13
打扰了... 谢谢师傅
最后于 2018-9-7 12:38 被wjzid编辑 ,原因: 我错了...
2018-9-7 12:37
0
雪    币: 6166
活跃值: (4942)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
14
mark
2019-11-3 21:49
1
雪    币: 1067
活跃值: (627)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2021-10-28 12:15
0
雪    币: 288
活跃值: (282)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
没看明白和PAT位有什么关系,PAT位是用于控制内存缓存策略的一种机制,和是否有效有什么关系。。。
2023-12-15 21:09
0
游客
登录 | 注册 方可回帖
返回
//