patchGuard v2

来源:互联网 发布:网络主题团日活动 编辑:程序博客网 时间:2024/06/10 06:05
算是对http://www.uninformed.org/?v=6&a=1&t=sumry 的一个中文摘要,理解还不一定全对


PG2一些改动:


cmp cs:KdDebuggerNotPresent, r12b
jnz short continue_initialization_1
infinite_loop_1:
jmp short infinite_loop_1
sti


更改KdDebuggerNotPresent比较容易,索性直接死循环掉


dr7清零






初始化完毕之后会把这些初始化代码清零


此外不变的是对于自己的数据还是使用了随即内存大小、pool tag,随即密钥等等加密。利用DPC来触发异常,不过可供选择的DPC增加到了十个。




方法一


1.patch guard2也是利用DPC执行一次检查,这些DPC routine都是正常dpc历程,但是传入的DPC context并不是一个有效指针。


所以会转到异常处理_C_specific_handler,patchguard检测函数将在这个异常里无限循环检测patch


bypass的话就可以hook _C_specific_handler这个导出函数


hook之后怎么正常返回到dpc routine的调用位置继续执行是个问题


x86的话会比较麻烦,利用X64的seh表是轻而易举的,调用RtlVirtualUnwind即可


2.这个hook只执行一次,定时器只触发一次,然后就走到异常处理之后无限循环检查


3.接下来的问题是如何过滤异常时有patchguard故意抛出的


内核代码经常会probe一些地址,这有可能引发常规的access violation,但一般probe的都是环3地址,PatchGuard则例外


PG则是引用了non-canonical addresses,处理器抛出这个异常的时候他的exception handler总是-1。


一些写的不规范的驱动可能也会出发这个异常,作者认为可以无视,我想判断也很容易吧,反正有上下文。


方法二


修改所有可能用到的DPC的异常处理注册函数


这需要定位是哪些DPC


DPC该有如下特征


1. Each DPC routine has one exception/unwind-marked registration with
_C_specific_handler.


每条DPC在PE的seh表中有一个handler注册为_C_specific_handler


2. Each DPC routine has exactly four _C_specific_handler scopes.


每个DPC对应_C_specific_handler的scopes_table有四条scopes


3. Each DPC routine is referenced in raw address form (64-bit pointer) in
the executable code sections comprising ntoskrnl at least twice.


dpc的地址被内核代码引用至少两次(这是通过PE的重定位信息获取的吗?)


4. Each DPC routine has at least two _C_specific_handler scopes with an
associated unwind/exception handler.


每个DPC至少被注册了两个_C_specific_handler


5. Each DPC routine has exactly one _C_specific_handler scope with a
call to a common subfunction that references RtlUnwindEx (an exported
routine).


每个_C_specific_handler的scope只有一个子函数调用了RtlUnwindEx(用来展开函数的)


6. Each DPC routine has several sets of distinctive, normally rare instruc-
tions (ror/rol instructions).


dpc有解密指令


通过这些特征可以在非换页内存区搜索到十个可能被PG用到的dpc


