Friday, June 18, 2010

How to check top level unhandle exception filter by windbg


Many applications use SetUnhandledExceptionFilter to catch unexpeted exception. This API let application be able to hook top-level exception handler of a process. Through it app has a chance to dump debugging information or report abnormal status before process exit. This function also be used as an anti-debugging trick: If a debugger attach to a process, top-level exception handler won’t be invoked, so we can hide our logic inside the exception handler.

Sounds good, but there can be only one top-level exception handler for a process. If not only one module, could be legacy code or 3-party lib, uses SetUnhandledExceptionFilter, the result may be unwanted.

How to check top-level exception handler by windbg? Although this is a process-specific information, we need full memory dump or two machine live debugging to get the handler. First, set the process context to the process you want to check, search for the       kernel32!BasepCurrentTopLevelFilter, this is where the pointer to the top-level exception handler stored :

   1: kd> x kernel32!BasepCurrentTopLevelFilter 
   2: 76d0a5d0 kernel32!BasepCurrentTopLevelFilter = 
   3: kd> dd 76d0a5d0 L1 
   4: 76d0a5d0  d849453d 

So the address of the handler is 0xd849456d? No. Since WinXP SP2, Windows will encoding the addres. We have to decode it. On Vista SP2, basicly encoding is XOR with process cookie. Cookie is avaliable in EPROCESS structure. That is why we need full memory dump or two machine live debugging.

   1: kd> dt _EPROCESS 83625d90       
   2: ntdll!_EPROCESS 
   3:    +0x000 Pcb              : _KPROCESS 
   4:    +0x080 ProcessLock      : _EX_PUSH_LOCK 
   5:    +0x088 CreateTime       : _LARGE_INTEGER 0x1cb01ba`83f00f26 
   6:    … skipped
   7:    +0x234 PriorityClass    : 0x2 '' 
   8:    +0x238 VadRoot          : _MM_AVL_TABLE 
   9:    +0x258 Cookie           : 0x4905fc08 
  10:    +0x25c AlpcContext      : _ALPC_PROCESS_CONTEXT 
Cookie is 0x4905fc08. The encoding process also include magic number and shift operation. Let’s check the assembley of decoding :
   1: kd> uf ntdll!rtldecodepointer 
   2: ntdll!RtlDecodePointer: 
   3: 77170a18 8bff            mov     edi,edi 
   4: 77170a1a 55              push    ebp 
   5: 77170a1b 8bec            mov     ebp,esp 
   6: 77170a1d 51              push    ecx 
   7: 77170a1e 6a00            push    0 
   8: 77170a20 6a04            push    4 
   9: 77170a22 8d45fc          lea     eax,[ebp-4] 
  10: 77170a25 50              push    eax 
  11: 77170a26 6a24            push    24h 
  12: 77170a28 6aff            push    0FFFFFFFFh 
  13: 77170a2a e865f40200      call    ntdll!ZwQueryInformationProcess (7719fe94) 
  14: 77170a2f 85c0            test    eax,eax 
  15: 77170a31 0f8cf8660400    jl      ntdll!RtlDecodePointer+0x1b (771b712f) 
  16:  
  17: ntdll!RtlDecodePointer+0x21: 
  18: 77170a37 8a45fc          mov     al,byte ptr [ebp-4] 
  19: 77170a3a 241f            and     al,1Fh 
  20: 77170a3c b120            mov     cl,20h 
  21: 77170a3e 2ac8            sub     cl,al 
  22: 77170a40 8b4508          mov     eax,dword ptr [ebp+8] 
  23: 77170a43 d3c8            ror     eax,cl 
  24: 77170a45 3345fc          xor     eax,dword ptr [ebp-4] 
  25: 77170a48 c9              leave 
  26: 77170a49 c20400          ret     4 
It will be painful to use calculator to decode, so I made a small program :
   1: void main(){ 
   2:  DWORD Pointer = 0xd849456d ; 
   3:  DWORD Cookie = 0x4905fc08; 
   4:  DWORD result = 0; 
   5:  // 
   6:  //decoding process 
   7:  // 
   8:  __asm{ 
   9:   mov     al,byte ptr [Cookie] 
  10:   and     al,1Fh 
  11:   mov     cl,20h 
  12:   sub     cl,al 
  13:   mov     eax,dword ptr [Pointer] 
  14:   ror     eax,cl 
  15:   xor     eax,dword ptr [Cookie] 
  16:   mov     result,eax 
  17:  }; 
  18: } 
The result is 0x0040C1DO, it is the current top-level exception handler. Use “ln” to check if it is what we want.
   1: kd> ln 0040C1D0 
   2: d:\xxx\src\utility\util_crashhandler.cpp(xxx) 
   3: (0040c1d0)   xxx!CrashHandlerExceptionFilter   |  (0040c310)   xxx!SetCrashHandlerFilter 
   4: Exact matches: 
   5:     xxx!CrashHandlerExceptionFilter (struct _EXCEPTION_POINTERS *)