• 标 题:瑞星2011 Hookhelp.sys在LoadImageNotifyRoutine中的全局性Fast Mutex设置可能导致死锁(deadlock)
  • 作 者:轩辕小聪
  • 时 间:2011-04-01 10:43:29
  • 链 接:http://bbs.pediy.com/showthread.php?t=131688

调试环境:WIN7 32bit SP1
调试工具:windbg+IDA
影响版本:瑞星杀毒软件2011 hookhelp.sys版本25.0.0.9
调试目的:解决自己遇到的问题,同时学习相应方法和知识。

近一个月来,我两台刚刚更新了WIN7 SP1系统的电脑(一台实验室用的PC,一台宿舍用的笔记本)多次出现死锁情况,现象表现为突然整个图形界面没响应,只有鼠标可以动,有时鼠标有忙的图标(蓝圈),有时没有,同时可以听到CPU风扇声明显增大。这种现象经常出现在系统长期待机后唤醒进入登录界面后几秒,后来在正在使用过程中时不时也出现。

我十分头疼,觉得应该是系统底层哪里死锁了,不得已设置了CrashOnCtrlScroll键,希望遇到的时候可以用它来BSOD掉然后分析dmp。

昨晚在宿舍的电脑上又给我碰上,这次是重启后刚登录后,过了登录界面将要出来桌面之前,刚好屏幕一片黑的时候,就又停在那里了。用右边Ctrl键+两次ScrollLock键手动BSOD,重启后选正常启动系统,用windbg加载保存下来的核心内存转储文件,开始找原因。

以前调dmp文件都是有比较明确目标的,比如一个出异常的用户态程序,或者导致BSOD的驱动,那样只要在出异常或BSOD的当前线程进行栈回溯就比较容易发现问题了,这次则不然,因为BSOD是人工触发的,跟真正的死锁原因无关,所以找原因要麻烦得多。由于这块确实不熟,调了N久,中间有很多无用功,发帖的时候就只贴有用的部分了。

代码:
0: kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

MANUALLY_INITIATED_CRASH (e2)
The user manually initiated this crash dump.
Arguments:
Arg1: 00000000
Arg2: 00000000
Arg3: 00000000
Arg4: 00000000

Debugging Details:
------------------


BUGCHECK_STR:  MANUALLY_INITIATED_CRASH

DEFAULT_BUCKET_ID:  VISTA_DRIVER_FAULT

PROCESS_NAME:  System

CURRENT_IRQL:  6

LAST_CONTROL_TRANSFER:  from 945df160 to 83318f20

STACK_TEXT:  
83362c0c 945df160 000000e2 00000000 00000000 nt!KeBugCheckEx+0x1e
83362c3c 945df768 00105d30 000000c6 00000000 i8042prt!I8xProcessCrashDump+0x251
83362c88 832747ad 88ab4a00 86105c78 83362cb4 i8042prt!I8042KeyboardInterruptService+0x2ce
83362c88 832b1e1a 88ab4a00 86105c78 83362cb4 nt!KiInterruptDispatch+0x6d
83362d24 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0x1a
以前分析dmp最先用的!analyze -v,这次提供的信息显然没有什么大用,只是说明BSOD确实是我通过PS/2键盘手动触发的。

看到网上有文章说查deadlock可以先用!locks命令看看哪些lock是在held的状态,就先试试了。
代码:
0: kd> !locks
**** DUMP OF ALL RESOURCE OBJECTS ****
KD: Scanning for held 

locks........................................................................................................................................................

.......................................................................................................
8159 total locks
空白一片,看来不是ERESOURCE的问题。

用!stacks看一看各个线程的情况(部分省略):
代码:
0: kd> !stacks
Proc.Thread  .Thread  Ticks   ThreadState Blocker
                            [85404020 System]
   4.00001c  85429850 0000923 Blocked    nt!AlpcpSignalAndWait+0x7b
   4.000028  85424020 0000923 Blocked    nt!AlpcpSignalAndWait+0x7b
   4.000034  85424798 0001178 Blocked    nt!AlpcpSignalAndWait+0x7b
   4.000044  85423a70 0000923 Blocked    nt!AlpcpSignalAndWait+0x7b
   4.00005c  8542a5f8 0000031 Blocked    nt!MiModifiedPageWriter+0x39
   4.000074  85427a28 0000001 Blocked    nt!CcQueueLazyWriteScanThread+0x4a
   4.000080  8541f5a8 0003751 Blocked    nt!AlpcpReceiveMessagePort+0x245
   4.000084  85435d48 0000041 Blocked    nt!EtwpLogger+0xd0
   4.000088  854357b0 0000001 Blocked    nt!EtwpLogger+0xd0
   4.00008c  85449d48 0000001 Blocked    nt!EtwpLogger+0xd0
   4.000090  854497b0 0000001 Blocked    nt!EtwpLogger+0xd0
   4.000098  85c94540 0000001 Blocked    nt!EtwpLogger+0xd0
......

                            [88ab53d0 svchost.exe]
 48c.000490  a27caa88 00038c4 Blocked    nt!KiFastCallEntry+0x12a
 48c.0004a8  a27e43b0 000017f Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.0004b0  a27e7868 00031e0 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0004c0  a15fdd48 00004ce Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.0004f8  a27f07e0 0000804 Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.0004fc  a27fd030 0003797 Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.000500  a27ffc18 0003817 Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.000548  a2d8d548 00038d2 Blocked    nt!IoRemoveIoCompletion+0x23
 48c.000560  a2d96388 0003081 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0005b8  a2d89a10 00036fc Blocked    nt!KiAcquireFastMutex+0x56
 48c.0005d4  a2db6380 0002db7 Blocked    nt!KiAcquireFastMutex+0x56
 48c.000674  a2df87b8 0003e84 Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.000ff8  b6c4dc50 0003675 Blocked    nt!KiAcquireFastMutex+0x56
 48c.000858  b6cc0910 00031e0 Blocked    nt!KiAcquireFastMutex+0x56
 48c.000728  85542030 0003534 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0002dc  854a75f8 0003cab Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.000744  854a7a60 0003cb0 Blocked    nt!IoRemoveIoCompletion+0x23
 48c.000554  8554d4c0 00038c3 Blocked    nt!ObpWaitForMultipleObjects+0x262
 48c.001004  a15e6690 0003014 Blocked    nt!KiAcquireFastMutex+0x56
 48c.00105c  85639a08 0002ee7 Blocked    nt!KiAcquireFastMutex+0x56
 48c.001060  85639720 0002ee7 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0010a8  a15e6358 0002e8e Blocked    nt!KiAcquireFastMutex+0x56
 48c.0010ac  855a9030 0002e57 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0010b8  8560c568 0002dc6 Blocked    nt!KiAcquireFastMutex+0x56
 48c.0010c0  85568880 0002a4f Blocked    nt!KiAcquireFastMutex+0x56
 48c.0010fc  855b5030 0002a4f Blocked    nt!KiAcquireFastMutex+0x56
 48c.001100  855b5d48 0001274 Blocked    nt!AlpcpSignalAndWait+0x7b
 48c.001194  85585030 000017f Blocked    nt!IoRemoveIoCompletion+0x23
 48c.0011a8  85584a60 000017f Blocked    nt!IoRemoveIoCompletion+0x23
