反调试技术二
五、使用NtQueryInformationProcess函數(shù)
NtQueryInformationProcess函數(shù)是一個(gè)未公開的API,它的第二個(gè)參數(shù)可以用來(lái)查詢進(jìn)程的調(diào)試端口。如果進(jìn)程被調(diào)試,那么返回的端口值會(huì)是-1,否則就是其他的值。由于這個(gè)函數(shù)是一個(gè)未公開的函數(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");
???????else
??????????????return?debugPort == -1;
?
???????return?false;
}
?
六、NtSetInformationThread方法
這個(gè)也是使用Windows的一個(gè)未公開函數(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)程沒有被調(diào)試的話,那么觸發(fā)一個(gè)未處理異常,會(huì)導(dǎo)致操作系統(tǒng)將控制權(quán)交給先前注冊(cè)的函數(shù)A;而如果進(jìn)程被調(diào)試的話,那么這個(gè)未處理異常會(huì)被調(diào)試器捕捉,這樣我們的函數(shù)A就沒有機(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_PARAMETER
???????return?(GetLastError() != 0x57);
}
?
未完待續(xù)
總結(jié)
- 上一篇: 反调试技术
- 下一篇: 给网游写一个挂吧(二) – 启动外挂上