手头有个驱动,因为客户需求的原因,只在x86的2k3和xp系统上做过测试,今天在把它往x64 win7系统移植的时候遇到这么一个问题:
在PsSetCreateProcessNotifyRoutine注册的回调函数中,通过FltSendMessage发通知给应用层接收者。在发送“进程退出”事件时FltSendMessage总是会返回STATUS_THREAD_IS_TERMINATING(0xC000004B)。
琢磨了一会,找到了问题关键,分析过程如下:
-
定位返回STATUS_THREAD_IS_TERMINATING的点
1: kd> pc fltmgr!FltSendMessage+0x1bf: fffff880`011a1ddf call qword ptr [fltmgr!_imp_FsRtlCancellableWaitForMultipleObjects (fffff880`011a75c8)] 1: kd> p fltmgr!FltSendMessage+0x1c5: fffff880`011a1de5 mov ebx,eax 1: kd> r eax eax=c000004b
这里定位到在FltSendMessage函数中是FsRtlCancellableWaitForMultipleObjects这个函数返回的这个错误。
-
继续更细致的定位
... nt! ?? ::NNGAKEGL::`string'+0xcb90: fffff800`0418d031 mov rax,qword ptr gs:[188h] fffff800`0418d03a mov ecx,dword ptr [rax+448h] fffff800`0418d040 test cl,1 fffff800`0418d043 jne nt! ?? ::NNGAKEGL::`string'+0xcc16 (fffff800`0418d0b7) ... nt! ?? ::NNGAKEGL::`string'+0xcc16: fffff800`0418d0b7 mov eax,0C000004Bh fffff800`0418d0bc jmp nt!FsRtlCancellableWaitForMultipleObjects+0x69 (fffff800`0416a271) ...
很幸运,就是FsRtlCancellableWaitForMultipleObjects这个函数返回的STATUS_THREAD_IS_TERMINATING。 (有时候要深入好几层函数调用才能定位到错误码的直接返回者)
-
根据2中的汇编码,继续分析
1: kd> dp gs:[188h] 002b:00000000`00000188 fffffa80`0d168b60 1: kd> !pool fffffa80`0d168b60 2 Pool page fffffa800d168b60 region is Nonpaged pool *fffffa800d168b00 size: 500 previous size: 280 (Allocated) *Thre (Protected) Pooltag Thre : Thread objects, Binary : nt!ps
看来这个地址是个线程结构体。 为了确认这个地址就是线程结构体的开头,再找个线程相关函数来看看吧:
0: kd> u nt!KeGetCurrentThread nt!PsGetCurrentThread: fffff800`03f23f40 mov rax,qword ptr gs:[188h] fffff800`03f23f49 ret
那么不是kthread就是ethread,而汇编码中取值的偏移是0x448,前者大小不对,那看看后者的0x448是什么吧
1 1: kd> dt nt!_ethread 2 nt!_ETHREAD 3 ... 4 +0x448 CrossThreadFlags : Uint4B 5 +0x448 Terminated : Pos 0, 1 Bit 6 +0x448 ThreadInserted : Pos 1, 1 Bit 7 +0x448 HideFromDebugger : Pos 2, 1 Bit 8 +0x448 ActiveImpersonationInfo : Pos 3, 1 Bit 9 +0x448 Reserved : Pos 4, 1 Bit 10 +0x448 HardErrorsAreDisabled : Pos 5, 1 Bit 11 +0x448 BreakOnTermination : Pos 6, 1 Bit 12 +0x448 SkipCreationMsg : Pos 7, 1 Bit 13 +0x448 SkipTerminationMsg : Pos 8, 1 Bit 14 +0x448 CopyTokenOnOpen : Pos 9, 1 Bit 15 +0x448 ThreadIoPriority : Pos 10, 3 Bits 16 +0x448 ThreadPagePriority : Pos 13, 3 Bits 17 +0x448 RundownFail : Pos 16, 1 Bit 18 +0x448 NeedsWorkingSetAging : Pos 17, 1 Bit 19 ...
对照着汇编码,可以知道在FsRtlCancellableWaitForMultipleObjects函数判断了当前线程的ethread::Terminated(行5)这个域,而在我的调试环境中该域是1,于是FsRtlCancellableWaitForMultipleObjects就返回了STATUS_THREAD_IS_TERMINATING。
到这里,已经弄清楚了在什么情况下会出现这个错误。那解决方案也很简单,不赘述。
另外,多说两句:
- 这个特性是从vista开始引入。
- 目的是为了保证线程结束的处理过程中不被卡住。
参考资料: