首页
论坛
课程
招聘
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝

[调试逆向] [原创][原创]用DEP实现BreakOnExecute

2009-7-3 21:27 14178

[调试逆向] [原创][原创]用DEP实现BreakOnExecute

2009-7-3 21:27
14178
用DEP实现BreakOnExecute

以前抄过OllyBone,但不大好用,很多时候停不下来,似乎在PTE上做的手脚没起作用。后来看到
DEP,这不就是我想要的功能么,又重新试了一次。

打开DEP后,系统工作在PAE下,PTE为64位。不过改了以后,还是存在拦不住的情况,用SoftIce跟了一下,
PTE的确被动过。Hook KeInterlockedSwapPte就可以了,估计OllyBone不好用也是同样道理。

前段时间写的东西,细节已经记不起来了。反正代码都贴出来了。功能跟OllyBone一样的。在xp sp2/sp3下测试可以用。

#define MiGetPteAddressPAE(va)   ((PMMPTE)(PTE_BASE + ((((ULONG)(va)) >> 12) << 3)))

typedef struct _HARDWARE_PTE_X86PAE {
    union {
        struct {
        ULONGLONG Valid : 1;
        ULONGLONG Write : 1;
        ULONGLONG Owner : 1;
        ULONGLONG WriteThrough : 1;
        ULONGLONG CacheDisable : 1;
        ULONGLONG Accessed : 1;
        ULONGLONG Dirty : 1;
        ULONGLONG LargePage : 1;
        ULONGLONG Global : 1;
        ULONGLONG CopyOnWrite : 1; 	// software field
        ULONGLONG Prototype : 1;   	// software field
        ULONGLONG reserved0 : 1;  	// software field
        ULONGLONG PageFrameNumber : 26;
        //ULONGLONG reserved1 : 26;  	// software field
	ULONGLONG reserved1 : 25;  	// software field
	ULONGLONG ExecuteDisable : 1;	// 这个
        };
        struct {
            ULONG LowPart;
            ULONG HighPart;
        };
    };
} HARDWARE_PTE_X86PAE, *PHARDWARE_PTE_X86PAE;


VOID HookMemory(ULONG address, ULONG size)
{
	ULONG i;

	g_HookMem = TRUE;
	g_HookStart = address;
	g_HookEnd = address + size;

	g_PteStart = (ULONG)MiGetPteAddressPAE(g_HookStart);
	g_PteEnd   = (ULONG)MiGetPteAddressPAE(g_HookEnd);

	//DbgPrint("PteStart = %08X, PteEnd = %08X", g_PteStart, g_PteEnd);
	
	KeAttachProcess(g_ObjProc);

	for (i = address; i < address+size; i += PAGE_SIZE) //4KB Pages only
		HookPage((PUCHAR)i);
	
	KeDetachProcess();
}


