天天看點

調試器攻擊技術 - ThreadHideFromDebugger

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

繼續閱讀