【原创】内核内存空间中执行Ring3代码

很久很久以前,我系统还是XP的时候,自己的机子不知道因为卡巴还是蓝牙驱动的问题,每次对LoadLibraryA下断都会发现LoadLibraryA不是原本的LoadLibrary,而是只有一小段代码然后是一个inline hook的jmp,最出奇的是这个jmp的地址是大于0x80000000的,这导致我没法按Ctrl+F9执行到retn!(因为Ctrl+F9是单步执行,单步到80000000后就挂了)。

又有一段时间研究NTVDM.EXE,想知道DOS程序是怎么被执行的,发现是直接按DOS下的方法在00000000处开始映射内存(后来知道这是V86模式),只是中间又遇到了一个诡异的调用——我断下了一个CreateFileA调用,发现返回地址居然是大于80000000的!

当时以为这是从内核发来的调用,但现在看了驱动的书,写着“内核在没有被调试的时候不能下断,否则会BSOD”,想想当时调NTVDM在DOS代码中下断的时候运行到了就抛出了win32异常,想必DOS代码是ring0中执行的,所以不能下断,那么由此原因既然我断下了这个CreateFile,那么这个代码肯定是Ring3的。

这就奇怪了。如果它是Ring3,为什么能访问Ring0才能访问的后2GB内存;如果它是Ring0,为什么又能被OD断下来。

今天临睡看PDF版的《天书夜读》,在讲不可执行保护原理时提到了2GB违规的原理。本来是要说明页表中只有R/W位限制而没有执行限制而使黑客容易通过缓冲区溢出到堆里执行代码,但是没写清楚,让我以为可以随意jmp到内核里执行,就在虚拟机里用OD写了句Call到内核里的一个Ret——很显然失败了(不然就是大笑话了)。

但是提到的2GB违规由页表中U/S位控制,让我感到有点希望~于是通宵的研究就开始了。

网上找到一些文章,大都是说VA的C0000000地址是映射到进程的页表的,但是我怎么调怎么不对,后来了解到是XP内核开始支持超过4G的内存(2003就支持,XP是故意阉割了),也就是PAE页表模式,于是再搜索0xc0000000 pae关键词,第一篇文章就详细分析了非PAE和PAE模式中内存的映射方法,最后给出了公式

int   PDE = ((lVirtualAddress>>21)<<3) & 0x3FF8 + 0xC0600000;
int   PTE = ((lVirtualAddress>>12)<<3) & 0x7FFFF8 + 0xC0000000;

并给出WinDbg中打!pte可以得到PDE和PTE的地址。

于是开始试验:

在nt!NtTerminateProcess下断,OD打开任务管理器,随便终止个进程,这时系统被断下,且进程上下文是taskmgr.exe。

在内核里随便找个ret的语句,这里我找的是805c9bf9的ret 08,输入!pte 805c9bf9,输出

                    VA 805c9bf9
PDE at C0602010            PTE at C0402E48
contains 0000000000C1A167  contains 00000000005C9161
pfn c1a       -G-DA–UWEV   pfn 5c9       -G-DA–KREV

分别将PDE和PTE的R/W和U/S位置1:即输入eb c0602010 67和eb c0402e48 67。

检查无误后按g继续运行系统,在OD中找块空地,随便写点代码:

PUSH 0
PUSH 0
CALL 805C9BF9
NOP

把EIP改到这里,F8单步到Call,然后把眼睛闭上按下F8——成功了!

重新试一下,如果是F7步入,则反汇编会跳到空的地方,这时按F7 F8 F9均会说不知如何进行,估计是ReadProcessMemory看到指针在内核空间就直接返回了吧,和我当年碰到的那个LoadLibraryA情况相同,能步过但不能步入。

由此再次可见——在系统底层,没有什么是不可能的。

至于这个可行性的应用,我就不明言了,相信大家能想到的。

算是解开了一年多来的这个谜吧。

One Reply to “【原创】内核内存空间中执行Ring3代码”

Leave a Reply

Your email address will not be published. Required fields are marked *