-
-
[原创]摘微过滤驱动回调的研究-续
-
2022-9-30 12:21
7167
-
摘微过滤驱动回调的研究-续
问题
自上回发现对象引用计数后,再次在Win10上进行测试,发现大概率又卡死在ExWaitForRundownProtectionRelease函数。因为只有引用计数在调用这个函数之前不为0,就会进入等待。那么PCHunter是怎么处理引用计数的呢?Why not reverse it?
逆向PCHunter驱动
首先我们通过WinArk设置一个镜像回调拦截驱动加载。这里感谢砍酱的提醒,如果PCHunter已经启动过了,那么pchunter关闭的时候不会卸载驱动。所以需要手动卸载后再拦截。

拦截到驱动后

拖入IDA,上ret-sync。
1 2 3 | .load F:\github\ret - sync\ext_windbg\sync\x64\Release\sync.dll
!sync
bp fltmgr!FltUnregisterFilter
|
我们使用一个DelProtect.sys,作为测试驱动,然后依旧选择移除过滤器,函数断下
1 2 3 4 | 0 : kd> g
Breakpoint 0 hit
FLTMGR!FltUnregisterFilter:
fffff804` 73eed6b0 48895c2418 mov qword ptr [rsp + 18h ],rbx
|
此时我们通过下面的指令查看当前线程函数入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 0 : kd> !thread @$thread
THREAD ffffcf8f7fc74080 Cid 0004.03c4 Teb: 0000000000000000 Win32Thread: 0000000000000000 RUNNING on processor 0
Not impersonating
DeviceMap ffffa88a30e14c00
Owning Process ffffcf8f7bc8d300 Image: System
Attached Process N / A Image: N / A
Wait Start TickCount 0 Ticks: 76618 ( 0 : 00 : 19 : 57.156 )
Context Switch Count 1 IdealProcessor: 1
UserTime 00 : 00 : 00.000
KernelTime 00 : 00 : 00.000
Win32 Start Address PCHunter64as ( 0xfffff80477666a18 )
Stack Init ffffbb8c5239dc90 Current ffffbb8c5239dc20
Base ffffbb8c5239e000 Limit ffffbb8c52398000 Call 0000000000000000
Priority 8 BasePriority 8 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child - SP RetAddr : Args to Child : Call Site
ffffbb8c` 5239dbd8 fffff804` 77666a4b : 00000000 ` 00000000 ffffcf8f` 7bc8d300 ffffcf8f` 7eb43180 00000000 ` 00000192 : FLTMGR!FltUnregisterFilter
ffffbb8c` 5239dbe0 fffff804` 711c3d65 : ffffcf8f` 7fc74080 fffff804` 77666a18 ffffcf8f` 805a4bc0 00000000 ` 00000001 : PCHunter64as + 0x16a4b
ffffbb8c` 5239dc10 fffff804` 71268a8c : fffff804` 6f89b180 ffffcf8f` 7fc74080 fffff804` 711c3d10 ffffcf8f` 7cd85870 : nt!PspSystemThreadStartup + 0x55
ffffbb8c` 5239dc60 00000000 ` 00000000 : ffffbb8c` 5239e000 ffffbb8c` 52398000 00000000 ` 00000000 00000000 ` 00000000 : nt!KiStartSystemThread + 0x1c
|
可以看到函数地址为0xfffff80477666a18
在IDA中转到

通过交叉引用,可知此为r0接口处理处

再往上走就是其他一堆功能函数了

继续分析,我们对这个功能函数下断,从头跟踪一次,因为FltUnregisterFilter
已经走到很后面了。
我们使用命令查看返回地址之前的反汇编
1 2 3 4 5 6 7 8 9 10 | 0 : kd> ub PCHunter64as + 0x16a4b
PCHunter64as + 0x16a34 :
fffff804` 77666a34 488bd8 mov rbx,rax
fffff804` 77666a37 e89cf60000 call PCHunter64as + 0x260d8 (fffff804` 776760d8 )
fffff804` 77666a3c 4885ff test rdi,rdi
fffff804` 77666a3f 740a je PCHunter64as + 0x16a4b (fffff804` 77666a4b )
fffff804` 77666a41 4885c0 test rax,rax
fffff804` 77666a44 7405 je PCHunter64as + 0x16a4b (fffff804` 77666a4b )
fffff804` 77666a46 488bcf mov rcx,rdi
fffff804` 77666a49 ffd0 call rax
|
由此rax指向的函数就是FltUnregisterFilter
,线程函数的context传入的是PFLT_FILTER
。
继续分析这里面还有一个rbx指向的函数指针。
1 2 3 4 5 6 7 8 9 10 | 0 : kd> u rbx
nt!PsTerminateSystemThread:
fffff804` 7173cca0 4883ec28 sub rsp, 28h
fffff804` 7173cca4 8bd1 mov edx,ecx
fffff804` 7173cca6 65488b0c2588010000 mov rcx,qword ptr gs:[ 188h ]
fffff804` 7173ccaf f7417400040000 test dword ptr [rcx + 74h ], 400h
fffff804` 7173ccb6 0f84bc651200 je nt!PsTerminateSystemThread + 0x1265d8 (fffff804` 71863278 )
fffff804` 7173ccbc 41b001 mov r8b, 1
fffff804` 7173ccbf e88cfa0000 call nt!PspTerminateThreadByPointer (fffff804` 7174c750 )
fffff804` 7173ccc4 4883c428 add rsp, 28h
|
综上这个线程函数分析完毕,姚老师不是在线程函数里完成_FLT_OBJECT的对象引用计数递减的。

继续进行函数调用上层分析,根据内核编程基础,可直接推测在创建线程后,对_ETHREAD增加引用计数,然后进行等待,等待完成后解引用对象,最后关闭线程句柄。

由此分析完毕,这个函数也不是我们想找的。

继续往上分析,也无大发现。

静态分析无果,Windbg动态调试。
我们跟踪一下哪里调用进来的这个函数。下断返回地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 0 : kd> bp FFFFF804776669FF
0 : kd> g
Breakpoint 1 hit
PCHunter64as + 0x169ff :
fffff804` 776669ff ff15b3360600 call qword ptr [PCHunter64as + 0x7a0b8 (fffff804` 776ca0b8 )]
0 : kd> !sync
[sync] No argument found, using default host ( 127.0 . 0.1 : 9100 )
[sync] sync success, sock 0x638
[sync] probing sync
[sync] sync is now enabled with host 127.0 . 0.1
[sync] recv: connection closed
[sync] sync is off
0 : kd> !sync
[sync] No argument found, using default host ( 127.0 . 0.1 : 9100 )
[sync] sync success, sock 0x638
[sync] probing sync
[sync] sync is now enabled with host 127.0 . 0.1
|
然后就断下来了。

