Windows反调试技术全攻略
生活随笔
收集整理的這篇文章主要介紹了
Windows反调试技术全攻略
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在調(diào)試一些病毒程序的時(shí)候,可能會(huì)碰到一些反調(diào)試技術(shù),也就是說(shuō),被調(diào)試的程序可以檢測(cè)到自己是否被調(diào)試器附加了,如果探知自己正在被調(diào)試,肯定是有人試圖反匯編啦之類(lèi)的方法破解自己。為了了解如何破解反調(diào)試技術(shù),首先我們來(lái)看看反調(diào)試技術(shù)。一、Windows API方法Win32提供了兩個(gè)API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用來(lái)檢測(cè)當(dāng)前進(jìn)程是否正在被調(diào)試,以IsDebuggerPresent函數(shù)為例,例子如下:BOOL ret = IsDebuggerPresent();
printf("ret = %d\n", ret);破解方法很簡(jiǎn)單,就是在系統(tǒng)里將這兩個(gè)函數(shù)hook掉,讓這兩個(gè)函數(shù)一直返回false就可以了,網(wǎng)上有很多做hook API工作的工具,也有很多工具源代碼是開(kāi)放的,所以這里就不細(xì)談了。二、查詢(xún)進(jìn)程PEB的BeingDebugged標(biāo)志位當(dāng)進(jìn)程被調(diào)試器所附加的時(shí)候,操作系統(tǒng)會(huì)自動(dòng)設(shè)置這個(gè)標(biāo)志位,因此在程序里定期查詢(xún)這個(gè)標(biāo)志位就可以了,例子如下:bool PebIsDebuggedApproach()
{char result = 0;__asm{// 進(jìn)程的PEB地址放在fs這個(gè)寄存器位置上mov eax, fs:[30h]// 查詢(xún)BeingDebugged標(biāo)志位mov al, BYTE PTR [eax + 2] mov result, al}return result != 0;
}三、查詢(xún)進(jìn)程PEB的NtGlobal標(biāo)志位 跟第二個(gè)方法一樣,當(dāng)進(jìn)程被調(diào)試的時(shí)候,操作系統(tǒng)除了修改BeingDebugged這個(gè)標(biāo)志位以外,還會(huì)修改其他幾個(gè)地方,其中NtDll中一些控制堆(Heap)操作的函數(shù)的標(biāo)志位就會(huì)被修改,因此也可以查詢(xún)這個(gè)標(biāo)志位,例子如下:bool PebNtGlobalFlagsApproach()
{int result = 0;__asm{// 進(jìn)程的PEBmov eax, fs:[30h]// 控制堆操作函數(shù)的工作方式的標(biāo)志位mov eax, [eax + 68h]// 操作系統(tǒng)會(huì)加上這些標(biāo)志位FLG_HEAP_ENABLE_TAIL_CHECK, // FLG_HEAP_ENABLE_FREE_CHECK and FLG_HEAP_VALIDATE_PARAMETERS,// 它們的并集就是x70//// 下面的代碼相當(dāng)于C/C++的// eax = eax & 0x70and eax, 0x70mov result, eax}return result != 0;
}四、查詢(xún)進(jìn)程堆的一些標(biāo)志位這個(gè)方法是第三個(gè)方法的變種,只要進(jìn)程被調(diào)試,進(jìn)程在堆上分配的內(nèi)存,在分配的堆的頭信息里,ForceFlags這個(gè)標(biāo)志位會(huì)被修改,因此可以通過(guò)判斷這個(gè)標(biāo)志位的方式來(lái)反調(diào)試。因?yàn)檫M(jìn)程可以有很多的堆,因此只要檢查任意一個(gè)堆的頭信息就可以了,所以這個(gè)方法貌似很強(qiáng)大,例子如下:bool HeapFlagsApproach()
{int result = 0;__asm{// 進(jìn)程的PEBmov eax, fs:[30h]// 進(jìn)程的堆,我們隨便訪問(wèn)了一個(gè)堆,下面是默認(rèn)的堆mov eax, [eax + 18h]// 檢查ForceFlag標(biāo)志位,在沒(méi)有被調(diào)試的情況下應(yīng)該是mov eax, [eax + 10h]mov result, eax}return result != 0;
}五、使用NtQueryInformationProcess函數(shù)NtQueryInformationProcess函數(shù)是一個(gè)未公開(kāi)的API,它的第二個(gè)參數(shù)可以用來(lái)查詢(xún)進(jìn)程的調(diào)試端口。如果進(jìn)程被調(diào)試,那么返回的端口值會(huì)是-1,否則就是其他的值。由于這個(gè)函數(shù)是一個(gè)未公開(kāi)的函數(shù),因此需要使用LoadLibrary和GetProceAddress的方法獲取調(diào)用地址,示例代碼如下:// 聲明一個(gè)函數(shù)指針。
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");elsereturn debugPort == -1;return false;
}六、NtSetInformationThread方法這個(gè)也是使用Windows的一個(gè)未公開(kāi)函數(shù)的方法,你可以在當(dāng)前線程里調(diào)用NtSetInformationThread,調(diào)用這個(gè)函數(shù)時(shí),如果在第二個(gè)參數(shù)里指定0x11這個(gè)值(意思是ThreadHideFromDebugger),等于告訴操作系統(tǒng),將所有附加的調(diào)試器統(tǒng)統(tǒng)取消掉。示例代碼:// 聲明一個(gè)函數(shù)指針。
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);
}七、觸發(fā)異常的方法這個(gè)技術(shù)的原理是,首先,進(jìn)程使用SetUnhandledExceptionFilter函數(shù)注冊(cè)一個(gè)未處理異常處理函數(shù)A,如果進(jìn)程沒(méi)有被調(diào)試的話,那么觸發(fā)一個(gè)未處理異常,會(huì)導(dǎo)致操作系統(tǒng)將控制權(quán)交給先前注冊(cè)的函數(shù)A;而如果進(jìn)程被調(diào)試的話,那么這個(gè)未處理異常會(huì)被調(diào)試器捕捉,這樣我們的函數(shù)A就沒(méi)有機(jī)會(huì)運(yùn)行了。這里有一個(gè)技巧,就是觸發(fā)未處理異常的時(shí)候,如果跳轉(zhuǎn)回原來(lái)代碼繼續(xù)執(zhí)行,而不是讓操作系統(tǒng)關(guān)閉進(jìn)程。方案是在函數(shù)A里修改eip的值,因?yàn)樵诤瘮?shù)A的參數(shù)_EXCEPTION_POINTERS里,會(huì)保存當(dāng)時(shí)觸發(fā)異常的指令地址,所以在函數(shù)A里根據(jù)這個(gè)指令地址修改寄存器eip的值就可以了,示例代碼如下:// 進(jìn)程要注冊(cè)的未處理異常處理程序A
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei)
{SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)pei->ContextRecord->Eax);// 修改寄存器eip的值pei->ContextRecord->Eip += 2;// 告訴操作系統(tǒng),繼續(xù)執(zhí)行進(jìn)程剩余的指令(指令保存在eip里),而不是關(guān)閉進(jìn)程return EXCEPTION_CONTINUE_EXECUTION;
}bool UnhandledExceptionFilterApproach()
{SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);__asm{// 將eax清零xor eax, eax// 觸發(fā)一個(gè)除零異常div eax}return false;
}八、調(diào)用DeleteFiber函數(shù)如果給DeleteFiber函數(shù)傳遞一個(gè)無(wú)效的參數(shù)的話,DeleteFiber函數(shù)除了會(huì)拋出一個(gè)異常以外,還是將進(jìn)程的LastError值設(shè)置為具體出錯(cuò)原因的代號(hào)。然而,如果進(jìn)程正在被調(diào)試的話,這個(gè)LastError值會(huì)被修改,因此如果調(diào)試器繞過(guò)了第七步里講的反調(diào)試技術(shù)的話,我們還可以通過(guò)驗(yàn)證LastError值是不是被修改過(guò)來(lái)檢測(cè)調(diào)試器的存在,示例代碼:bool DeleteFiberApproach()
{char fib[1024] = {0};// 會(huì)拋出一個(gè)異常并被調(diào)試器捕獲DeleteFiber(fib);// 0x57的意思是ERROR_INVALID_PARAMETERreturn (GetLastError() != 0x57);
}
?
總結(jié)
以上是生活随笔為你收集整理的Windows反调试技术全攻略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 服务器身份验证和授权
- 下一篇: esplise自定义快捷代码补全_Ecp