1: kd> x nt!*DpcRoutine
fffff800`03f7ff40 nt!WheapWorkQueueDpcRoutine = <no type information>
fffff800`0435ddc0 nt!ViWdIrpTimerDpcRoutine = <no type information>
fffff800`03f7fd00 nt!CmpLazyCommitDpcRoutine = <no type information>
fffff800`03f7ffc0 nt!WheapDeferredRecoveryServiceDpcRoutine = <no type information>
fffff800`03f84640 nt!TmpTransactionThawDpcRoutine = <no type information>
fffff800`03ec0464 nt!EtwpAdjustBuffersDpcRoutine = <no type information>
fffff800`03e77800 nt!CmpLazyFlushDpcRoutine = <no type information>
fffff800`03e5b670 nt!ExpTimeRefreshDpcRoutine = <no type information>
fffff800`03f83fa0 nt!PspRateControlTimerDpcRoutine = <no type information>
fffff800`03f801b0 nt!ExpWorkerFactoryThreadCreationDpcRoutine = <no type information>
fffff800`03fd0c60 nt!PfpPowerActionDpcRoutine = <no type information>
fffff800`03eb0a14 nt!CmpDelayDerefKCBDpcRoutine = <no type information>
fffff800`03fadb90 nt!PspCpuQuotaDpcRoutine = <no type information>
fffff800`03f7f340 nt!ExpNextYearDpcRoutine = <no type information>
fffff800`03f7f4f0 nt!ExpTimeZoneDpcRoutine = <no type information>
fffff800`03f7f740 nt!PspJobTimeLimitsDpcRoutine = <no type information>
fffff800`0434bb70 nt!KdpTimeSlipDpcRoutine = <no type information>
fffff800`03e77798 nt!CmpDelayFreeRMDpcRoutine = <no type information>
fffff800`03fc7390 nt!ExpTimerDpcRoutine = <no type information>
fffff800`03f7fd30 nt!CmpFreezeThawDpcRoutine = <no type information>
fffff800`03e6df90 nt!CmpEnableLazyFlushDpcRoutine = <no type information>
fffff800`03e6d390 nt!PfSnTracingStateDpcRoutine = <no type information>
fffff800`03f7f370 nt!ExpCenturyDpcRoutine = <no type information>
fffff800`03e4d3e4 nt!TmpCheckForProgressDpcRoutine = <no type information>
fffff800`03e651a8 nt!ApphelpServiceTimeoutDpcRoutine = <no type information>


在做seh table hook时,要注意pe文件里面的unwind data 的rva是32位的,所以hook指向的位置只能是nt+4GB的范围内


相比方法一这个太复杂了。。。


方法三 
异常分发器对于函数表的查询是有缓存的,这个缓存是nt!PsInvertedFunctionTable,我们伪造缓存也能达到异常处理重定向的功能

函数缓存表的结构可以查看wrk。这个方法不再受到pe函数表中32位rva的4gb寻址限制,但是还是要特征搜索DPC

方法四
x64环境下,调试异常触发后根据IDT走到KiDebugTrapOrFault。反正是说利用硬件断点断下PG任何一个执行流程都可以xx,无限可能。


方法五
之前说到PG会对dr7清零,如果在此之前对dr7设置General Detect位,将在代码访问调试寄存器之后发生中断,所以我们可以获得pg的执行权

方法六
    hook dpc dispatcher,这个超容易,查一个dpc,从dpc函数回溯,查一下函数表,成功了。hook之后PG的dpc当然没机会执行了。
方法七
搜索PG的dpc KTIMER,KDPC (作者之前说dpc是一次性触发然后进入一个无限循环的,现在又改成说是定期触发,神马意思?如果是一次性触发,那也搜不到,搜到也没意义了。)

搜索条件,了解了pg的特性,其实不说也知道了

1. The memory is a valid KTIMER object. This means that the linked
list entries should be valid, and point to other seemingly valid KTIMER
objects (or the list head), and that the type field of the KTIMER is
consistent with a timer object.

2. The timer should have a timer interval in the range of several minutes.
PatchGuard applies a randomized fuzz factor to the timer interval (within
a small range), but verifying that the range of the timer is no more than
several minutes (say 7 or 8) should be an ample sanity check.
3. The timer should have a KDPC associated with it (and the pointer should
be valid non-paged pool).
4. The associated KDPC should have the appropriate type field.
5. The associated KDPC should have a DPC routine that is within the con-
fines of the kernel image in-memory.
6. The associated KDPC should have a DeferredContext value that is a non-
canonical kernel address.
方法八
利用TLB隐藏自己的hook,这个不算bypass,是hide


方法九

hook 那些DPC

这篇有60多页。。。去看PG3了,看完再调试一下

原创粉丝点击