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 *) 

Saturday, March 27, 2010

How to develop a user mode debugger under Windows / x86 platform

 

Have you ever wonder how Visual Studio do debugging things like the picture below?

Picture1

I made a slide about how to implement a debugger on Windows / x86 platform. In this sharing, we will discuss the challenge for OS to support a ring 3 debugger, the overview of Windows Ring 3 debugging, and how to implement the basic features of a debugger.

Understanding oplock and avoid sharing violation

Opportunistic locking (oplock) is a cache coherence protocol. General relations between Local FSD(file system driver), Server FSD, user and filter driver can be described in the picture below :oplock
In this sharing we are going to disscus the problem that oplock wants to solve, FILE_COMPLETE_IF_OPLOCKED flag, and different types of oplock.

Filter oplock was introduced on Windows 2000, it was design to solve sharing violatoin issue. For example, we have a kernel driver that will access \systemroot\OplockTest with only share access FILE_SHARE_READ. Now if we use notepad to modify the file at the same time, a sharing violation will occure :
ShareViolation
With filter oplock, kernel driver will be notified if a sharing violation is happenning. After driver acknowledge oplock break, notepad will be able to modify the file without sharing violation. User is totally unware all of these events.
Acknowledge Oplock Break
Although we give a kernel mode example here, we can use filter oplock in user mode. Simply replace the function with Win32 API and use DeviceIoCtl to send FSCTL_REQUEST_FILTER_OPLOCK request.
status = ZwCreateEvent(&EventHandle,
                       EVENT_ALL_ACCESS,
                       0,
                       NotificationEvent,FALSE);
  if (NT_SUCCESS(status))
  {
      
      InitializeObjectAttributes(&oa,
      MagicFilePath,
      OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
      0,0);
      
      //notice here, AP will get sharing violation 
      //if they try to modify 
      //"\\SystemRoot\\OplockTest", 
      //with oplock we can avoid sharing violation.   
      status = ZwCreateFile(&MagicFileHandle,
          GENERIC_READ,
          &oa,
          &iosb,
          0,
          0,
          FILE_SHARE_READ,  
          FILE_OPEN_IF,
          FILE_NON_DIRECTORY_FILE,0,0);
 
      if (NT_SUCCESS(status))
      {
          status = ZwFsControlFile(MagicFileHandle,
                          EventHandle,
                          NULL,
                          NULL,
                          &iosb,
                          FSCTL_REQUEST_FILTER_OPLOCK,
                          0,
                          0,
                          0,
                          0);
          //
          //STATUS_PENDING means we got oplock, 
          //
          if (status == STATUS_PENDING)
          {
          //
          //Event will be signalled when the sharing 
          //violation happen. Polling the oplock event to 
          //see if the oplock has broken. You can 
          //wait for the event in a different thread.
          //
              LARGE_INTEGER  Timeout;
              Timeout.QuadPart = (LONGLONG)-10000000;
              while(TRUE)
              {
 
                  status = ZwWaitForSingleObject(
                                          EventHandle,
                                          false,
                                          &Timeout);
                  if (!NT_SUCCESS(status))
                  {
                      //
                      //error happened
                      //
                      goto CleanupAndExit;
                  } 
                  else if(status == STATUS_TIMEOUT)
                  {
                      //
                      //  Perform major task here.  
                      //
 
                      KdPrint(("access file...\n",
                               status));
                      continue;
                  }
                  else
                  {
                      //
                      //Some operation is waiting 
                      //quickly finish up and get out. 
                      //
              KdPrint(("ackonwledge oplock break \n",
              status));
                      goto CleanupAndExit;
                  }
              }
          }
      }        
  }
  
eanupAndExit:
  
  if (MagicFileHandle)
  {
  //
  //close handle will relinquishes oplock (acknowledge 
  //the oplock break) Until acknowledge oplock break 
  //AP thread will <<<<<HANG>>>>> if AP doesn't specify 
  //FILE_COMPLETE_IF_OPLOCKED in CreateFile
  //
      ZwClose(MagicFileHandle);
      MagicFileHandle = 0;
  }