3、 ThreadHideFromDebugger
這項技術用到了常常被用來設定線程優先級的API ntdll!NtSetInformationThread(),不過這個API也能夠用來防止調試事件被發往調試器。
NtSetInformationThread()的參數清單如下。要實作這一功能,ThreadHideFromDebugger(0x11)被當作ThreadInformationClass參數傳遞,ThreadHandle通常設為目前線程的句柄(0xFFFFFFFE):
NTSTATUS NTAPI NtSetInformationThread(
HANDLE ThreadHandle,
THREAD_INFORMATION_CLASS ThreadInformaitonClass,
PVOID ThreadInformation,
ULONG ThreadInformationLength
);
ThreadHideFromDebugger内部設定核心結構ETHREAD16的HideThreadFromDebugger成員。一旦這個成員設定以後,主要用來向調試器發送事件的核心函數_DbgkpSendApiMessage()将不再被調用。
示例
調用NtSetInformationThread()的一個典型示例:
push 0 ;InformationLength
push NULL ;ThreadInformation
push ThreadHideFromDebugger ;0x11
push 0xfffffffe ;GetCurrentThread()
call [NtSetInformationThread]
對策
可以在ntdll!NtSetInformationThread()裡下斷,斷下來後,逆向分析人員可以操縱EIP防止API調用到達核心,這些都可以通過ollyscript來自動完成。另外,Olly Advanced插件也有補這個API的選項。補過之後一旦ThreadInformaitonClass參數為HideThreadFromDebugger,API将不再深入核心僅僅執行一個簡單的傳回。
【原創】inline hook SSDT 躲避 Themida 的ThreadHideFromDebugger (學習筆記2)
1 個附件
Themida保護的程式會采用ThreadHideFromDebugger來反調試,這個一旦設定,就一直起效,是以對于需要附加的程式就不太好辦了,或者有的程式直接調用 int 2e來反調試, 雖然有插件比如 invisible可以躲過,但是需要附加進行調試的程式就不起作用了。
比如我以前寫的一個反調試函數
void anti()
{
/
/
//NtSetInformationThread 功能設定調試端口為0
//
//
//
::GetCurrentThread();
_asm // xp系統
{
push 0 //InformationLength
push 0 //ThreadInformation
push 0x11 //11 就是ThreadHideFromDebugger
push eax //目前線程句柄
mov eax, 0xe5 //EAX NtSetInformationThread調用号
mov edx, esp //EDX 目前堆棧放
int 0x2e
add esp, 0x10
}
::GetCurrentThread();
_asm // 2000系統
{
push 0
push 0
push 0x11
push eax
mov eax, 0xc6
mov edx, esp
int 0x2e
add esp, 0x10
}
}
為了躲避上面的ANTI inline hook是個不錯的方法
inline hook 分5步
第一步 申請一個全局變量 用來存放被破壞的指令
第二步 聲明一個類型 用來強制轉換
第三步 自己的函數
第四步 安裝HOOK
第五部 解除安裝HOOK
其中起關鍵作用的是下面的代理函數
NTSTATUS __stdcall //第三步 自己的函數
MyNtSetInformationThread(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
IN PVOID ThreadInformation,
IN ULONG ThreadInformationLength
)
{
if(ThreadInformationClass == 0x11)
{
DbgPrint("發現 ANTI DEBUG 行為");
DbgPrint("NtSetInformationThread 參數 %8x %8x %8x %8x ", ThreadHandle, ThreadInformationClass, ThreadInformationClass, ThreadInformationLength); // 列印出參數
ThreadInformationClass = (THREADINFOCLASS)0xff;
DbgPrint("參數被我改過後是 %8x %8x %8x %8x ", ThreadHandle, ThreadInformationClass, ThreadInformationClass, ThreadInformationLength);
return STATUS_SUCCESS; //直接傳回成功 這樣ANTI就失效了
}
//強制轉換成函數的形式
return ((SYSNTSETINFORMATIIONTHREAD)OldNtSetInformationThread)( // 放行
ThreadHandle,
ThreadInformationClass,
ThreadInformation,
ThreadInformationLength
);
}
下面是全部的代碼 附件中也包含完成的代碼
SYS加載有 你隻要調用下 anti這個函數 代理函數就會輸出參數 并且 改變參數
void * OldNtSetInformationThread;; //第一步 申請一個全局變量 用來存放被破壞的指令
//NtSetInformationThread
typedef NTSTATUS (__stdcall *SYSNTSETINFORMATIIONTHREAD) //第二步 聲明一個類型 用來強制轉換
(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
IN PVOID ThreadInformation,
IN ULONG ThreadInformationLength
);
NTSTATUS __stdcall //第三步 自己的函數
MyNtSetInformationThread(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
IN PVOID ThreadInformation,
IN ULONG ThreadInformationLength
)
{
if(ThreadInformationClass == 0x11)
{
DbgPrint("發現 ANTI DEBUG 行為");
DbgPrint("NtSetInformationThread 參數 %8x %8x %8x %8x ", ThreadHandle, ThreadInformationClass, ThreadInformationClass, ThreadInformationLength); //證明自己存在
ThreadInformationClass = (THREADINFOCLASS)0xff;
DbgPrint("參數被我改過後是 %8x %8x %8x %8x ", ThreadHandle, ThreadInformationClass, ThreadInformationClass, ThreadInformationLength); //證明自己存在
return STATUS_SUCCESS; //直接傳回成功
}
//強制轉換成函數的形式
return ((SYSNTSETINFORMATIIONTHREAD)OldNtSetInformationThread)( // 放行
ThreadHandle,
ThreadInformationClass,
ThreadInformation,
ThreadInformationLength
);
}
ULONG AddrNtSetInformationThread;
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
// 驅動程式加載時調用DriverEntry例程
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
DbgPrint("Hook NtSetInformationThread DriverEntry");
pDriverObj->DriverUnload = OnUnload;
AddrNtSetInformationThread = *PULONG((ULONG)KeServiceDescriptorTable->ServiceTable + 0xe5 * 4);
AfxHookCode((void*)AddrNtSetInformationThread, (void*)MyNtSetInformationThread, (void**)&OldNtSetInformationThread, 10); //第四步 AFXHOOK
// if(驅動加載失敗)
// 一定要 AfxUnHookCode 不然驅動加載失敗 自己配置設定的記憶體代碼也就失效了 系統經過這個SSDT也就藍了
return STATUS_SUCCESS;
}
//
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
AfxUnHookCode((void*)AddrNtSetInformationThread, OldNtSetInformationThread, 10); //第五部 解除安裝 驅動解除安裝一定要還原HOOK 因為驅動解除安裝了,自己配置設定的記憶體也就失效了
DbgPrint("Unload Hook NtSetInformationThread");
}
下面是RING 0 HOOK 子產品
#ifndef __AFXHOOKCODE_H__
#define __AFXHOOKCODE_H__
bool AfxHookCode(void* TargetProc, void* NewProc,void ** l_OldProc, int bytescopy = 5)
{
*l_OldProc = ExAllocatePool(NonPagedPool,(bytescopy+5)); // 執行被覆寫的指令 再跳到原來的代碼上運作
memcpy(*l_OldProc, TargetProc, bytescopy); // 事先儲存被破壞的指令
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
// 我的記憶體的代碼執行完 跳到原來的 代碼+破壞的代碼的長度 上去
*((unsigned char*)(*l_OldProc) + bytescopy) = 0xe9;
// 我記憶體代碼跳到原來代碼上的偏移
*(unsigned int *)((unsigned char*)(*l_OldProc) +bytescopy + 1)=(unsigned int)(TargetProc) + bytescopy - ( (unsigned int)((*l_OldProc)) + 5 + bytescopy ) ;
//被HOOK的函數頭改為jmp
*(unsigned char*)TargetProc =(unsigned char)0xe9;
//被HOOK的地方跳到我的新過程 接受過濾
*(unsigned int*)((unsigned int)TargetProc +1) = (unsigned int)NewProc - ( (unsigned int)TargetProc + 5);
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
return true;
}
bool AfxUnHookCode(void* TargetAddress, void * l_SavedCode, unsigned int len)
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
// 恢複HOOK
memcpy(TargetAddress, l_SavedCode, len);
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
return true;
}
#endif