如題,一種是利用異常處理例程進行反調試:首先安裝好一個異常處理例程,然後人為抛出異常在異常處理例程中通過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:
進入異常時,[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
當程式從異常處理例程傳回時,又可以見到對話框了:
接下來看下第二種反調試方法:
#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);
}
}
這段代碼,還是讀者自己試下,我暫時沒想到如果繞過檢測的方法。