1. 程式人生 > >檢查進程是否被調試

檢查進程是否被調試

關閉進程 validate tla 獲取 運行 win32 span typedef pan

轉自:http://www.cnblogs.com/this-543273659/archive/2013/03/04/2943380.html

在調試一些病毒程序的時候,可能會碰到一些反調試技術,也就是說,被調試的程序可以檢測到自己是否被調試器附加了,如果探知自己正在被調試,肯定是有人試圖反匯編啦之類的方法破解自己。為了了解如何破解反調試技術,首先我們來看看反調試技術。 一、Windows API方法 Win32提供了兩個API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用來檢測當前進程是否正在被調試,以IsDebuggerPresent函數為例,例子如下:
BOOL ret = IsDebuggerPresent(); printf("ret = %d\n", ret); 破解方法很簡單,就是在系統裏將這兩個函數hook掉,讓這兩個函數一直返回false就可以了,網上有很多做hook API工作的工具,也有很多工具源代碼是開放的,所以這裏就不細談了。 二、查詢進程PEB的BeingDebugged標誌位 當進程被調試器所附加的時候,操作系統會自動設置這個標誌位,因此在程序裏定期查詢這個標誌位就可以了,例子如下: bool PebIsDebuggedApproach() { char result = 0; __asm
{ // 進程的PEB地址放在fs這個寄存器位置上 mov eax, fs:[30h] // 查詢BeingDebugged標誌位 mov al, BYTE PTR [eax + 2] mov result, al } return result != 0; } 三、查詢進程PEB的NtGlobal標誌位 跟第二個方法一樣,當進程被調試的時候,操作系統除了修改BeingDebugged這個標誌位以外,還會修改其他幾個地方,其中NtDll中一些控制堆(Heap)操作的函數的標誌位就會被修改,因此也可以查詢這個標誌位,例子如下:
bool PebNtGlobalFlagsApproach() { int result = 0; __asm { // 進程的PEB mov eax, fs:[30h] // 控制堆操作函數的工作方式的標誌位 mov eax, [eax + 68h] // 操作系統會加上這些標誌位FLG_HEAP_ENABLE_TAIL_CHECK, // FLG_HEAP_ENABLE_FREE_CHECK and FLG_HEAP_VALIDATE_PARAMETERS, // 它們的並集就是x70 // // 下面的代碼相當於C/C++的 // eax = eax & 0x70 and eax, 0x70 mov result, eax } return result != 0; } 四、查詢進程堆的一些標誌位 這個方法是第三個方法的變種,只要進程被調試,進程在堆上分配的內存,在分配 的堆的頭信息裏,ForceFlags這個標誌位會被修改,因此可以通過判斷這個標誌位的方式來反調試。因為進程可以有很多的堆,因此只要檢查任意一個堆 的頭信息就可以了,所以這個方法貌似很強大,例子如下: bool HeapFlagsApproach() { int result = 0; __asm { // 進程的PEB mov eax, fs:[30h] // 進程的堆,我們隨便訪問了一個堆,下面是默認的堆 mov eax, [eax + 18h] // 檢查ForceFlag標誌位,在沒有被調試的情況下應該是 mov eax, [eax + 10h] mov result, eax } return result != 0; } 五、使用NtQueryInformationProcess函數 NtQueryInformationProcess函數是一個未公開的 API,它的第二個參數可以用來查詢進程的調試端口。如果進程被調試,那麽返回的端口值會是-1,否則就是其他的值。由於這個函數是一個未公開的函數,因 此需要使用LoadLibrary和GetProceAddress的方法獲取調用地址,示例代碼如下: // 聲明一個函數指針。 typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)( HANDLE processHandle, PROCESSINFOCLASS processInformationClass, PVOID processInformation, ULONG processInformationLength, PULONG returnLength); bool NtQueryInformationProcessApproach() { int debugPort = 0; HMODULE hModule = LoadLibrary(TEXT("Ntdll.dll ")); NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess"); if ( NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)7, &debugPort, sizeof(debugPort), NULL) ) printf("[ERROR NtQueryInformationProcessApproach] NtQueryInformationProcess failed\n"); else return debugPort == -1; return false; } 六、NtSetInformationThread方法 這個也是使用Windows的一個未公開函數的方法,你可以在當前線程裏調用 NtSetInformationThread,調用這個函數時,如果在第二個參數裏指定0x11這個值(意思是 ThreadHideFromDebugger),等於告訴操作系統,將所有附加的調試器統統取消掉。示例代碼: // 聲明一個函數指針。 typedef NTSTATUS (*NtSetInformationThreadPtr)(HANDLE threadHandle, THREADINFOCLASS threadInformationClass, PVOID threadInformation, ULONG threadInformationLength); void NtSetInformationThreadApproach() { HMODULE hModule = LoadLibrary(TEXT("ntdll.dll")); NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hModule, "NtSetInformationThread"); NtSetInformationThread(GetCurrentThread(), (THREADINFOCLASS)0x11, 0, 0); } 七、觸發異常的方法 這個技術的原理是,首先,進程使用 SetUnhandledExceptionFilter函數註冊一個未處理異常處理函數A,如果進程沒有被調試的話,那麽觸發一個未處理異常,會導致操 作系統將控制權交給先前註冊的函數A;而如果進程被調試的話,那麽這個未處理異常會被調試器捕捉,這樣我們的函數A就沒有機會運行了。 這裏有一個技巧,就是觸發未處理異常的時候,如果跳轉回原來代碼繼續執行,而 不是讓操作系統關閉進程。方案是在函數A裏修改eip的值,因為在函數A的參數_EXCEPTION_POINTERS裏,會保存當時觸發異常的指令地 址,所以在函數A裏根據這個指令地址修改寄存器eip的值就可以了,示例代碼如下: // 進程要註冊的未處理異常處理程序A LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei) { SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) pei->ContextRecord->Eax); // 修改寄存器eip的值 pei->ContextRecord->Eip += 2; // 告訴操作系統,繼續執行進程剩余的指令(指令保存在eip裏),而不是關閉進程 return EXCEPTION_CONTINUE_EXECUTION; } bool UnhandledExceptionFilterApproach() { SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); __asm { // 將eax清零 xor eax, eax // 觸發一個除零異常 div eax } return false; } 八、調用DeleteFiber函數 如果給DeleteFiber函數傳遞一個無效的參數的 話,DeleteFiber函數除了會拋出一個異常以外,還是將進程的LastError值設置為具體出錯原因的代號。然而,如果進程正在被調試的話,這 個LastError值會被修改,因此如果調試器繞過了第七步裏講的反調試技術的話,我們還可以通過驗證LastError值是不是被修改過來檢測調試器 的存在,示例代碼: bool DeleteFiberApproach() { char fib[1024] = {0}; // 會拋出一個異常並被調試器捕獲 DeleteFiber(fib); // 0x57的意思是ERROR_INVALID_PARAMETER return (GetLastError() != 0x57); }

檢查進程是否被調試