反调试技术二
五、使用NtQueryInformationProcess函數(shù)
NtQueryInformationProcess函數(shù)是一個未公開的API,它的第二個參數(shù)可以用來查詢進程的調(diào)試端口。如果進程被調(diào)試,那么返回的端口值會是-1,否則就是其他的值。由于這個函數(shù)是一個未公開的函數(shù),因此需要使用LoadLibrary和GetProceAddress的方法獲取調(diào)用地址,示例代碼如下:
?
//?聲明一個函數(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");
???????else
??????????????return?debugPort == -1;
?
???????return?false;
}
?
六、NtSetInformationThread方法
這個也是使用Windows的一個未公開函數(shù)的方法,你可以在當前線程里調(diào)用NtSetInformationThread,調(diào)用這個函數(shù)時,如果在第二個參數(shù)里指定0x11這個值(意思是ThreadHideFromDebugger),等于告訴操作系統(tǒng),將所有附加的調(diào)試器統(tǒng)統(tǒng)取消掉。示例代碼:
?
//?聲明一個函數(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ā)異常的方法
這個技術(shù)的原理是,首先,進程使用SetUnhandledExceptionFilter函數(shù)注冊一個未處理異常處理函數(shù)A,如果進程沒有被調(diào)試的話,那么觸發(fā)一個未處理異常,會導(dǎo)致操作系統(tǒng)將控制權(quán)交給先前注冊的函數(shù)A;而如果進程被調(diào)試的話,那么這個未處理異常會被調(diào)試器捕捉,這樣我們的函數(shù)A就沒有機會運行了。
這里有一個技巧,就是觸發(fā)未處理異常的時候,如果跳轉(zhuǎn)回原來代碼繼續(xù)執(zhí)行,而不是讓操作系統(tǒng)關(guān)閉進程。方案是在函數(shù)A里修改eip的值,因為在函數(shù)A的參數(shù)_EXCEPTION_POINTERS里,會保存當時觸發(fā)異常的指令地址,所以在函數(shù)A里根據(jù)這個指令地址修改寄存器eip的值就可以了,示例代碼如下:
//?進程要注冊的未處理異常處理程序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í)行進程剩余的指令(指令保存在eip里),而不是關(guān)閉進程
???????return?EXCEPTION_CONTINUE_EXECUTION;
}
?
bool?UnhandledExceptionFilterApproach()
{
?????? SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
???????__asm
?????? {
????????????? //?將eax清零
??????????????xor eax, eax
????????????? //?觸發(fā)一個除零異常
??????????????div eax
?????? }
?
???????return?false;
}
八、調(diào)用DeleteFiber函數(shù)
如果給DeleteFiber函數(shù)傳遞一個無效的參數(shù)的話,DeleteFiber函數(shù)除了會拋出一個異常以外,還是將進程的LastError值設(shè)置為具體出錯原因的代號。然而,如果進程正在被調(diào)試的話,這個LastError值會被修改,因此如果調(diào)試器繞過了第七步里講的反調(diào)試技術(shù)的話,我們還可以通過驗證LastError值是不是被修改過來檢測調(diào)試器的存在,示例代碼:
bool?DeleteFiberApproach()
{
???????char?fib[1024] = {0};
???????//?會拋出一個異常并被調(diào)試器捕獲
???????DeleteFiber(fib);
?
???????// 0x57的意思是ERROR_INVALID_PARAMETER
???????return?(GetLastError() != 0x57);
}
?
未完待續(xù)
總結(jié)
- 上一篇: 反调试技术
- 下一篇: 给网游写一个挂吧(二) – 启动外挂上