天天看點

2種基于異常機制的反調試方法

    如題,一種是利用異常處理例程進行反調試:首先安裝好一個異常處理例程,然後人為抛出異常在異常處理例程中通過IsDebuggerPresent來判斷程式是否被調試;另一種是利用未處理異常進行反跟蹤,其原理是:當異常發生時所有異常處理例程都不能處理異常,系統線程異常處理例程将起作用,它調用 ZwQueryInformationProcess 判斷是否被調試, 

如果沒有調試并且程式中調用SetUnhandledExceptionFilter安裝了最後異常處理例程的話,系統轉向對它的調用。

    第一種方法中IsDebuggerPresent是通過傳回FS寄存器上記錄的位址的一些偏移量來實作的。在debugger中可以任意操作目前程序記憶體位址上的值,是以隻需要用調試器把[[FS:30]:2的值修改成0,IsDebuggerPresent就會傳回false,導緻判斷失敗。相比第二種方法中UnhandledExceptionFilter是否調用取決于系統核心的判斷。使用者态的調試器要想改變這個行為就得頗費功夫了。下面我将依次展示兩種反調試方法。

    第一種方法,通過在異常處理例程中判斷IsDebuggerPresent的傳回值:

#include <windows.h>

int AntiDebugInSEH(EXCEPTION_POINTERS* ExceptionInfo)
{
  DWORD state = 0x00;

  __asm
  {
    mov eax,dword ptr fs:[30h];
    movzx eax, byte ptr ds:[eax+2h];
    mov state,eax;
  }

  ExceptionInfo->ContextRecord->Eax = state;
  ExceptionInfo->ContextRecord->Eip = 0x06+(DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress;
  return EXCEPTION_CONTINUE_EXECUTION;
}

int main()
{
  int i=0;
  __try
  {
    __asm
    {
      xor eax,eax;
      mov dword ptr [eax],0; //觸發異常
    }
    __asm
    {
                    //異常傳回後,eax的值為AntiDebugInSEH中ExceptionInfo->ContextRecord->Eax設定的值                    
                    mov i,eax
    }
    if(i==1)
    {}
    else
    {
      MessageBox(NULL,"","",0);
    }
    i++;
  }
  __except(AntiDebugInSEH(GetExceptionInformation()))
  {
    
  }
}      

上面這段代碼,如果正常運作将彈出對話框,如果調試運作對話框是看不到了。文章中也提到了,可以通過修改[[fs:30]:2]處的記憶體位址使IsDebuggerPresent失效,好的,祭出windbg:

2種基于異常機制的反調試方法

進入異常時,[fs:[30h]:2]處的值為0x0001,即正在調試狀态:

0:000> r eax
eax=7ffd5000
0:000> dd eax
7ffd5000  00010000 ffffffff 00400000 76fb8880
7ffd5010  00291940 00000000 00290000 76fb8380      

為了使IsDebuggerPresent失效,修改0x7ffd5000處的記憶體值:

0:000> ed 7ffd5000  0x00000000
0:000> dd eax
7ffd5000  00000000 ffffffff 00400000 76fb8880
7ffd5010  00291940 00000000 00290000 76fb8380      

當程式從異常處理例程傳回時,又可以見到對話框了:

2種基于異常機制的反調試方法

接下來看下第二種反調試方法:

#include <windows.h>

LONG WINAPI UnhandledExcept(EXCEPTION_POINTERS *pExpInfo)  
{
  if(pExpInfo->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT)
  {
    return EXCEPTION_EXECUTE_HANDLER;
  }
  else
  {
    pExpInfo->ContextRecord->Eip = (DWORD)(pExpInfo->ContextRecord->Eip+1);
    pExpInfo->ContextRecord->Eax = 0x0000FEEE;
    return EXCEPTION_CONTINUE_EXECUTION;
  }
}

int main()
{
  HMODULE hMod = LoadLibrary("kernel32.dll");
  DWORD* funcAddr = (DWORD*)GetProcAddress(hMod,"ExitProcess");
  
  SetUnhandledExceptionFilter(UnhandledExcept);
  _asm int 3;
  __asm
  {
    cmp ax,0xFEEE;
    jz next;
    push 0;
    mov eax,funcAddr;
    call eax;
next:
  }
  {
    MessageBox(NULL,"","",MB_OK);
  }
}      

這段代碼,還是讀者自己試下,我暫時沒想到如果繞過檢測的方法。