手头有个驱动,因为客户需求的原因,只在x86的2k3和xp系统上做过测试,今天在把它往x64 win7系统移植的时候遇到这么一个问题:
  在PsSetCreateProcessNotifyRoutine注册的回调函数中,通过FltSendMessage发通知给应用层接收者。在发送“进程退出”事件时FltSendMessage总是会返回STATUS_THREAD_IS_TERMINATING(0xC000004B)。

  琢磨了一会,找到了问题关键,分析过程如下:

  1. 定位返回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这个函数返回的这个错误。

  2. 继续更细致的定位

     ...
     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。 (有时候要深入好几层函数调用才能定位到错误码的直接返回者)

  3. 根据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。

  到这里,已经弄清楚了在什么情况下会出现这个错误。那解决方案也很简单,不赘述。

  另外,多说两句:

  1. 这个特性是从vista开始引入。
  2. 目的是为了保证线程结束的处理过程中不被卡住。

  参考资料: