patchguard v3

来源:互联网 发布:淘宝怎么取消小儿介入 编辑:程序博客网 时间:2024/04/28 22:24

纯理论的看完了三篇PG的文章,还没看代码,还没入手调试.第三代是vista的版本


win7 sp1的PG肯定跟uniformed的文章不一样了。回头再研究了。反正到win7的时候貌似已经没有人写一大堆文档在内核里对抗pg了,直接给nt打patch了= =


对于x64也是刚接触没几天,资料也不是很系统,还需要进一步学习。


原文见http://uninformed.org/?v=8&a=5&t=sumry


这个也可以参考 http://www.codeproject.com/Articles/28318/Bypassing-PatchGuard-3


PG3 


机制改进


1.有多条线路在系统启动是开启,有时有一条,有时两条或多条



2.PG的dpc异常不再直接由_C_specific_handler处理,而是一段汇编代码随机化了异常地址,异常状态码等内容,然后调用_C_specific_handler,不再让你轻松过滤。

;
; EXCEPTION_DISPOSITION
; KiCustomAccessHandler8 (
; /* rcx */ IN PEXCEPTION_RECORD ExceptionRecord,
; /* rdx */ IN ULONG64 EstablisherFrame,
; /* r8 */ IN OUT PCONTEXT ContextRecord,
; /* r9 */ IN OUT struct _DISPATCHER_CONTEXT* DispatcherContext
; );
KiCustomAccessHandler8 proc near
test [rcx+_EXCEPTION_RECORD.ExceptionFlags], 66h
loc_14009B4C7:
jnz short retpoint
rdtsc
; Randomize ExceptionInformation[ 1 ]
; ( This is the "referenced address" for
; an access violation exception.)
;
; ( Note that rax is not set to any
; specific defined value in this
; context. It depends upon the value
; that RtlpExecututeHandlerForException
; and by extension RtlDispatchException
; last set rax to. )
;rax 是一个不确定的值,被赋予给缺页异常的引用地址
mov [rcx+(_EXCEPTION_RECORD.ExceptionInformation+8)], rax
xor [rcx+(_EXCEPTION_RECORD.ExceptionInformation+8)], rdx
shr eax, 5
and eax, 70h
sub [r8+98h], rax


and edx, 7Fh
or edx, 0C0000000h
; Set ExceptionCode to a random value. The code
; always has 0xC0000000 set, and the lowest byte
; is always masked with 7F. This often results
; in an exception code that appears like a
; legitimate exception code.)
;设置ExceptionCode为一个随机值
mov [rcx+_EXCEPTION_RECORD.ExceptionCode], edx
lea rax, loc_14009B4C7+1
; Set ExceptionAddress to a bogus value. In this case,
; it is set to in the middle of an instruction. This
; may interfere with attempts to unwind successfully from
;the exception.
mov [rcx+_EXCEPTION_RECORD.ExceptionAddress], rax
; Set Context->Rip to the same
; bogus exception address value.
mov [r8+0F8h], rax
and qword ptr [r8+88h], 0
retpoint:
mov eax, 1
retn
KiCustomAccessHandler8 endp

3.SEH-less mode 
不依赖seh的模式,一段代码拷贝到内存中。作为定时器的context,然后初始化整个PG,这是传入dpc的context是一个正常的值。然而旧的seh模式依然保留

;IN PKDPC Dpc,rcx
    ;IN PVOID DeferredContext, rdx
    ;IN PVOID SystemArgument1, r8x
    ;IN PVOID SystemArgument2 r9x
    ;)


KiTimerDispatch proc near
pushf
sub rsp, 20h
mov eax, [rsp+28h+var_8]
xor r9d, r9d
xor r8d, r8d
mov [rsp+28h+arg_0], rax
; [rcx+40] -> PatchGuard Decryption Key
mov rax, [rcx+40h]
mov rcx, 0FFFFF80000000000h
xor rax, rdx
; Form a valid address for the PatchGuard context block by
; xoring the decryption key with the DeferredContext
; argument.

; PatchGuard context block = Key xor DeferredContext + 0FFFFF80000000000h
or rax, rcx
; Set the initial code for the stage 1 self-decrypting stub.
mov rcx, 8513148113148F0h
mov rdx, [rax]
mov dword ptr [rax], 113148F0h
xor rdx, rcx
mov rcx, rax
; Call the stage 1 self-decrypting stub.
call rax

add rsp, 20h
pop rcx
retn
KiTimerDispatch endp


只要patch到这段代码就可以对抗
4.混淆了的dpc调用栈
以前版本只要hook掉异常处理,然后执行一次unwind就可以返回到dpc routine的上一层继续执行,dpc随机call几个函数,必须知道确切unwind几层才能顺利回到dpc之外。

然而对抗这个问题的方法是容易的,只要我们判断上一层是dpc dispatcher函数的时候就可以返回
5.patchGuard开始关注更多的全局变量,比如PsInvertedFunctionTable


PG3 增加机制


1.KTIMER的DPC加密了


ULONGLONG Deobfuscated;
PKDPC RealDpc;
Deobfuscated = Timer->Dpc ^ KiWaitNever;
Deobfuscated = _rotl64(Deobfuscated, (UCHAR)KiWaitNever);
Deobfuscated = Deobfuscated ^ Timer;
Deobfuscated = _byteswap_uint64(Deobfuscated);
Deobfuscated = Deobfuscated ^ KiWaitAlways;
RealDpc = (PKDPC)Deobfuscated;


2.反调试


在代码中多出检查KdDebuggerNotPresent,一旦检测到则直接死循环


3.KeBugCheckEx保护


bsod后会清零线程栈。而且PG初始化会对KeBugCheckEx进行备份从而绕过钩子。也给bugcheck中调用到的DbgPrint打了patch


mov rax, [rbx+PATCHGUARD_CONTEXT.DbgPrint]
mov byte ptr [rax], 0C3h ; ’+’ ; ret


4.分两阶段解密PG CONTEXT


5 钩子检测


比如检测SwapContext的
cmp rdi, [rbx+PATCHGUARD_CONTEXT.SwapContext]
jnz short NotSwapContextExemption
cmp byte ptr [rdi], 0EBh ; ’d’ ; backward jmps (short)
jnz short NotSwapContextExemption
cmp byte ptr [rdi+1], 0F9h ; ’’
jnz short NotSwapContextExemption
cmp byte ptr [rdi-5], 0E9h ; ’T’ ; jmp (long)
jnz short NotSwapContextExemption
mov rcx, [rbx+PATCHGUARD_CONTEXT.EnlightenedSwapContext]
movsxd rax, dword ptr [rdi-4]
sub rcx, rdi
cmp rax, rcx
jz short BadSwapContextHook

绕过办法:


方法一 综合的seh拦截以及内存搜索


对于seh mode:(seh infomation 混淆,触发异常前调用栈随机化)
针对seh混淆,一个方法是hook KiGeneralProtectionFault,在未交由seh处理前那到执行权。
另一个方法:实际上真实的系统dpc函数能抛出的异常是有限的,这些“合法”异常一般都不带参数,而且它们的上一层就是dpc dispatcher,不会有任何调用栈混淆;
PG抛出的必定带两个随机化的参数。

针对调用栈的随机化,只要找出DPC dispatcher来就可以顺利返回继续执行,我们发一个dpc or 定时器栈回溯结合函数表就能找到函数开头。

另外似乎是可以通过patch KiCustomAccessHandler 绕过_C_specific_handler 这个还不太懂

对于seh less mode:采用内存搜索特征把他patch掉


方法二
hook dpc dispatcher
DeferredContext如果是非传统地址,就禁止掉,万事大吉



方法三:取消dpc
seh已经不是PG3的唯一通路,但是dpc是。

1.如何定位KiTimerTableListHead:特徵码定位/设置一个timer然后搜索链表头
2.timer->dpc是加密的,需要定位KiWaitAlways, KiWaitNever来解决

方法四:
Page-Table Swap 作者回回提这个,蛋疼。就是dpc一旦调用,就伪造页,切换cr3.


方法五
伪造dpc的函数表,这个PG2就有,微软竟然没防。只要引用了这个0x8513148113148F0字串的代码,就是dpc,就要patch他的seh,于是成功了。这比PG2的定位方法要简单多了。


方法六
MSR切换,这跟方法四是差不多的,不过这个是针对ssdt

原创粉丝点击