......

                            [855a4d40 explorer.exe]
 71c.0008b4  855c3030 00035ee Blocked    nt!KiAcquireFastMutex+0x56
 71c.000adc  b6cd7d48 0003069 Blocked    nt!ObpWaitForMultipleObjects+0x262
 71c.000cb0  b6cdc920 00037c0 Blocked    nt!KiFastCallEntry+0x12a
 71c.000480  854a4ad8 00034bc Blocked    nt!KiFastCallEntry+0x12a
 71c.00056c  8556ed48 000282e Blocked    nt!KiFastCallEntry+0x12a
 71c.000c80  854f14f0 0002889 Blocked    nt!KiAcquireFastMutex+0x56
 71c.000cfc  854e1ad8 00036f9 Blocked    nt!KiAcquireFastMutex+0x56
 71c.000070  85667030 0003790 Blocked    nt!ObpWaitForMultipleObjects+0x262
 71c.0001a4  8543dd48 000372c Blocked    nt!ObpWaitForMultipleObjects+0x262
 71c.000128  8560f030 00034a9 Blocked    nt!KiAcquireFastMutex+0x56
 71c.000de0  8560e970 0003719 Blocked    nt!ObpWaitForMultipleObjects+0x262
 71c.000e60  855c05f8 00036fc Blocked    nt!ExfAcquirePushLockExclusive+0x100
 71c.000ae8  85626030 0003743 Blocked    nt!ObpWaitForMultipleObjects+0x262
 71c.000a00  854fbbe0 0002800 Blocked    nt!KiFastCallEntry+0x12a
 71c.0009e4  856267f0 00034ac Blocked    nt!KiFastCallEntry+0x12a
 71c.000830  855bad48 000372c Blocked    win32k!xxxRealSleepThread+0x1d7
 71c.00099c  854fa380 0000a18 Blocked    win32k!xxxRealSleepThread+0x1d7
 71c.000814  8562f838 00036fc Blocked    nt!KiAcquireFastMutex+0x56
 71c.000568  8556f030 0003700 Blocked    nt!KiFastCallEntry+0x12a
 71c.000cdc  85632d48 0003069 Blocked    nt!ExfAcquirePushLockExclusive+0x100
......

                            [8562cd40 dllhost.exe]
 d00.000d0c  85633030 00036fc Blocked    nt!KiAcquireFastMutex+0x56

                            [856166a0 IMSCMIG.EXE]
 bf8.0001b0  8564b640 00064ea ????       nt!KiThreadStartup
看到大量的进程的Blocker都是nt!KiAcquireFastMutex+0x56,给了我第一个线索,难道是拥有某个FastMutex的线程与其他线程造成了死锁?
那么先应该找到那个FastMutex。比如看dllhost.exe的这个线程:
代码:
0: kd> !thread 85633030
THREAD 85633030  Cid 0d00.0d0c  Teb: 7ffdf000 Win32Thread: fe52c7e0 WAIT: (WrFastMutex) KernelMode Non-Alertable
    86e15014  SynchronizationEvent
Not impersonating
DeviceMap                 c14ef898
Owning Process            8562cd40       Image:         dllhost.exe
Attached Process          N/A            Image:         N/A
Wait Start TickCount      11758          Ticks: 14076 (0:00:03:39.587)
Context Switch Count      16             
UserTime                  00:00:00.000
KernelTime                00:00:00.015
Win32 Start Address 0x00661609
Stack Init b5e47fd0 Current b5e47198 Base b5e48000 Limit b5e45000 Call 0
Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr  Args to Child              
b5e471b0 832b869d 85633030 00000000 83365d20 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
b5e471e8 832b74f7 856330f0 85633030 86e15014 nt!KiSwapThread+0x266
b5e47210 832b10cf 85633030 856330f0 00000000 nt!KiCommitThreadWait+0x1df
b5e47288 8326b08e 86e15014 00000022 00000000 nt!KeWaitForSingleObject+0x393
b5e472b0 832ce55b 85633030 8e015390 8ece16c7 nt!KiAcquireFastMutex+0x56
b5e472bc 8ece16c7 8615c440 00000000 b5e47304 nt!ExAcquireFastMutex+0x1e (FPO: [0,0,2])
WARNING: Stack unwind information not available. Following frames may be wrong.
b5e47b0c 834a939c a1583d40 00000d00 b5e47b5c HOOKHELP!RisingInlineUnHook+0x1467
b5e47b34 8349127f a1583d40 00000d00 8337cb88 nt!PsCallImageNotifyRoutines+0x62
b5e47be8 83481d4a a1581ca0 8562cd40 b5e47ce4 nt!MiMapViewOfImageSection+0x670
b5e47c58 83481e3a 8562cd40 b5e47ce4 00000000 nt!MiMapViewOfSection+0x22e
b5e47c88 83482599 c4cb0360 8562cd40 b5e47ce4 nt!MmMapViewOfSection+0x2a
b5e47d04 832781ea 0000005c ffffffff 0007f4b0 nt!NtMapViewOfSection+0x204
b5e47d04 779970b4 0000005c ffffffff 0007f4b0 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ b5e47d34)
0007f41c 00000000 00000000 00000000 00000000 0x779970b4
看到了瑞星的HOOKHELP.sys,从调用栈里明显看到这是dllhost.exe进入了瑞星设置的位于HOOKHELP.sys的LoadImageNotifyRoutine里面,后者调用ExAcquireFastMutex之后就等在那里了。
代码:
0: kd> dd nt!PspLoadImageNotifyRoutine
8337cb80  8a334e77 8a36505f 8e015393 bb0a1e4f
8337cb90  00000000 00000000 00000000 00000000
8337cba0  00000004 00000000 00000000 00000000
8337cbb0  00000000 00000000 00000000 00000000
8337cbc0  8a38fa63 bb0f0ca7 00000000 00000000
8337cbd0  00000000 00000000 00000000 00000000
8337cbe0  00000000 00000000 00000000 00000000
8337cbf0  00000000 00000000 00000000 00000000
8e015393这个EX_CALLBACK从地址上看比较可能是瑞星加的,后三位二进制数是计数,真正EX_CALLBACK_ROUTINE_BLOCK地址是8e015390
代码:
0: kd> dd 8e015390
8e015390  00000010 8ece1690 00000000 7d52504c
8e0153a0  06700203 6666744e 00f00702 01000a48
8e0153b0  00005756 00010000 00000000 00000001
8e0153c0  00000000 00000000 8e01565c 8e01565c
8e0153d0  8e0154e0 8e0154e0 00000000 be89a9b8
8e0153e0  86bf70d8 86d28690 86d28714 429eb9ca
8e0153f0  01ca040f 42a105b0 01ca040f 13e98a58
8e015400  01caa6ba 429eb9ca 01ca040f 00009000
0: kd> u 8ece1690
HOOKHELP!RisingInlineUnHook+0x1430:
8ece1690 55              push    ebp
8ece1691 8bec            mov     ebp,esp
8ece1693 81ec48080000    sub     esp,848h
8ece1699 c745f800000000  mov     dword ptr [ebp-8],0
8ece16a0 837d1000        cmp     dword ptr [ebp+10h],0
8ece16a4 0f849e030000    je      HOOKHELP!RisingInlineUnHook+0x17e8 (8ece1a48)
8ece16aa 8b4510          mov     eax,dword ptr [ebp+10h]
8ece16ad 8b08            mov     ecx,dword ptr [eax]
这就找到了瑞星的LoadImageNotifyRoutine。
代码:
0: kd> u 8ece1690
HOOKHELP!RisingInlineUnHook+0x1430:
8ece1690 55              push    ebp
8ece1691 8bec            mov     ebp,esp
8ece1693 81ec48080000    sub     esp,848h
8ece1699 c745f800000000  mov     dword ptr [ebp-8],0
8ece16a0 837d1000        cmp     dword ptr [ebp+10h],0
8ece16a4 0f849e030000    je      HOOKHELP!RisingInlineUnHook+0x17e8 (8ece1a48)
8ece16aa 8b4510          mov     eax,dword ptr [ebp+10h]
8ece16ad 8b08            mov     ecx,dword ptr [eax]
0: kd> u
HOOKHELP!RisingInlineUnHook+0x144f:
8ece16af c1e908          shr     ecx,8
8ece16b2 83e101          and     ecx,1
8ece16b5 0f858d030000    jne     HOOKHELP!RisingInlineUnHook+0x17e8 (8ece1a48)
8ece16bb 8b0d1845ce8e    mov     ecx,dword ptr [HOOKHELP!RisingInlineUnHook+0x42b8 (8ece4518)]
8ece16c1 ff150453ce8e    call    dword ptr [HOOKHELP!RisingInlineUnHook+0x50a4 (8ece5304)]
8ece16c7 8d550c          lea     edx,[ebp+0Ch]
8ece16ca 52              push    edx
8ece16cb 8b0d1845ce8e    mov     ecx,dword ptr [HOOKHELP!RisingInlineUnHook+0x42b8 (8ece4518)]
0: kd> dds 8ece5304
8ece5304  832ce53d nt!ExAcquireFastMutex
8ece5308  00000000
...(省略无关部分)
可以看到这里就是调用ExAcquireFastMutex的位置,8ece4518处保存相应FAST_MUTEX结构指针。
代码:
0: kd> dd 8ece4518
8ece4518  86e15008 00000000 cf396c68 6952c38e
8ece4528  676e6973 6b6f6f48 00007845 4b2ea300
8ece4538  00000083 00000000 00000000 00000000
8ece4548  00000000 00000000 00000000 00000000
8ece4558  00000000 00000000 00000000 00000000
8ece4568  00000000 00000000 00000000 00000000
8ece4578  00000000 00000000 00000000 00000000
8ece4588  00000000 00000000 00000000 00000000
0: kd> dt _FAST_MUTEX 86e15008
hal!_FAST_MUTEX
   +0x000 Count            : 0n1200
   +0x004 Owner            : 0x855c05f8 _KTHREAD
   +0x008 Contention       : 0x4f5
   +0x00c Event            : _KEVENT
   +0x01c OldIrql          : 0
看到这个FAST_MUTEX的Count和Contention值,我无语了,显然这个应该是HOOKHELP.sys中全局使用的一个FAST_MUTEX,结果因为Owner线程因为某些原因一直没Release,导致其他调用全都在这里等待。

于是关键就是其Owner线程为什么没Release它。看一下Owner线程是哪一个:
代码:
0: kd> !thread 855c05f8
THREAD 855c05f8  Cid 071c.0e60  Teb: 7ffd3000 Win32Thread: fe53e170 WAIT: (WrPushLock) KernelMode Non-Alertable
    bd82e310  SynchronizationEvent
Not impersonating
DeviceMap                 c14ef898
Owning Process            855a4d40       Image:         explorer.exe
Attached Process          N/A            Image:         N/A
Wait Start TickCount      11758          Ticks: 14076 (0:00:03:39.587)
Context Switch Count      124             
UserTime                  00:00:00.015
KernelTime                00:00:00.062
Win32 Start Address 0x75bc42ed
Stack Init bd82ffd0 Current bd82e1e0 Base bd830000 Limit bd82d000 Call 0
Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr  Args to Child              
bd82e1f8 832b869d 855c05f8 00000000 807c6120 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
bd82e230 832b74f7 855c06b8 855c05f8 bd82e310 nt!KiSwapThread+0x266
bd82e258 832b10cf 855c05f8 855c06b8 00000000 nt!KiCommitThreadWait+0x1df
bd82e2d4 832ccc8d bd82e310 0000001c 00000000 nt!KeWaitForSingleObject+0x393
bd82e344 83466cdc 984ad368 bd82e44c bd82e4dc nt!ExfAcquirePushLockExclusive+0x100
bd82e42c 832781ea ffffffff bd82e514 00000000 nt!NtAllocateVirtualMemory+0x127a
bd82e42c 832758e1 ffffffff bd82e514 00000000 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ bd82e44c)
bd82e4bc 8ece0d4a ffffffff bd82e514 00000000 nt!ZwAllocateVirtualMemory+0x11 (FPO: [6,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
bd82e650 8ece1011 00000bf8 bd82ee98 00000208 HOOKHELP!RisingInlineUnHook+0xaea
bd82f0c0 834a276c 0000071c 00000bf8 00000001 HOOKHELP!RisingInlineUnHook+0xdb1
bd82f178 834aa799 8564b640 016166a0 bd82f1d4 nt!PspInsertThread+0x5c0
bd82f884 8eceebf4 0463e6dc 0463e6b8 02000000 nt!NtCreateUserProcess+0x742
bd82fd00 832781ea 0463e6dc 0463e6b8 02000000 Hooksys+0x7bf4
bd82fd00 779970b4 0463e6dc 0463e6b8 02000000 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ bd82fd34)
0463e9fc 00000000 00000000 00000000 00000000 0x779970b4
这下关键点出来了,这个线程是explorer.exe进程所有,在创建微软输入法IMSCMIG.EXE进程的过程中,进入了HOOKHELP.sys的CreateProcessNotifyRoutine。

找一下这个Routine的位置:
代码:
0: kd> dd nt!PspCreateProcessNotifyRoutine
8337cce0  8a204bb7 8a24116f 8b2020df 8a31a2ff
8337ccf0  8a332a67 8a306e27 8a3219d6 b9a9c077
8337cd00  9972f5af 00000000 00000000 00000000
8337cd10  00000000 00000000 00000000 00000000
8337cd20  00000000 00000000 00000000 00000000
8337cd30  00000000 00000000 00000000 00000000
8337cd40  00000000 00000000 00000000 00000000
8337cd50  00000000 00000000 00000000 00000000
...(一个一个EX_CALLBACK结构地看)...
0: kd> dd 8a3219d0
8a3219d0  00000010 8ece0f50 00000000 8b200078
8a3219e0  06080203 6646744e 0024005c 00780045
8a3219f0  00650074 0064006e 0024005c 006d0052
8a321a00  0065004d 00610074 00610064 00610074
8a321a10  0024005c 00780054 004c0066 0067006f
8a321a20  06060208 69446350 8e0f3520 8787377c
8a321a30  6994ad04 11d093ef a000cca3 963122c9
8a321a40  010e010c 8eaa50d8 885e05e8 01000000
0: kd> u 8ece0f50
HOOKHELP!RisingInlineUnHook+0xcf0:
8ece0f50 55              push    ebp
8ece0f51 8bec            mov     ebp,esp
8ece0f53 81ec580a0000    sub     esp,0A58h
8ece0f59 57              push    edi
8ece0f5a 8b4508          mov     eax,dword ptr [ebp+8]
8ece0f5d 8945e8          mov     dword ptr [ebp-18h],eax
8ece0f60 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
8ece0f63 894dec          mov     dword ptr [ebp-14h],ecx
这样找到了HOOKHELP.sys的CreateProcessNotifyRoutine。用IDA加载HOOKHELP.sys,看看这个函数:
代码:
.text:00011F50 ; =============== S U B R O U T I N E =======================================
.text:00011F50
.text:00011F50 ; Attributes: bp-based frame
.text:00011F50
.text:00011F50 ; void __stdcall CreateProcessNotifyRoutine(HANDLE, HANDLE, BOOLEAN)
.text:00011F50 CreateProcessNotifyRoutine proc near    ; DATA XREF: sub_11840+B8o
.text:00011F50                                         ; sub_11840+DDo ...
.text:00011F50
.text:00011F50 var_A58         = dword ptr -0A58h
.text:00011F50 P               = dword ptr -0A54h
.text:00011F50 var_A50         = dword ptr -0A50h
.text:00011F50 var_A4C         = dword ptr -0A4Ch
.text:00011F50 var_A48         = word ptr -0A48h
.text:00011F50 var_83C         = dword ptr -83Ch
.text:00011F50 var_838         = word ptr -838h
.text:00011F50 var_438         = word ptr -438h
.text:00011F50 var_230         = dword ptr -230h
.text:00011F50 var_22C         = dword ptr -22Ch
.text:00011F50 ProcessFileNameBuffer= word ptr -228h
.text:00011F50 var_226         = byte ptr -226h
.text:00011F50 var_1C          = byte ptr -1Ch
.text:00011F50 ParentId1       = dword ptr -18h
.text:00011F50 ProcessId1      = dword ptr -14h
.text:00011F50 var_10          = dword ptr -10h
.text:00011F50 var_C           = dword ptr -0Ch
.text:00011F50 var_8           = dword ptr -8
.text:00011F50 var_4           = dword ptr -4
.text:00011F50 ParentId        = dword ptr  8
.text:00011F50 ProcessId       = dword ptr  0Ch
.text:00011F50 Create          = byte ptr  10h
.text:00011F50
.text:00011F50                 push    ebp
.text:00011F51                 mov     ebp, esp
.text:00011F53                 sub     esp, 0A58h
.text:00011F59                 push    edi
.text:00011F5A                 mov     eax, [ebp+ParentId]
.text:00011F5D                 mov     [ebp+ParentId1], eax
.text:00011F60                 mov     ecx, [ebp+ProcessId]
.text:00011F63                 mov     [ebp+ProcessId1], ecx
.text:00011F66                 mov     [ebp+var_8], 0
.text:00011F6D                 mov     [ebp+var_4], 0
.text:00011F74                 cmp     [ebp+ProcessId], 0
.text:00011F78                 jnz     short loc_11F7F
.text:00011F7A                 jmp     loc_122B6
.text:00011F7F ; ---------------------------------------------------------------------------
.text:00011F7F
.text:00011F7F loc_11F7F:                              ; CODE XREF: CreateProcessNotifyRoutine+28j
.text:00011F7F                 mov     [ebp+var_10], 0
.text:00011F86                 mov     [ebp+var_C], 0
.text:00011F8D                 mov     [ebp+var_1C], 0
.text:00011F91                 movzx   edx, [ebp+Create]
.text:00011F95                 test    edx, edx
.text:00011F97                 jz      loc_12084
.text:00011F9D                 mov     ecx, ds:FastMutex ; FastMutex
.text:00011FA3                 call    ds:ExAcquireFastMutex
.text:00011FA9                 mov     ecx, ds:FastMutex
.text:00011FAF                 add     ecx, 40h
.text:00011FB2                 call    sub_135A0
.text:00011FB7                 cmp     eax, 2710h
.text:00011FBC                 jnb     loc_12073
.text:00011FC2                 lea     eax, [ebp+ProcessId]
.text:00011FC5                 push    eax
.text:00011FC6                 mov     ecx, ds:FastMutex
.text:00011FCC                 add     ecx, 40h
.text:00011FCF                 call    sub_13B00
.text:00011FD4                 test    eax, eax
.text:00011FD6                 jnz     loc_12073
.text:00011FDC                 mov     [ebp+ProcessFileNameBuffer], 0
.text:00011FE5                 mov     ecx, 81h
.text:00011FEA                 xor     eax, eax
.text:00011FEC                 lea     edi, [ebp+var_226]
.text:00011FF2                 rep stosd
.text:00011FF4                 stosw
.text:00011FF6                 push    208h            ; Size
.text:00011FFB                 lea     ecx, [ebp+ProcessFileNameBuffer]
.text:00012001                 push    ecx             ; Buffer
.text:00012002                 mov     edx, [ebp+ProcessId]
.text:00012005                 push    edx             ; ProcessId
.text:00012006                 mov     ecx, ds:FastMutex
.text:0001200C                 call    GetProcessFileName
其中使用的全局性的_FAST_MUTEX对象
代码:
.text:00011F9D                 mov     ecx, ds:FastMutex ; FastMutex
.text:00011FA3                 call    ds:ExAcquireFastMutex
正是刚才所说的Count很大的_FAST_MUTEX对象,目前是这个线程所有。

该CreateProcessNotifyRoutine在获得这个_FAST_MUTEX之后,获取新创建的进程的映像文件路径:
代码:
.text:00011C60
.text:00011C60 ; =============== S U B R O U T I N E =======================================
.text:00011C60
.text:00011C60 ; Attributes: bp-based frame
.text:00011C60
.text:00011C60 ; int __stdcall GetProcessFileName(int ProcessId, void *Buffer, int Size)
.text:00011C60 GetProcessFileName proc near            ; CODE XREF: CreateProcessNotifyRoutine+BCp
.text:00011C60
.text:00011C60 var_170         = dword ptr -170h
.text:00011C60 len             = dword ptr -16Ch
.text:00011C60 ImagePathBuffer = dword ptr -168h
.text:00011C60 ImagePathName   = dword ptr -164h
.text:00011C60 ProcessPeb      = dword ptr -160h
.text:00011C60 ProcessInformation= PROCESS_BASIC_INFORMATION ptr -15Ch
.text:00011C60 ProcessParameters= dword ptr -144h
.text:00011C60 FreeSize        = dword ptr -140h
.text:00011C60 BaseAddress     = dword ptr -13Ch
.text:00011C60 var_138         = byte ptr -138h
.text:00011C60 var_137         = byte ptr -137h
.text:00011C60 status          = dword ptr -2Ch
.text:00011C60 ObjectAttributes= OBJECT_ATTRIBUTES ptr -28h
.text:00011C60 Handle          = dword ptr -10h
.text:00011C60 ClientId        = CLIENT_ID ptr -0Ch
.text:00011C60 var_4           = dword ptr -4
.text:00011C60 ProcessId       = dword ptr  8
.text:00011C60 Buffer          = dword ptr  0Ch
.text:00011C60 Size            = dword ptr  10h
.text:00011C60
.text:00011C60                 push    ebp
.text:00011C61                 mov     ebp, esp
.text:00011C63                 sub     esp, 170h
.text:00011C69                 push    edi
.text:00011C6A                 mov     [ebp+var_170], ecx
.text:00011C70                 mov     [ebp+var_4], 0
.text:00011C77                 mov     [ebp+Handle], 0
.text:00011C7E                 mov     [ebp+ClientId.UniqueProcess], 0
.text:00011C85                 xor     eax, eax
.text:00011C87                 mov     [ebp+ClientId.UniqueThread], eax
.text:00011C8A                 cmp     [ebp+Buffer], 0
.text:00011C8E                 jnz     short loc_11C97
.text:00011C90                 xor     eax, eax
.text:00011C92                 jmp     loc_11F17
.text:00011C97 ; ---------------------------------------------------------------------------
.text:00011C97
.text:00011C97 loc_11C97:                              ; CODE XREF: GetProcessFileName+2Ej
.text:00011C97                 mov     [ebp+ObjectAttributes.Length], 18h
.text:00011C9E                 mov     [ebp+ObjectAttributes.RootDirectory], 0
.text:00011CA5                 mov     [ebp+ObjectAttributes.Attributes], 0
.text:00011CAC                 mov     [ebp+ObjectAttributes.ObjectName], 0
.text:00011CB3                 mov     [ebp+ObjectAttributes.SecurityDescriptor], 0
.text:00011CBA                 mov     [ebp+ObjectAttributes.SecurityQualityOfService], 0
.text:00011CC1                 mov     ecx, [ebp+ProcessId]
.text:00011CC4                 mov     [ebp+ClientId.UniqueProcess], ecx
.text:00011CC7                 lea     edx, [ebp+ClientId]
.text:00011CCA                 push    edx             ; ClientId
.text:00011CCB                 lea     eax, [ebp+ObjectAttributes]
.text:00011CCE                 push    eax             ; ObjectAttributes
.text:00011CCF                 push    1F0FFFh         ; DesiredAccess
.text:00011CD4                 lea     ecx, [ebp+Handle]
.text:00011CD7                 push    ecx             ; ProcessHandle
.text:00011CD8                 call    ds:ZwOpenProcess
.text:00011CDE                 mov     [ebp+status], eax
.text:00011CE1                 cmp     [ebp+status], 0
.text:00011CE5                 jl      loc_11F14
.text:00011CEB                 mov     edx, ds:FastMutex
.text:00011CF1                 cmp     dword ptr [edx+104h], 0
.text:00011CF8                 jz      loc_11F0A
.text:00011CFE                 mov     [ebp+var_138], 0
.text:00011D05                 mov     ecx, 40h
.text:00011D0A                 xor     eax, eax
.text:00011D0C                 lea     edi, [ebp+var_137]
.text:00011D12                 rep stosd
.text:00011D14                 stosw
.text:00011D16                 stosb
.text:00011D17                 mov     [ebp+BaseAddress], 0
.text:00011D21                 mov     [ebp+FreeSize], 1000h
.text:00011D2B                 push    PAGE_READWRITE  ; Protect
.text:00011D2D                 push    MEM_COMMIT      ; AllocationType
.text:00011D32                 lea     eax, [ebp+FreeSize]
.text:00011D38                 push    eax             ; AllocationSize
.text:00011D39                 push    0               ; ZeroBits
.text:00011D3B                 lea     ecx, [ebp+BaseAddress]
.text:00011D41                 push    ecx             ; BaseAddress
.text:00011D42                 push    0FFFFFFFFh      ; ProcessHandle
.text:00011D44                 call    ds:ZwAllocateVirtualMemory
.text:00011D4A                 mov     [ebp+status], eax
.text:00011D4D                 cmp     [ebp+status], 0
.text:00011D51                 jl      loc_11F0A
.text:00011D57                 mov     [ebp+ProcessPeb], 0
.text:00011D61                 push    0               ; ReturnLength
.text:00011D63                 push    18h             ; ProcessInformationLength
.text:00011D65                 lea     edx, [ebp+ProcessInformation]
.text:00011D6B                 push    edx             ; ProcessInformation
.text:00011D6C                 push    0               ; ProcessInformationClass
.text:00011D6E                 mov     eax, [ebp+Handle]
.text:00011D71                 push    eax             ; ProcessHandle
.text:00011D72                 call    ds:ZwQueryInformationProcess
.text:00011D78                 test    eax, eax
.text:00011D7A                 jl      short loc_11D88
.text:00011D7C                 mov     ecx, [ebp+ProcessInformation.PebBaseAddress]
.text:00011D82                 mov     [ebp+ProcessPeb], ecx
.text:00011D88
.text:00011D88 loc_11D88:                              ; CODE XREF: GetProcessFileName+11Aj
.text:00011D88                 cmp     [ebp+ProcessPeb], 0
.text:00011D8F                 jz      short loc_11DDB
.text:00011D91                 push    0
.text:00011D93                 push    4
.text:00011D95                 mov     edx, [ebp+BaseAddress]
.text:00011D9B                 push    edx
.text:00011D9C                 mov     eax, [ebp+ProcessPeb]
.text:00011DA2                 add     eax, 10h
.text:00011DA5                 push    eax
.text:00011DA6                 mov     ecx, [ebp+Handle]
.text:00011DA9                 push    ecx
.text:00011DAA                 mov     edx, ds:FastMutex
.text:00011DB0                 call    dword ptr [edx+104h] ; NtReadVirtualMemory
.text:00011DB6                 mov     [ebp+status], eax
.text:00011DB9                 cmp     [ebp+status], 0
.text:00011DBD                 jnz     short loc_11DCF
.text:00011DBF                 mov     eax, [ebp+BaseAddress] ; ProcessParameters
.text:00011DC5                 mov     ecx, [eax]      ; _RTL_USER_PROCESS_PARAMETERS
.text:00011DC7                 mov     [ebp+ProcessParameters], ecx
.text:00011DCD                 jmp     short loc_11DD9
.text:00011DCF ; ---------------------------------------------------------------------------
.text:00011DCF
.text:00011DCF loc_11DCF:                              ; CODE XREF: GetProcessFileName+15Dj
.text:00011DCF                 mov     [ebp+ProcessParameters], 20000h
.text:00011DD9
.text:00011DD9 loc_11DD9:                              ; CODE XREF: GetProcessFileName+16Dj
.text:00011DD9                 jmp     short loc_11DE5
.text:00011DDB ; ---------------------------------------------------------------------------
.text:00011DDB
.text:00011DDB loc_11DDB:                              ; CODE XREF: GetProcessFileName+12Fj
.text:00011DDB                 mov     [ebp+ProcessParameters], 20000h
.text:00011DE5
.text:00011DE5 loc_11DE5:                              ; CODE XREF: GetProcessFileName:loc_11DD9j
.text:00011DE5                 mov     edx, [ebp+ProcessParameters]
.text:00011DEB                 add     edx, RTL_USER_PROCESS_PARAMETERS.ImagePathName
.text:00011DEE                 mov     [ebp+ImagePathName], edx
.text:00011DF4                 push    0
.text:00011DF6                 push    8
.text:00011DF8                 mov     eax, [ebp+BaseAddress]
.text:00011DFE                 push    eax
.text:00011DFF                 mov     ecx, [ebp+ImagePathName]
.text:00011E05                 push    ecx
.text:00011E06                 mov     edx, [ebp+Handle]
.text:00011E09                 push    edx
.text:00011E0A                 mov     eax, ds:FastMutex
.text:00011E0F                 call    dword ptr [eax+104h] ; NtReadVirtualMemory
.text:00011E15                 mov     [ebp+status], eax
.text:00011E18                 cmp     [ebp+status], 0
.text:00011E1C                 jl      loc_11EEF
.text:00011E22                 mov     ecx, [ebp+BaseAddress]
.text:00011E28                 mov     edx, [ecx+4]
.text:00011E2B                 mov     [ebp+ImagePathBuffer], edx
.text:00011E31                 mov     eax, [ebp+BaseAddress]
.text:00011E37                 movzx   ecx, word ptr [eax]
.text:00011E3A                 mov     [ebp+len], ecx
.text:00011E40                 mov     edx, [ebp+ImagePathBuffer]
.text:00011E46                 cmp     edx, [ebp+ProcessParameters]
.text:00011E4C                 jnb     short loc_11E60
.text:00011E4E                 mov     eax, [ebp+ImagePathBuffer]
.text:00011E54                 add     eax, [ebp+ProcessParameters]
.text:00011E5A                 mov     [ebp+ImagePathBuffer], eax
.text:00011E60
.text:00011E60 loc_11E60:                              ; CODE XREF: GetProcessFileName+1ECj
.text:00011E60                 cmp     [ebp+len], 0FFFh
.text:00011E6A                 jbe     short loc_11E76
.text:00011E6C                 mov     [ebp+len], 0FFFh
.text:00011E76
.text:00011E76 loc_11E76:                              ; CODE XREF: GetProcessFileName+20Aj
.text:00011E76                 push    0
.text:00011E78                 mov     ecx, [ebp+len]
.text:00011E7E                 push    ecx
.text:00011E7F                 mov     edx, [ebp+BaseAddress]
.text:00011E85                 push    edx
.text:00011E86                 mov     eax, [ebp+ImagePathBuffer]
.text:00011E8C                 push    eax
.text:00011E8D                 mov     ecx, [ebp+Handle]
.text:00011E90                 push    ecx
.text:00011E91                 mov     edx, ds:FastMutex
.text:00011E97                 call    dword ptr [edx+104h]
.text:00011E9D                 mov     [ebp+status], eax
.text:00011EA0                 cmp     [ebp+status], 0
.text:00011EA4                 jl      short loc_11EEF
.text:00011EA6                 mov     eax, [ebp+len]
.text:00011EAC                 add     eax, 2
.text:00011EAF                 cmp     eax, [ebp+Size]
.text:00011EB2                 jbe     short loc_11EB8
.text:00011EB4                 xor     eax, eax
.text:00011EB6                 jmp     short loc_11F17
.text:00011EB8 ; ---------------------------------------------------------------------------
.text:00011EB8
.text:00011EB8 loc_11EB8:                              ; CODE XREF: GetProcessFileName+252j
.text:00011EB8                 mov     ecx, [ebp+len]
.text:00011EBE                 push    ecx             ; size_t
.text:00011EBF                 mov     edx, [ebp+BaseAddress]
.text:00011EC5                 push    edx             ; void *
.text:00011EC6                 mov     eax, [ebp+Buffer]
.text:00011EC9                 push    eax             ; void *
.text:00011ECA                 call    memcpy
.text:00011ECF                 add     esp, 0Ch
.text:00011ED2                 mov     ecx, [ebp+Buffer]
.text:00011ED5                 push    ecx             ; wchar_t *
.text:00011ED6                 call    ConvertToNormalPath
.text:00011EDB                 mov     edx, [ebp+Buffer]
.text:00011EDE                 push    edx             ; wchar_t *
.text:00011EDF                 call    ds:_wcsupr
.text:00011EE5                 add     esp, 4
.text:00011EE8                 mov     [ebp+var_4], 1
.text:00011EEF
.text:00011EEF loc_11EEF:                              ; CODE XREF: GetProcessFileName+1BCj
.text:00011EEF                                         ; GetProcessFileName+244j
.text:00011EEF                 push    8000h           ; FreeType
.text:00011EF4                 lea     eax, [ebp+FreeSize]
.text:00011EFA                 push    eax             ; FreeSize
.text:00011EFB                 lea     ecx, [ebp+BaseAddress]
.text:00011F01                 push    ecx             ; BaseAddress
.text:00011F02                 push    0FFFFFFFFh      ; ProcessHandle
.text:00011F04                 call    ds:ZwFreeVirtualMemory
.text:00011F0A
.text:00011F0A loc_11F0A:                              ; CODE XREF: GetProcessFileName+98j
.text:00011F0A                                         ; GetProcessFileName+F1j
.text:00011F0A                 mov     edx, [ebp+Handle]
.text:00011F0D                 push    edx             ; Handle
.text:00011F0E                 call    ds:ZwClose
.text:00011F14
.text:00011F14 loc_11F14:                              ; CODE XREF: GetProcessFileName+85j
.text:00011F14                 mov     eax, [ebp+var_4]
.text:00011F17
.text:00011F17 loc_11F17:                              ; CODE XREF: GetProcessFileName+32j
.text:00011F17                                         ; GetProcessFileName+256j
.text:00011F17                 pop     edi
.text:00011F18                 mov     esp, ebp
.text:00011F1A                 pop     ebp
.text:00011F1B                 retn    0Ch
.text:00011F1B GetProcessFileName endp
.text:00011F1B
这里调用ZwAllocateVirtualMemory给在当前进程(explorer.exe)申请了一块内存,之后通过读子进程的PEB得到映像文件路径并copy到堆栈里。

于是从栈回溯结果可以推断,NtAllocateVirtualMemory里的ExfAcquirePushLockExclusive获取某个PushLock的时候处在等待状态,从而导致HOOKHELP.sys中其他需要获取这个HOOKHELP.sys的全局Fast Mutex的线程都只能等待了。

这个ZwAllocateVirtualMemory的问题,我自己遇到过,曾经在LoadImageNotifyRoutine中调用ZwAllocateVirtualMemory,结果那个线程死锁了。
网上有debugman的帖子http://www.debugman.com/discussion/1110/关于zwallocatevirtualmemory/p1,询问关于这个问题,但是没有结果。
搜了一下又发现OSR里有一篇:http://www.osronline.com/cf.cfm?PageURL=showThread.CFM?link=183262,其中提到:
代码:
> Well i think i've found the reason of the deadlock. I've assumed the
> callback function is always executed at PASSIVE IRQL LEVEL. However
> after first two loaded modules, IRQL raises to APC LEVEL :-(

This is a known limitation. For user image loads the callback is
invoked with the process address space lock held, so you can't
call any APIs which might directly or indirectly try to acquire
the same lock.
还有另个名叫胡宇光的程序员的live空间(http://huyuguang1976.spaces.live.com/),他自己遇到了这个问题并提出了相应的解释,不过由于微软live空间已停止服务,我只能从google快照中看到他的描述:
代码:
......
6,如果不是new created process,例如是随后的dll导致的callback,或者是父进程导致的exe的callback,堆栈如下(x86 win7):
8bb01b3c 83c9abc6 nt!PsCallImageNotifyRoutines+0x62
8bb01be8 83cb3979 nt!MiMapViewOfImageSection+0x7fd
8bb01c58 83cc4241 nt!MiMapViewOfSection+0x22e
8bb01c88 83cc416c nt!MmMapViewOfSection+0x2a
8bb01d04 83a8844a nt!NtMapViewOfSection+0x204
8bb01d04 76eb64f4 nt!KiFastCallEntry+0x12a
......
8,场景不同有什么意义?
第一种场景下,可以调用ZwQueryVirtualMemory等函数
第二种场景下,这些函数都不能调用。原因是在第二种场景下,系统在进入load image callback之前,在MmMapViewOfSection中会调用
#define LOCK_ADDRESS_SPACE(PROCESS)                                  \
            ExAcquireFastMutex( &((PROCESS)->AddressCreationLock))获得Mutex。
(以上#define来自win2k src code,如果是2k3以上,不是fast mutex,而是push lock,但类似。不同os获得的锁不尽相同,但总之都需要获得锁,并且锁都是不可递归获取的)
并且在load image callback结束后回到MmMapViewOfSection才释放。

9,有什么影响?
ZwQueryVirtualMemory/Allocate/Protect同样会获得这个mutex或者push lock。因此,如果是第二种场景,在load image callback中调用ZwQueryVirtualMemory将导致deadlock

。win2k的代码是LOCK_WS_AND_ADDRESS_SPACE。
......
此问题在create process/thread callback中不存在。因为这2个callback不涉及map view/memory,所以很容易理解。
......
引自http://huyuguang1976.spaces.live.com/(原文链接已失效)
我的情况,调用者(HOOKHELP.sys的CreateProcessNotifyRoutine)在调用了ExAcquireFastMutex之后确实应该处于APC_LEVEL,进入NtAllocateVirtualMemory后的相应操作代码我也依照上面栈回溯信息找到了:
代码:
NtAllocateVirtualMemory的相应位置:
0: kd> u 83466cc5
nt!NtAllocateVirtualMemory+0x1263:
83466cc5 8b4de4          mov     ecx,dword ptr [ebp-1Ch];EPROCESS pointer
83466cc8 81c100010000    add     ecx,100h;_EPROCESS.AddressCreationLock
83466cce 8bc1            mov     eax,ecx
83466cd0 f00fba2800      lock bts dword ptr [eax],0
83466cd5 7305            jae     nt!NtAllocateVirtualMemory+0x127a (83466cdc)
83466cd7 e8b15ee6ff      call    nt!ExfAcquirePushLockExclusive (832ccb8d)
83466cdc 808b8902000002  or      byte ptr [ebx+289h],2
83466ce3 8b45e4          mov     eax,dword ptr [ebp-1Ch]
确实是EPROCESS的AddressCreationLock,按照栈回溯信息:
代码:
bd82e42c 832781ea ffffffff bd82e514 00000000 nt!NtAllocateVirtualMemory+0x127a
bd82e42c就是进入NtAllocateVirtualMemory后的ebp
0: kd> dd bd82e42c-1c
bd82e410  855a4d40 bd82e34c 8ec7821f ffffffff
bd82e420  832f9ccd a6e177ec fffffffe bd82e44c
bd82e430  832781ea ffffffff bd82e514 00000000
bd82e440  bd82e510 00001000 00000000 bd82e650
bd82e450  832758e1 badb0d00 bd82e4c4 ba000d00
bd82e460  bd82e4cc 83001703 00000000 bd82ee00
bd82e470  807c6120 bd82e484 00000000 00010022
bd82e480  00000000 bd82e4c4 bd82fd34 832b48c6
855a4d40确实是explorer.exe的EPROCESS。
代码:
0: kd> dt -r _EPROCESS 855a4d40
nt!_EPROCESS
...
   +0x100 AddressCreationLock : _EX_PUSH_LOCK
      +0x000 Locked           : 0y1
      +0x000 Waiting          : 0y1
      +0x000 Waking           : 0y0
      +0x000 MultipleShared   : 0y0
      +0x000 Shared           : 0y1100010100011111011010101011 (0xc51f6ab)
      +0x000 Value            : 0xc51f6ab3
      +0x000 Ptr              : 0xc51f6ab3 Void
AddressCreationLock的Locked和Waiting确实置位了。

但是问题是,这个NtAllocateVirtualMemory调用是在CreateProcessNotifyRoutine里面做的,按理应该不会有这样的问题了才对。也就是说很可能不是这个线程本身先锁定了AddressCreationLock,而是被另一个线程先锁定了,那另一个线程又怎么会不解锁呢?

再在explorer.exe里找找,结果发现了这个线程:
代码:
        THREAD 8562f838  Cid 071c.0814  Teb: 7ff9a000 Win32Thread: fe5121d8 WAIT: (WrFastMutex) KernelMode Non-Alertable
            86e15014  SynchronizationEvent
        Not impersonating
        DeviceMap                 c14ef898
        Owning Process            855a4d40       Image:         explorer.exe
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      11758          Ticks: 14076 (0:00:03:39.587)
        Context Switch Count      42             
        UserTime                  00:00:00.000
        KernelTime                00:00:00.000
        Win32 Start Address 0x0025ad5f
        Stack Init c51ebfd0 Current c51eb198 Base c51ec000 Limit c51e9000 Call 0
        Priority 9 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
        ChildEBP RetAddr  
        c51eb1b0 832b869d nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
        c51eb1e8 832b74f7 nt!KiSwapThread+0x266
        c51eb210 832b10cf nt!KiCommitThreadWait+0x1df
        c51eb288 8326b08e nt!KeWaitForSingleObject+0x393
        c51eb2b0 832ce55b nt!KiAcquireFastMutex+0x56
        c51eb2bc 8ece16c7 nt!ExAcquireFastMutex+0x1e (FPO: [0,0,2])
WARNING: Stack unwind information not available. Following frames may be wrong.
        c51ebb0c 834a939c HOOKHELP!RisingInlineUnHook+0x1467
        c51ebb34 8349127f nt!PsCallImageNotifyRoutines+0x62
        c51ebbe8 83481d4a nt!MiMapViewOfImageSection+0x670
        c51ebc58 83481e3a nt!MiMapViewOfSection+0x22e
        c51ebc88 83482599 nt!MmMapViewOfSection+0x2a
        c51ebd04 832781ea nt!NtMapViewOfSection+0x204
        c51ebd04 779970b4 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ c51ebd34)
        04e6f948 00000000 0x779970b4
0: kd> .thread 8562f838
Implicit thread is now 8562f838
0: kd> kb
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  Args to Child              
c51eb1b0 832b869d 8562f838 00000000 83365d20 nt!KiSwapContext+0x26
c51eb1e8 832b74f7 8562f8f8 8562f838 86e15014 nt!KiSwapThread+0x266
c51eb210 832b10cf 8562f838 8562f8f8 00000000 nt!KiCommitThreadWait+0x1df
c51eb288 8326b08e 86e15014 00000022 00000000 nt!KeWaitForSingleObject+0x393
c51eb2b0 832ce55b 8562f838 8e015390 8ece16c7 nt!KiAcquireFastMutex+0x56
c51eb2bc 8ece16c7 8615c480 00000000 c51eb304 nt!ExAcquireFastMutex+0x1e
WARNING: Stack unwind information not available. Following frames may be wrong.
c51ebb0c 834a939c a2dae2e0 0000071c c51ebb5c HOOKHELP!RisingInlineUnHook+0x1467
c51ebb34 8349127f a2dae2e0 0000071c 8337cb88 nt!PsCallImageNotifyRoutines+0x62
c51ebbe8 83481d4a a1baa568 855a4d40 c51ebce4 nt!MiMapViewOfImageSection+0x670
c51ebc58 83481e3a 855a4d40 c51ebce4 00000000 nt!MiMapViewOfSection+0x22e
c51ebc88 83482599 c4d32150 855a4d40 c51ebce4 nt!MmMapViewOfSection+0x2a
c51ebd04 832781ea 000006a0 ffffffff 04e6f9dc nt!NtMapViewOfSection+0x204
c51ebd04 779970b4 000006a0 ffffffff 04e6f9dc nt!KiFastCallEntry+0x12a
04e6f948 00000000 00000000 00000000 00000000 0x779970b4
0: kd> dc a2dae2e0
a2dae2e0  0078003c b54556f8 00000000 00000000  <.x..VE.........
a2dae2f0  00000000 00000000 00000000 00040001  ................
a2dae300  00000000 a2dae304 a2dae304 00040000  ................
a2dae310  00000000 a2dae314 a2dae314 00000000  ................
a2dae320  00000000 a2dae324 a2dae324 00000000  ....$...$.......
a2dae330  04080017 ee657645 00000000 00000040  ....Eve.....@...
a2dae340  00000000 00000000 00000001 00000001  ................
a2dae350  00000000 0008000c 83370cc0 00000000  ..........7.....
0: kd> dc b54556f8
b54556f8  0057005c 006e0069 006f0064 00730077  \.W.i.n.d.o.w.s.
b5455708  0053005c 00730079 00650074 0033006d  \.S.y.s.t.e.m.3.
b5455718  005c0032 00750041 00690064 0053006f  2.\.A.u.d.i.o.S.
b5455728  00730065 0064002e 006c006c 548d0000  e.s...d.l.l....T
b5455738  f28bff12 f63302eb 850ff685 fffff5dd  ......3.........
b5455748  3bf2508b 7d74f251 0ff2b60f 2bf251b6  .P.;Q.t}.....Q.+
b5455758  331574f2 0ff685d2 548dc29f f28bff12  .t.3.......T....
b5455768  850ff685 fffff5b5 00010210 e24e4d43  ............CMN.
explorer.exe的这个线程在加载AudioSes.dll的时候进入了HOOKHELP.sys的LoadImageNotifyRoutine,并同样停在了ExAcquireFastMutex对HOOKHELP.sys全局Fast Mutex的获取中。

但是重点是,如上面所提到的,这个线程在PsCallImageNotifyRoutines前,在MiMapViewOfSection中已经锁定了explorer.exe进程的AddressCreationLock:
代码:
PAGE:00647C86                      loc_647C86:                             ; CODE XREF: MiMapViewOfSection(x,x,x,x,x,x,x,x,x,x,x,x)+15Ej
PAGE:00647C86 8B 03                                mov     eax, [ebx]
PAGE:00647C88 64 8B 3D 24 01 00 00                 mov     edi, large fs:124h
PAGE:00647C8F 89 44 24 14                          mov     [esp+40h+var_2C], eax
PAGE:00647C93 8B 45 08                             mov     eax, [ebp+Eprocess]
PAGE:00647C96 39 47 50                             cmp     [edi+50h], eax
PAGE:00647C99 74 16                                jz      short loc_647CB1
PAGE:00647C9B 8D 4C 24 28                          lea     ecx, [esp+40h+var_18]
PAGE:00647C9F 51                                   push    ecx             ; int
PAGE:00647CA0 50                                   push    eax             ; Eprocess
PAGE:00647CA1 E8 0B 41 E5 FF                       call    _KeStackAttachProcess@8 ; KeStackAttachProcess(x,x)
PAGE:00647CA6 8B 45 08                             mov     eax, [ebp+Eprocess]
PAGE:00647CA9 C7 44 24 10 01 00 00+                mov     [esp+40h+var_30], 1
PAGE:00647CB1
PAGE:00647CB1                      loc_647CB1:                             ; CODE XREF: MiMapViewOfSection(x,x,x,x,x,x,x,x,x,x,x,x)+17Dj
PAGE:00647CB1 66 FF 8F 86 00 00 00                 dec     word ptr [edi+86h]
PAGE:00647CB8 8D 88 00 01 00 00                    lea     ecx, [eax+100h];AddressCreationLock
PAGE:00647CBE 89 4C 24 20                          mov     [esp+40h+var_20], ecx
PAGE:00647CC2 8B D1                                mov     edx, ecx
PAGE:00647CC4 F0 0F BA 2A 00                       lock bts dword ptr [edx], 0
PAGE:00647CC9 73 08                                jnb     short loc_647CD3
PAGE:00647CCB E8 BD AE E4 FF                       call    @ExfAcquirePushLockExclusive@4 ; ExfAcquirePushLockExclusive(x)
由此推断,explorer.exe进程的AddressCreationLock此时应该属于它所有。

于是整个过程清晰了:

0. 系统启动时,瑞星的HOOKHELP.sys这个内核dll加载,hooksys.sys通过调用它进行底层HOOK和挂NotifyRoutine的工作。HOOKHELP.sys初始化了一个全局性的Fast Mutex(我称为HookHelpMutex)。其实这个Fast Mutex后部还放了一些HOOKHELP.sys自定义的内容。

1. 在本例中,explorer.exe进程启动过程中,其中一个线程加载AudioSes.dll的时候,在MiMapViewOfSection中调用ExfAcquirePushLockExclusive,获取了explorer.exe进程的
AddressCreationLock。

2. 与1大概同时,explorer.exe进程的另一个线程执行启动微软输入法IMSCMIG.EXE进程的过程中,进入了HOOKHELP.sys的CreateProcessNotifyRoutine,后者调用ExAcquireFastMutex获取了HookHelpMutex。

3. 随后,加载AudioSes.dll的线程进入了HOOKHELP.sys的LoadImageNotifyRoutine,同样调用ExAcquireFastMutex试图获取HookHelpMutex,由于此时HookHelpMutex已被创建IMSCMIG.EXE进程的线程获取,加载AudioSes.dll的线程只能等待。

4. 最终,创建IMSCMIG.EXE进程的线程调用了ZwAllocateVirtualMemory,后者调用ExfAcquirePushLockExclusive试图获取explorer.exe进程的AddressCreationLock,由于它已被加载AudioSes.dll的线程获取并且未释放,创建IMSCMIG.EXE进程的线程也只能等待。

加载AudioSes.dll的线程的继续,依赖于创建IMSCMIG.EXE进程的线程释放HookHelpMutex,然而后者请求的explorer.exe进程的AddressCreationLock又已被前者获取,因此两个线程之间形成了deadlock。


如果只是这两个线程之间deadlock,影响不大,但是由于HookHelpMutex对于HOOKHELP.sys来说是全局性的,HOOKHELP.sys中大量系统底层HOOK的函数要通过它进行同步,因此它的被死锁导致了很大一部分线程一进入系统底层调用就停在ExAcquireFastMutex中。只有那些不跳进HOOKHELP.sys的线程,比如进入win32k.sys的线程,由于瑞星2011似乎没有做SSDT Shadow HOOK而幸免于难,所以鼠标还能动,但是点什么什么没反应……

分析到此结束,我能想到的建议,是希望瑞星改进一下这个全局Fast Mutex的设置,比如对LoadImageNotifyRoutine做特殊处理等,保证这个回调尽量不要被其他例程锁住,要不然这种死锁就会有几率性地出现。