您的位置:首页 > 其它

X64下MmIsAddressValid的逆向及内存寻址解析

2016-06-27 09:55 337 查看
标 题: 【原创】X64下MmIsAddressValid的逆向及内存寻址解析
作 者: 普通朋友

时 间: 2015-10-21,20:03:52
链 接: http://bbs.pediy.com/showthread.php?t=205143
在内核编程时,经常会用到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;

}


jpg 改 rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: