利用 DebugAPI 调试程序时,系统通过 DbgkForwardException 函数给调试器发送异常等消息。DbgkForwardException 函数又会调用 DbgkpSendApiMessage 函数给调试对象发送调试消息。DbgkpSendApiMessage 再调用 DbgkpQueueMessage 函数把调试消息插入到调试对象的事件队列里面,插入前先获取一个 DbgkpProcessDebugPortMutex 快速互斥体。

根据调试器的这个流程,我们可以写一个驱动始终占有 DbgkpProcessDebugPortMutex,那么被调试进程就会挂在内核代码里面,始终得不到执行。下面用 WinDbg 来演示这个过程。

用 OllyDbg 打开一个程序开始调试,这时候单步什么的都很正常。然后打开 WinDbg 的本地内核调试,

lkd> x nt!DbgkpProcessDebugPortMutex
805576c0 nt!DbgkpProcessDebugPortMutex = <no type information>

lkd> dt nt!_FAST_MUTEX 0x805576c0 /b
   +0x000 Count            : 1
   +0x004 Owner            : (null) 
   +0x008 Contention       : 0
   +0x00c Event            : _KEVENT
      +0x000 Header           : _DISPATCHER_HEADER
         +0x000 Type             : 0x1 ''
         +0x001 Absolute         : 0 ''
         +0x002 Size             : 0x4 ''
         +0x003 Inserted         : 0 ''
         +0x004 SignalState      : 0
         +0x008 WaitListHead     : _LIST_ENTRY [ 0x805576d4 - 0x805576d4 ]
            +0x000 Flink            : 0x805576d4 
            +0x004 Blink            : 0x805576d4 
   +0x01c OldIrql          : 0

lkd> ed 0x805576c0 0

lkd> dt nt!_FAST_MUTEX 0x805576c0 /b
   +0x000 Count            : 0
   +0x004 Owner            : (null) 
   +0x008 Contention       : 0
   +0x00c Event            : _KEVENT
      +0x000 Header           : _DISPATCHER_HEADER
         +0x000 Type             : 0x1 ''
         +0x001 Absolute         : 0 ''
         +0x002 Size             : 0x4 ''
         +0x003 Inserted         : 0 ''
         +0x004 SignalState      : 0
         +0x008 WaitListHead     : _LIST_ENTRY [ 0x805576d4 - 0x805576d4 ]
            +0x000 Flink            : 0x805576d4 
            +0x004 Blink            : 0x805576d4 
   +0x01c OldIrql          : 0

把 _FAST_MUTEX.Count 改为 0,相当于占住了这个快速互斥体。(这里的效果不确定,跟踪发现执行 ExAcquireFastMutex 后就是这个动作,所以这里也就这样做了。^_^)

然后回到 OllyDbg 发现单步没有作用了,原因就是被调试进程挂起在 DbgkpQueueMessage 函数里面,正在等待 DbgkpProcessDebugPortMutex 这个快速互斥体。

lkd> !process 0n3868 2
Searching for Process with Cid == f1c
PROCESS 84aa1020  SessionId: 0  Cid: 0f1c    Peb: 7ffdf000  ParentCid: 0b90
    DirBase: 0a441000  ObjectTable: e3e59eb8  HandleCount:  13.
    Image: DLL_Loader.exe

        THREAD 851a5178  Cid 0f1c.0f20  Teb: 7ffde000 Win32Thread: e11d9bf0 WAIT: (Executive) KernelMode Non-Alertable
            805576cc  SynchronizationEvent

lkd> ln 0x805576cc
(805576c0)   nt!DbgkpProcessDebugPortMutex+0xc   |  (805576e0)   nt!___PchSym_

nt!DbgkpProcessDebugPortMutex+0xc 就是 _FAST_MUTEX 里面的 Event。

这样修改之后,整个系统的 ring3 调试都不起作用了,反调试的代价有点大,呵呵!