windows平台下的反调试技术
生活随笔
收集整理的這篇文章主要介紹了
windows平台下的反调试技术
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在調試一些病毒程序的時候,可能會碰到一些反調試技術,也就是說,被調試的程序可以檢測到自己是否被調試器附加了,如果探知自己正在被調試,肯定是有人試圖反匯編啦之類的方法破解自己。為了了解如何破解反調試技術,首先我們來看看反調試技術。
?
一、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);
}
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的windows平台下的反调试技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 直接拿来用!GitHub10个开源免费的
- 下一篇: FreeRTOS应用开发笔记之一:Fre