VOID HookPage(PUCHAR Page)
{
	
	PHARDWARE_PTE_X86PAE PointerPte;
		
	
	__try {

		__asm {	
			mov   eax, Page	
			mov   eax, [eax]
		}
		
		PointerPte = (PHARDWARE_PTE_X86PAE)MiGetPteAddressPAE(Page);

		if (PointerPte->Valid == 1) {
			PointerPte->ExecuteDisable = 1;
			DbgPrint("HookPage = %08X", Page);
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER) {
		DbgPrint("Exception captured while hooking page");
	}
}


VOID UnhookPage(PUCHAR Page)
{
	PHARDWARE_PTE_X86PAE PointerPte;

	__try {
		
		__asm {
			mov  eax, Page	
			mov  eax, [eax]
		}

		PointerPte = (PHARDWARE_PTE_X86PAE)MiGetPteAddressPAE(Page);

		if (PointerPte->Valid == 1) {
			PointerPte->ExecuteDisable = 0;
		}
	} 
	__except(EXCEPTION_EXECUTE_HANDLER) {
		DbgPrint("Exception captured while unhooking page");
	}
}


VOID UnhookMemory(ULONG address, ULONG size)
{
	ULONG i;

	KeAttachProcess(g_ObjProc);
	
	for (i = address; i < address+size; i += PAGE_SIZE)
		UnhookPage((PUCHAR)i);
	
	KeDetachProcess();
}


NTSTATUS FindSwapPte(ULONG ntosbase, PULONG result)
{
	//
	// Search KeInterlockedSwapPte in kernel space:)
	// there are some lock cmpxchg8b, but this is the only one followed 
	// by a jnz 
	//

	UCHAR code[] = { 0xF0, 0x0F, 0xC7, 0x0E,	// lock cmpxchg8b qword ptr [esi]
			 0x75, 0xFA,			// jnz     short loc_46EC60
			 0x5E,				// pop     esi
			 0x5B,				// pop	   ebx
			 0xC2, 0x08, 0x00		// retn    8
			};


	NTSTATUS status = STATUS_UNSUCCESSFUL;
	PIMAGE_DOS_HEADER mz;
	PIMAGE_NT_HEADERS pe;
	ULONG current, end, i;
	SIZE_T size = sizeof(code);
	
	mz = (PIMAGE_DOS_HEADER)ntosbase;
	pe = (PIMAGE_NT_HEADERS)((ULONG)ntosbase + mz->e_lfanew);

	*result = 0;
	current = ntosbase;
	end = ntosbase + pe->OptionalHeader.SizeOfImage - sizeof(code);

	

	__try {
		while (current < end) {

			if (RtlCompareMemory((PVOID)current, code, size) == size) {
			
				
				// WARNING!
				// the hook func CAN'T work in Win2K3! refer to wrk,
				// the ULONG64 was pushed in stack instead of the pointer
				//

				for (i = 0; i < 32; i++) {

					if (*(PUSHORT)(current - i) == 0x5653) {
						*result = current - i;
						
						DbgPrint("KeInterlockedSwapPte = %08X", *result);
						status = STATUS_SUCCESS;
						break;
					}
				}
			}
			current++;
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER) {
		DbgPrint("Failed to retrive KeInterlockedSwapPte");
		*result = 0;
		return STATUS_FAIL_GET_KESWAPPTE;
	}

	return status;
}


VOID HookKeInterlockedSwapPte()
{
	
	ULONG  func = g_KeInterlockedSwapPte;
	ULONG  newlowpart, newhighpart;
	UCHAR  opcode[8] = {0x90};

	opcode[0] = 0xE9;
	*(PULONG)&opcode[1] = (ULONG)KeInterlockedSwapPte - (func+5);

	newlowpart  = *(PULONG)&opcode[0];
	newhighpart = *(PULONG)&opcode[4];

	DisableWP
	
	
	__asm {
		pushad

		mov   esi, func
		
		mov   edi, offset g_SavedSwapPte
		mov   edx, [esi+4]
		mov   [edi+4], edx  // save the old opcode
		mov   eax, [esi]
		mov   [edi], eax

		mov   ecx, newhighpart
		mov   ebx, newlowpart

__swapagain:
		lock  cmpxchg8b qword ptr [esi]
		jne   __swapagain	
		
		popad
	}

	EnableWP
		
	DbgPrint("Hook KeInterlockedSwapPte OK");
}


VOID __declspec(naked) KeInterlockedSwapPte()
{

	//
	// 这里的代码适用于xp,WRK压栈的是ULONG64,不是指针,要修改
	//

	// 	; ULONGLONG
	// 	; InterlockedExchangePte (
	// 	;     IN OUT PMMPTE Destination,
	// 	;     IN ULONGLONG Exchange
	// 	;     )
	// 
	// 	push    ebx
	//  	push    esi
	// 	mov     ebx, [esp] + 16         ; ebx = NewPteContents lowpart
	// 	mov     ecx, [esp] + 20         ; ecx = NewPteContents highpart
	// 	mov     esi, [esp] + 12         ; esi = PtePointer


	__asm 
	{
		push    ebx
		push    esi
		mov     ebx, dword ptr [esp+10h]	// ebx = pointer to Exchange
		mov     esi, dword ptr [esp+0Ch]	// esi = PtePointer
		mov     ecx, dword ptr [ebx+4]		// ecx = NewPteContents highpart
		mov     ebx, dword ptr [ebx]		// ebx = NewPteContents lowpart
		mov     edx, dword ptr [esi+4]		// edx = OldPteContents highpart
		mov     eax, dword ptr [esi]		// eax = OldPteContents lowpart, return old contents in edx:eax
			
	__swapagain:

		lock	cmpxchg8b qword ptr [esi]
		jne	__swapagain			// if z clear, exchange failed
			
		pushad					// if debuggee, fuck up nx
		call    dword ptr [IoGetCurrentProcess]
		cmp	eax, g_ObjProc
		jne	__end

		cmp	esi, g_PteStart
		jb	__end
		cmp	esi, g_PteEnd
		ja	__end

		or	dword ptr [esi+4], 80000000h  //管用的就是这句

	__end:
		popad
		pop     esi
		pop     ebx
		ret     8
	}
}


volatile __declspec(naked) void NewInt0E_PAE()
{
	// 
	//	- Interrupt 0E Handler -
	//
	//  offset   | contains
	//  ---------+-----------------------------
	//  esp      : Error Code
	//  esp + 4  : EIP Context
	//  esp + 8  : CS  Context
	//  esp + C  : EFLAGS
	//

	__asm 
	{
		pushad
		push    fs
		push    ds
		push    es
		mov     eax, 30h
		mov     fs, ax
		mov     eax, 23h
		mov     ds, ax
		mov     es, ax

		call	dword ptr [IoGetCurrentProcess]
		cmp	eax, g_ObjProc 		// debuggee?
		jne	__oldint0e
			
		mov	eax, [esp+20h+0Ch]	// error code
		test	eax, 1			// not present
		je	__oldint0e

		test	eax, 4
		je	__oldint0e		// skip kernel mode page fault

			
		cmp	g_HookMem, 0
		je	__oldint0e

		mov	eax, cr2		// the faulting address
		cmp	eax, g_HookStart
		jb	__oldint0e

		cmp	eax, g_HookEnd
		jae	__oldint0e

		cmp	eax, [esp+20h+0Ch+4]	// eip
		jne	__oldint0e		// because of execution
			
		pop     es
		pop     ds
		pop     fs
            	popad
		add	esp, 4			// discard error code
		jmp	g_KiTrap01		// fuck up int1

__oldint0e:
		pop     es
          	pop     ds
	        pop     fs
	        popad
        	jmp	g_KiTrap0E
			
	}
}

最后感谢风月,把他的VMX代码给我。有时间再学习啦,我现在时间都耗在聊天上了哈。

[公告]看雪论坛2020激励机制上线了!多多参与讨论可以获得积分快速升级?

最新回复 (16)
雪    币: 25
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
xIkUg 活跃值 9 2009-7-3 22:06
2
0
丁页你。。。坐了一盘沙发
雪    币: 750
活跃值: 活跃值 (17)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
海风月影 活跃值 17 2009-7-3 23:50
3
0
看不懂啊,西裤哥坐沙发教教我
雪    币: 105
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
你猜 活跃值 2009-7-4 00:02
4
0
我是进来膜拜的.
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
softworm 活跃值 30 2009-7-4 00:06
5
0
嗯,是我写得太潦草了,再加几句说明:

跟OllyBone要做的事情一样,只是用的PTE的NX位,需要打开PAE。一般机器的DEP都是开着的,就在PAE下。
但做的标记会被清除掉,所以挂KeInterlockedSwapPte,在这把标记再置回去。大概就这意思。

deroko以前的DOER,也不好用。后来的xTracer更好,但我没读过。
雪    币: 310
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
stalker 活跃值 8 2009-7-4 16:37
6
0
SM大叔的帖子看不懂
雪    币: 95
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
假面泰山 活跃值 2009-7-5 14:33
7
0
看的头晕 虚心学习一下  楼主您辛苦了。
雪    币: 402
活跃值: 活跃值 (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Genius 活跃值 2009-7-5 14:42
8
0
学习中。。。。。
雪    币: 945
活跃值: 活跃值 (812)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2009-7-5 14:47
9
0
softworm的帖子是要收藏的
雪    币: 259
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
wuhanqi 活跃值 5 2009-7-6 13:56
10
0
看不懂的说啊...
雪    币: 290
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
狼行wolf 活跃值 2009-7-6 13:58
11
0
一点看不懂,哎,差距真大啊。。
雪    币: 1224
能力值: (RANK:680 )
在线值:
发帖
回帖
粉丝
jackozoo 活跃值 14 2009-7-6 15:16
12
0
强帖, 收藏了~~
雪    币: 278
活跃值: 活跃值 (15)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
zhuwg 活跃值 11 2009-7-7 19:26
13
0
跟着膜拜  看不懂。。学习

google了一个参考资料 http://www.flyingkisser.com/articledisplay.php?aid=6
有关DEP(数据执行保护)的一点研究
2006-11-02 flyingkisser
国外在这个方面的研究已经比较成熟了,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.可以绕过DE吗?如果可以,如何绕过呢?
答案是肯定的。可以绕过,即使是硬件上的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()来改变当前栈的属性。


雪    币: 221
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
himcrack 活跃值 6 2009-7-9 00:29
14
0
标记一下 方便日后膜拜 可能再出现个插件不?或者被SOD吸收?
雪    币: 44
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bozer 活跃值 2009-7-15 00:48
15
0
__asm {  
      mov   eax, Page  
      mov   eax, [eax]
    }
请问这里的[eax]送给eax,起到什么作用?
雪    币: 1470
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
bithaha 活跃值 5 2009-7-15 01:34
16
0
只能膜拜
无法学习
雪    币: 312
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
aa1ss2 活跃值 2 2009-7-18 19:26
17
0
[QUOTE=bozer;655639]__asm {  
      mov   eax, Page  
      mov   eax, [eax]
    }
请问这里的[eax]送给eax,起到什么作用?[/QUOTE]

测试是否有效指针~
游客
登录 | 注册 方可回帖
返回