BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / security / #4293同步于 2006/11/2
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Security机器人发帖

[原创]有关DEP(数据执行保护)的一点研究

flyingkisser
2006/11/2镜像同步3 回复
有关DEP(数据执行保护)的一点研究 by flyingkisser 06.10 国外在这个方面的研究已经比较成熟了,04年DEP刚出来没多久俄罗斯某黑客组织就宣称这个东西他们已经可以攻破 后来,美国的nologin组织也发了一篇paper,专门说如何绕过硬件DEP保护的。 而国内在此方面的讨论好像不多,似乎焦点上有人讨论过。 我这里主要说的是软DEP,非硬件DEP,所以深度上估计明显打了折扣。 不过没关系,软DEP讨论的好像真不多(或者是我错过了?),我就在此费话几句吧。 下面从以下4个问题开始进入这个话题。 1.什么是DEP 2.DEP能保护什么? 3.可以绕过DEP吗? 4.DEP在内核中到底是如何实现的? 1.什么是DEP DEP,全称是Data Excution Protection,中名叫数据执行保护,是xp+sp2,win2k03+sp1中加入的对内存的一种保护, 用来防止恶意程序对系统的攻击,如溢出。 现在只有两种设置方式, 一是只为系统关键进程和服务提供DEP保护,这也是默认选项 二是为所有程序和服务提供DEP保护,除去用户手动指定的程序 其设置在 我的电脑--属性--高级--性能 设置--数据执行保护 2.DEP能保护什么? 我觉得DEP就是专门为反溢出设计的,当然这么说有点狭隘了点,毕竟这种机制完善了cpu的内存管理机制。虽然 我觉得为每一个指令都进行内存页属性的检测会不会有点太费资源了。 一句话,DEP可以使指定的内存页不具有可执行属性。 这样一来,如果指定栈所在的内存页不可执行,那么,当我们要栈上溢出时,我们的Shellcode将难以被执行。 所以,DEP保护的就是内存,保护指定的内存上的代码不能被执行,这样就可以达到反溢出的目的。 当然,这是微软的一厢情愿罢了。因为越过这个限制也并不是一件难事。 3.可以绕过DEP吗?如果可以,如何绕过呢? 答案是肯定的。可以绕过,即使是硬件上的DEP保护。 具体方法也很容易想到。有一个API,改变指定内存页的属性的,VirtualProtect(),当然,还有其它的API,如 VirtualProtectEx(),ZwProtectVirtulMemory(),不过都是封闭在VirtualProtect()中。 所以,我们溢出的时候,把返回地址设计为这个API的地址,再精心构造一个栈为调用这个API的栈,就可以 改变当前栈的内存页的属性,使其从"不可执行"变成"可执行". 这个过程的示例如下面的一段代码 _test proc ;push 04 ;invoke VirtualProtect,esp,30h,PAGE_EXECUTE_READWRITE,esp ;pop eax mov dword ptr [esp+18h],4 mov eax,esp add eax,18h mov dword ptr [esp+14h],eax;lpOldProtection mov dword ptr [esp+10h],40h;dwNewProtection mov dword ptr [esp+0ch],30h;dwRegionSize mov dword ptr [esp+8],esp;dwStartAddress mov dword ptr [esp],VirtualProtect;func addr mov eax,esp add eax,1ch mov dword ptr [esp+4],eax;return address from VirtualProtect mov dword ptr [esp+1ch],90909090h;our shellcode ret _test endp 4.DEP在内核中到底是如何实现的? 这个问题我曾经费不了少时间去找答案,从内存操作的API上来看, 有标准内存管理API,虚拟内存管理API,堆管理API,内存映射文件API 从内存管理的结构上来看,有VAD,有PFN,有PTE,PDE,有段 一开始我认为windows可能会在任何一个层面上做文章,可能是VAD,也可能是PFN,也可能是PTE 并且我认为VAD的可能性比较大,因为PTE没有相关的bit位表示此页有没有可执行属性。PFN也没有。 VAD倒是有相关的位表示页的可执行属性。 经过一点点的测试和排除,发现VAD与此并没有关系,用VirtualProtect()改变页的属性时,此页对应 的VAD的flag位竟然毫无变化。 那么只剩下PFN和PTE了,发现这个API调用前后,PFN的restore pte这个字段有变化,PTE的低双字却 没有一点变化,高双字我时候我没有关心,我一直认为高双字是用于寻址4G以外的物理内存地址的。 然后我手动把PFN的restore pte改变成上面提到的API改成的值,但是结果却让人失望,我拷入shellcode 的页还是不可执行。 尽管这时,我还是没有意识到PTE的高双字发生的变化,并为此付出了代价,那就是二夜一天的对VirtualProtect() 相关API的反汇编。 VirtualProtect()调用了VirtualProtectEx() VirtualProtectEx()调用了ZwProtectVirtualMemory() ZwProtectVirtualMemory()通过sysenter进入内核,EAX中存放的服务号是0x89,对应的服务是NtProtecVirtualMemory() NtProtectVirtualMemory()又调用了MiProtectVirtualMemory() 在MiProtectVirtualMemory()内部,计算出要改变其属性的内存页的PTE的地址,新的属性,然后调用 MiFlushTbAndCapture()改变PTE的属性,但是我当时也只是看到把PTE的属性从067变成了027,和可执行属性还是 没有关系,然后我再深入到MiFlushTbAndCapture()中,发现它主要又是调用KeFlushSingleTb()来改变指定PTE 的属性的,深入到KeFlushSingleTb()内部,它主要是调用KeInterlockedSwapPte()来改变指定PTE的属性。 而KeInterlockedSwapPte()的内部是比较简单的,来看看它的反汇编代码: nt!KeInterlockedSwapPte: 80541c08 53 push ebx 80541c09 56 push esi 80541c0a 8b5c2410 mov ebx,dword ptr [esp+10h] ;ebx=新的PTE值所在变量的地址 80541c0e 8b74240c mov esi,dword ptr [esp+0Ch] ;esi=PTE地址 80541c12 8b4b04 mov ecx,dword ptr [ebx+4] ;ecx=新PTE值高双字 80541c15 8b1b mov ebx,dword ptr [ebx] ;ebx=新PTE值低双字 80541c17 8b5604 mov edx,dword ptr [esi+4] ;edx=旧PTE值高双字 80541c1a 8b06 mov eax,dword ptr [esi] ;eax=旧PTE值低双字 80541c1c 0fc70e cmpxchg8b qword ptr [esi] 看到关键点了吧,就是一个cmpxch8b指令,这个指令是干什么的呢? 执行的操作:edx,eax与DST相比较 如果 (edx,eax)=(dst) 则 ZF=1,(dst)<-(ecx,ebx) 否则 ZF=0,(edx,eax)<-dst 很简单,如果新的PTE属性和旧的不等,把PTE属性设置为新的属性。如果相等,则实际上等于不进行操作。 从这里我才发现,他把PTE的高双字设置为0了,以前的值是0x80000000. 所以,DEP是通过PTE的高双字的最高bit即bit63来实现的,这个位置位了,表示此页不可执行。没有置位, 表示此页可以执行。而win2k下面的页目录和页表项只有32个bit,所以不可能提供DEP的这个保护位,因此 DEP只有在64bit的PTE上才能实现。而只有cr4的bit5即PAE启用的时候,PTE才为64bit。 所以,可以这么说吧,intel的奔腾CPU是早就有PAE属性位的,可以支持64位的页表项,而windows系统 只有win2k的某个版本,winxp和win2003的某个server pack版本的内核才支持64的页表项。 不管怎样,PTE的第63位控制着页的可执行属性,我后来查了IA,在IA的修正说明中,才提到这一点,并把这个位叫做EXB,却对此位的作用一字为提。 但是我们是不能在ring3下操作PTE的,所以,绕过DEP还是得用return-to-lib的经典方式返回到VirtualProtect()来改变当前栈的属性。 一点补充: 进程对象结构体EPROCESS的PCB成员的最后一个字节Flags,这个Flags的类型是 _KEXECUTE_OPTIONS 具体定义是: kd> dt _KEXECUTE_OPTIONS +0x000 ExecuteDisable : Pos 0, 1 Bit +0x000 ExecuteEnable : Pos 1, 1 Bit +0x000 DisableThunkEmulation : Pos 2, 1 Bit +0x000 Permanent : Pos 3, 1 Bit +0x000 ExecuteDispatchEnable : Pos 4, 1 Bit +0x000 ImageDispatchEnable : Pos 5, 1 Bit +0x000 Spare : Pos 6, 2 Bits 和DEP相关的是前2位,当前2位为二进制10,即这个字节为2时,就可以绕过DEP了。 当启用DEP时,被DEP保护的进程的EPROCESS.Pcb.Flags为0,不可执行的页的PTE的 bit63为1 当把这个Flgas改为2,而PTE不做改变时,当系统执行到不可以执行的内存时, 引起内存读写异常,内核的MmAccessFault接管这个异常, 我简单得跟了一下这个内核函数,在这个内核函数里,如果发现引起异常的进程的 EPROCES.Pcb.Flag为02,就会把当引起异常的内存页的PTE的bit63设置为0,然后 重新去执行引起异常的指令。
订阅后,新回复会通过你的通知中心匿名送达。
3 条回复
rebirthatsix机器人#1 · 2006/11/3
猫哥,今年大会上提及了硬件DEP,但是没有谈如何攻破 日后攻破这个必然是主流趋势
flyingkisser机器人#2 · 2006/11/3
硬DEP没有条件啊,所以不好去研究。不过MS已经被攻破了, 我D了相关的PAPER,还没有来得及看,暂时也不打算去看。 另外软DEP还有一些其它的东西,如EPROCESS.PCB.FLAG的 ExecutionEnable,ExecutionDiable也和DEP有关系,在这两 位上改动改动系统会自动更改PTE的属性,目前还不知道 内核在这一块上是如何工作的,有待研究。 下个内存断点看看去。。。。 【 在 rebirthatsix (茫犭者) 的大作中提到: 】 : 猫哥,今年大会上提及了硬件DEP,但是没有谈如何攻破 : 日后攻破这个必然是主流趋势
rebirthatsix机器人#3 · 2006/11/3
唉,确实木条件