F10 一步步单步返回。
调试过程中出现了一些异常,为了避免跑飞啥的,下个ret处的断点。
最后我们回溯到了DriverObject的0xE函数里。

总感觉真的少了点什么步骤,因此我们在这里下个断点,放过去,然后加载卸载其他文件过滤驱动。从头来一次。
再次选中卸载一个微软驱动的微过滤器,后我们断在了DispatchHandler里。

一步步的调试,并为IDA添加注释。
这里我们不再断在线程函数里。
1 2 3 4 5 6 7 8 | 0 : kd> bd *
0 : kd> bl
0 d Enable Clear fffff804` 73eed6b0 0001 ( 0001 ) FLTMGR!FltUnregisterFilter
1 d Enable Clear fffff804` 776669ff 0001 ( 0001 ) PCHunter64as + 0x169ff
2 d Enable Clear fffff804` 776668e5 0001 ( 0001 ) PCHunter64as + 0x168e5
3 d Enable Clear fffff804` 776c160f 0001 ( 0001 ) PCHunter64as + 0x7160f
0 : kd> be 3
|
g放行,第二次断下到call ebx, 看了一下再初始化内存为0,然后没有传递相关微过滤驱动相关的东西,猜想应该是摘掉后遍历一次。直接放过去。

我们试试手动刷新会调用到哪里进行验证,确实如此。

第二次也断在同样的地方。由此逻辑就是刷新会调用两个功能函数。而移除后进行了刷新的调用。因此移除的功能只涉及一次函数调用。

白干了!PCHunter不卡的原因是他启用的系统线程去处理的?因为经过逆向分析,Windows 10 确实啥也没做。他不卡是因为创建了系统线程去跑,能不能摘掉就听天由命了。不信看官自己试试,他也摘不掉。

证明方法:通过遍历系统进程的线程,然后搜函数地址为0xFFFFF80477666A18的线程
1 2 3 4 5 6 7 | 0 : kd> !process 0 0 System
PROCESS ffffcf8f7bc8d300
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad002 ObjectTable: ffffa88a30e04ac0 HandleCount: 2216.
Image: System
0 : kd> !process ffffcf8f7bc8d300
|
综上所述,解决思路就是用系统线程去跑,如果没摘掉再跑一次,等到对象引用计数降为为0然后就摘掉了
eBPF安全开发与攻防对抗