前段时间因有需要我必须摘除一个MiniFilter
的回调,了解的人都知道FltUnregisterFilter
是移除MiniFilter
的API,但是MSDN强调过这个API只能模块自身使用,于是当我尝试拿他去对我的目标驱动手时,代码进入FltUnregisterFilter
后就一去不返了。
在看雪上拜读到这篇文章,文中定位了阻塞的原因:进入FltUnregisterFilter
后,ExWaitForRundownProtectionRelease
会检查filter对象的引用计数,按照MSDN的解释,如果不为0那么就会阻塞。作者的实验场景中此时引用计数为2,那么一定也就阻塞了。
作者再观察使用PCHunter
来摘除MiniFilter
的过程,发现在进入ExWaitForRundownProtectionRelease
之前这个filter对象的引用计数已经变为了0,于是他猜测PCHunter
使用APIExReleaseRundownProtection
来减少了引用计数。因此他也在自己驱动中在调用FltUnregisterFilter
之前添加执行了ExReleaseRundownProtection
,然后成功了。
我使用作者这个方法在一个环境下测试成功了,但是换了一个环境后并没成功,原因是要摘除的filter的引用计数在 ExWaitForRundownProtectionRelease
之前并不能到达0:
执行ExReleaseRundownProtection
后进入FltUnregisterFilter
之时引用计数值为0x7623:
执行到ExWaitForRundownProtectionRelease
之时这个引用计数值还剩0x6:
接着进入ExWaitForRundownProtectionRelease
就再次一去不返了。
所以我需要调多次ExReleaseRundownProtection
吗?如果是多次那么到底应该调几次才是恰当的?我尝试寻找一个真正的具有通用性的解决办法:
我尝试连续调用ExReleaseRundownProtection
,发现每次这个引用计数都会减少2:
所以如果我循环“引用计数/2”次是不是就能清零了?如下:
这样在执行FltUnregisterFilter
之前,引用计数清零了:
但是继续执行后,依然阻塞了。
暂停下来看传入对象的引用计数:
可见这个计数变得很大,高位为ffff,可以猜测是不是它在为0后经过了多次递减最终导致这么大的数字,现在就需要看看 FltUnregisterFilter
中发生了什么事了,关键反编译代码如下:
经过简单调试就可以发现,从刚进入FltUnregisterFilter
到断点FltpWaitForRundownProtectionReleaseInternal
,filter对象的引用计数发生较大的减小,a处FltpFreeInstance
函数和b处FltObjectDereference
执行了修改操作。
看看具体引用计数是怎么被修改的,先看FltObjectDereference
:
可见InterlockedCompareExchange
会将计数减2。
再看看FltpFreeInstance
,由于里面调用栈较深,我这里通过访问断点来演示,在filter+8处下写断点很快就命中了,调用栈如下:
这里也是每次减计数2:
从上面给的FltUnregisterFilter
精简代码可以看到,a会被循环调用,再观察下这个for循环的结构很容易猜测到这是一个_LIST_ENTRY
链接的链表,偏移位于208处。
现在大概知道FltUnregisterFilter
怎么处理filter对象的引用计数了:首先它会遍历对象偏移0xD0位置的链表,对每个节点使用 FltpFreeInstance
最终使用nt! ExfReleaseRundownProtection
来释放这个节点和它对filter对象的引用计数,这一步完成后,此时要求filter的引用计数剩且仅剩2次,然后下面再调用FltObjectDereference
减去最后两次引用,这样在进入FltpWaitForRundownProtectionReleaseInternal
前引用计数就清零了。
所以该怎么来恰如其分地释放filter对象?现在的问题在于,不知道在前面a处循环后到达b时还剩多少次引用计数,如本节开头我演示的实验,剩的引用计数为6,那么可以在FltUnregisterFilter
前使用三次ExReleaseRundownProtection
即可。但是要是再换一个环境或者当这个环境发生变化时这个计数又会变为多少?那我能不能计算在节点循环时引用计数会减少多少,然后再加上后面FltpWaitForRundownProtectionReleaseInternal
之前减少的2,两者之和与总引用计数值的差值就是我需要手动调用 ExReleaseRundownProtection
来减少的计数? 或许可以,这意味着我需要逆向分析FltpFreeInstance
内部实现,然后编写代码模拟其判断逻辑,才能精确计算出到底他使用了多少次引用计数,我想我没有兴趣做这件繁琐的事。这里有另一个办法:在执行 FltpWaitForRundownProtectionReleaseInternal
前进行hook,调用ExReleaseRundownProtection
减掉多余的引用计数值。
Hook怎么来编写?先看下fltmgr!FltUnregisterFilter
中调用FltpWaitForRundownProtectionReleaseInternal
处的代码长什么样:
这是在Windows 10 22H2的fltmgr.sys
中的样子,可以拿上图 "1C0058627"到"1C005862D+1"部分作为特征定位位置, 然后从"1C005861F"写入hook代码,到"1C005862D"正好有14个字节的空间。由于x64下内联hook进行跳转至少需要两条指令,因此在没有特殊条件下,至少14个字节完成如下两条指令:
示例代码如下:
搜索hook点和编写hook代码部分在部分平台会有差异,自行微调。当设置这个hook后,就只需要在简单对filter对象执行一次 FltUnregisterFilter
即可稳定地摘除它的回调。
这个方法在hook中减掉了多余的引用计数,但是这会不会有个问题,内核对象的引用都是有意义的,如果一个对象的引用不是由使用者在使用完毕后自行关闭,而是被其他模块在不告知的情况下关闭,那么使用者再次使用这个对象时就可能出现未知问题。所以如果注册MiniFilter
的模块再次使用这个filter对象时是有可能出现BSOD的,因为我在摘除后一直没有遇到问题所以暂时还没研究这个问题。
这是一种劫持执行流的解决方案,而且如0x5点所述我还不确定绝不会出现问题,不过这里似乎还有另一个方法,就是直接编写代码模仿FltUnregisterFilter
的行为,FltUnregisterFilter
反编译后可以看到其实并不是很复杂,如果手动循环调用FltpFreeInstance
后再在FltpWaitForRundownProtectionReleaseInternal
之前根据引用计数决定释放次数,然后结合调试执行其余关键的操作。我想理论上是可行的,这种办法还能避开不同系统版本的适配性问题,但这也无法解决0x5点中提到的问题,所以或许也不是拆除MiniFilter的最正确姿势。
UINT64 count
=
*
((UINT64
*
)pFilter
+
1
);
for
(size_t i
=
0
; i < count
/
2
; i
+
+
)
{
ExReleaseRundownProtection(RunRefs);
}
FltUnregisterFilter(pFilter);
UINT64 count
=
*
((UINT64
*
)pFilter
+
1
);
for
(size_t i
=
0
; i < count
/
2
; i
+
+
)
{
ExReleaseRundownProtection(RunRefs);
}
FltUnregisterFilter(pFilter);
__int64 __fastcall FltUnregisterFilter(__int64 a1)
{
_QWORD
*
*
v6;
_RBX
=
a1;
v6
=
(_QWORD
*
*
)(_RBX
+
208
);
for
(_QWORD
*
i
=
*
v6; i !
=
v6; i
=
(_QWORD
*
)
*
i )
{
if
( !(
*
(_DWORD
*
)(i
-
14
) &
1
) && !(
*
(_DWORD
*
)(i
-
5
) &
4
) )
{
v4
=
FltObjectReference((__int64)(i
-
14
));
if
( v4 >
=
0
)
{
ExReleaseResourceLite((PERESOURCE)(_RBX
+
104
));
KeLeaveCriticalRegion();
/
/
Inside here the references reduced
FltpFreeInstance((__int64)(i
-
14
),
2
*
(
*
(_DWORD
*
)(_RBX
+
88
) &
1
)
+
2
, v8);
/
/
a.
goto LABEL_7;
}
}
}
......
FltObjectDereference(_RBX);
/
/
b.
FltpWaitForRundownProtectionReleaseInternal(_RBX
+
8
,
0
);
/
/
c. Here the block resides
FltpTerminateActiveConnections(_RBX);
......
}
__int64 __fastcall FltUnregisterFilter(__int64 a1)
{
_QWORD
*
*
v6;
_RBX
=
a1;
v6
=
(_QWORD
*
*
)(_RBX
+
208
);
for
(_QWORD
*
i
=
*
v6; i !
=
v6; i
=
(_QWORD
*
)
*
i )
{
if
( !(
*
(_DWORD
*
)(i
-
14
) &
1
) && !(
*
(_DWORD
*
)(i
-
5
) &
4
) )
{
v4
=
FltObjectReference((__int64)(i
-
14
));
if
( v4 >
=
0
)
{
ExReleaseResourceLite((PERESOURCE)(_RBX
+
104
));
KeLeaveCriticalRegion();
/
/
Inside here the references reduced
FltpFreeInstance((__int64)(i
-
14
),
2
*
(
*
(_DWORD
*
)(_RBX
+
88
) &
1
)
+
2
, v8);
/
/
a.
goto LABEL_7;
}
}
}
......
FltObjectDereference(_RBX);
/
/
b.
FltpWaitForRundownProtectionReleaseInternal(_RBX
+
8
,
0
);
/
/
c. Here the block resides
FltpTerminateActiveConnections(_RBX);
......
}
__int64 __fastcall FltObjectDereference(__int64 a1)
{
return
ExReleaseRundownProtection(a1
+
8
);
/
/
located
in
ntoskrnl.exe
}
int
__fastcall ExfReleaseRundownProtection(volatile signed __int64
*
_RCX)
{
……
v3
=
_InterlockedCompareExchange(_RCX, v1
-
2
, v1);
……
return
v3;
}
__int64 __fastcall FltObjectDereference(__int64 a1)
{
return
ExReleaseRundownProtection(a1
+
8
);
/
/
located
in
ntoskrnl.exe
}
int
__fastcall ExfReleaseRundownProtection(volatile signed __int64
*
_RCX)
{
……
v3
=
_InterlockedCompareExchange(_RCX, v1
-
2
, v1);
……
return
v3;
}
1
: kd> ba w4 ffff9e07`a8346a68
1
: kd> g
Breakpoint
1
hit
nt!ExfReleaseRundownProtection
+
0x1c
:
fffff801`
1be219dc
4c8bc0
mov r8,rax
00
ffff8506`
709795c0
fffff801`
1ba58d29
nt!ExfReleaseRundownProtection
+
0x1c
01
ffff8506`
709795f0
fffff801`
1ba8a6b1
FLTMGR!DoReleaseContext
+
0xf9
02
ffff8506`
70979630
fffff801`
1ba9de47
FLTMGR!FltpDeleteContextList
+
0xc1
03
ffff8506`
70979660
fffff801`
1ba8e627
FLTMGR!FltpCleanupStreamListCtrlForInstanceRemoval
+
0xf1b7
04
ffff8506`
709796b0
fffff801`
1baa85aa
FLTMGR!FltpFreeInstance
+
0x1db
05
ffff8506`
70979780
fffff801`
451816a6
FLTMGR!FltUnregisterFilter
+
0x11a
06
ffff8506`
70979840
fffff801`
451813d6
MyDriver0!RemoveCsMinifilters
+
0x156
07
ffff8506`
709798b0
fffff801`
1c36dd1c
MyDriver0!DriverEntry
+
0x3d6
1
: kd> ba w4 ffff9e07`a8346a68
1
: kd> g
Breakpoint
1
hit
nt!ExfReleaseRundownProtection
+
0x1c
:
fffff801`
1be219dc
4c8bc0
mov r8,rax
00
ffff8506`
709795c0
fffff801`
1ba58d29
nt!ExfReleaseRundownProtection
+
0x1c
01
ffff8506`
709795f0
fffff801`
1ba8a6b1
FLTMGR!DoReleaseContext
+
0xf9
02
ffff8506`
70979630
fffff801`
1ba9de47
FLTMGR!FltpDeleteContextList
+
0xc1
03
ffff8506`
70979660
fffff801`
1ba8e627
FLTMGR!FltpCleanupStreamListCtrlForInstanceRemoval
+
0xf1b7
04
ffff8506`
709796b0
fffff801`
1baa85aa
FLTMGR!FltpFreeInstance
+
0x1db
05
ffff8506`
70979780
fffff801`
451816a6
FLTMGR!FltUnregisterFilter
+
0x11a
06
ffff8506`
70979840
fffff801`
451813d6
MyDriver0!RemoveCsMinifilters
+
0x156
07
ffff8506`
709798b0
fffff801`
1c36dd1c
MyDriver0!DriverEntry
+
0x3d6
mov rax, shellcode_addr
jump rax
mov rax, shellcode_addr
jump rax
NTSTATUS HookFltUnregisterFilter()
{
DbgPrint(
"Enter HookFltUnregisterFilter\n"
);
NTSTATUS status
=
STATUS_UNSUCCESSFUL;
BYTE
*
phookaddr
=
0
;
BYTE
*
pfun
=
(BYTE
*
)FltUnregisterFilter;
pfun
=
pfun
+
6
+
*
(
LONG
*
)(pfun
+
2
);
pfun
=
*
(BYTE
*
*
)pfun;
/
/
searching
for
the codes behind
"call FltObjectDereference"
/
/
33
D2
48
8D
4B
08
BYTE searchBytes[
6
]
=
{
0x33
,
0xD2
,
0x48
,
0x8D
,
0x4B
,
0x08
};
for
(
int
i
=
0
;i<
0x400
;i
+
+
)
{
if
(memcmp(pfun
+
i, searchBytes,
6
)
=
=
0
)
{
phookaddr
=
pfun
+
i
-
8
;
break
;
}
}
if
(phookaddr
=
=
0
)
{
DbgPrint(
"HookFltUnregisterFilter cant find hoodaddr\n"
);
return
status;
}
/
*
mov rcx, rbx
/
/
48
8b
cb
mov rax,
0x0000000000000000
/
/
48
b8
00
00
00
00
00
00
00
00
call rax
/
/
ff d0
mov edx,
1
/
/
BA
01
00
00
00
lea rcx, [rbx
+
8
]
/
/
48
8b
4b
08
lock cmpxchg [rcx], rdx
cmp
rax,
2
/
/
F0
48
0F
B1
11
48
83
F8
02
jnb short loc_1402F84CC
/
/
73
offset
xor edx, edx
/
/
33
d2 must
set
rdx
0
due to the origin codes
*
/
BYTE hookcode[]
=
{
0x48
,
0x8b
,
0xcb
,
0x48
,
0xb8
,
0x00
,
0x0
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xff
,
0xd0
,
0xba
,
0x01
,
0x00
,
0x00
,
0x00
,
0x48
,
0x8d
,
0x4b
,
0x08
,
0xf0
,
0x48
,
0x0f
,
0xb1
,
0x11
,
0x48
,
0x83
,
0xf8
,
0x02
,
0x73
,
0x00
,
0x33
,
0xd2
};
/
/
mov rax, next_addr ; jmp rax
BYTE jumpcode[
12
]
=
{
0x48
,
0xB8
,
0xFF
,
0xFF
,
0xFF
,
0xFF
,
0xAA
,
0xAA
,
0xAA
,
0xAA
,
0xFF
,
0xE0
};
int
jmpcodelen
=
12
;
int
jmpcode_uselen
=
14
;
int
hookcodelen
=
sizeof(hookcode);
/
/
1
backup
g_hookpoint1.g_HookBak
=
(BYTE
*
)ExAllocatePool(NonPagedPool,
0x100
);
memcpy(g_hookpoint1.g_HookBak, phookaddr, jmpcode_uselen);
g_hookpoint1.g_HookBakSize
=
jmpcode_uselen;
/
/
2
prepare hook
buffer
g_hookpoint1.g_HookAddr
=
phookaddr;
g_hookpoint1.g_HookBuffer
=
(BYTE
*
)ExAllocatePool(NonPagedPool,
0x100
);
/
/
/
here i must calculate the
abs
addr of FltObjectDereference
LONG
offset
=
*
(
LONG
*
)((UINT64)phookaddr
+
3
+
1
);
UINT64 nFltObjectDereferenceAddr
=
(UINT64)phookaddr
+
3
+
5
+
offset;
*
(UINT64
*
)(hookcode
+
5
)
=
nFltObjectDereferenceAddr;
DbgPrint(
"nFltObjectDereferenceAddr:<0x%p>\n"
, nFltObjectDereferenceAddr);
/
/
/
calculate the offset
for
loop
BYTE bytejmp
=
0
-
hookcodelen
+
2
;
hookcode[hookcodelen
-
3
]
=
bytejmp;
memcpy(g_hookpoint1.g_HookBuffer, hookcode, hookcodelen);
/
/
/
jump back
*
(UINT64
*
)(jumpcode
+
2
)
=
(UINT64)phookaddr
+
jmpcode_uselen;
memcpy(g_hookpoint1.g_HookBuffer
+
hookcodelen, jumpcode, jmpcodelen);
g_hookpoint1.g_HookBufferSize
=
0x100
;
/
/
3
now
set
the hook
*
(UINT64
*
)(jumpcode
+
2
)
=
(UINT64)g_hookpoint1.g_HookBuffer;
KIRQL tmpirql
=
WriteProtectOff();
/
/
turn off write protect
memcpy(phookaddr, jumpcode, jmpcodelen);
WriteProtectOn(tmpirql);
/
/
recover
DbgPrint(
"Leave HookFltUnregisterFilter\n"
);
return
status;
}
NTSTATUS HookFltUnregisterFilter()
{
DbgPrint(
"Enter HookFltUnregisterFilter\n"
);
NTSTATUS status
=
STATUS_UNSUCCESSFUL;
BYTE
*
phookaddr
=
0
;
BYTE
*
pfun
=
(BYTE
*
)FltUnregisterFilter;
pfun
=
pfun
+
6
+
*
(
LONG
*
)(pfun
+
2
);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-12-7 18:25
被adc又死了编辑
,原因: