静态反调试技术(3)
文章目錄
- ZwSetInformationThread
- 破解方法
- **調(diào)試程序代碼**:
- 利用TLS回調(diào)函數(shù)(詳情通過以下鏈接查看)
- ETC
- 破解之法
- **調(diào)試程序代碼**:
- 篇章大總結(jié):
- 反調(diào)試技術(shù)系列:
ZwSetInformationThread
強制分離(Detach)被調(diào)試者和調(diào)試器的技術(shù)。利用ZwSetInformationThread()API,被調(diào)試者可將自身從調(diào)試器中分離出來
typedef enum _THREAD_INFORMATION_CLASS {ThreadBasicInformation,ThreadTimes,ThreadPriority,ThreadBasePriority,ThreadAffinityMask,ThreadImpersonationToken,ThreadDescriptorTableEntry,ThreadEnableAlignmentFaultFixup,ThreadEventPair,ThreadQuerySetWin32StartAddress,ThreadZeroTlsCell,ThreadPerformanceCount,ThreadAmILastThread,ThreadIdealProcessor,ThreadPriorityBoost,ThreadSetTlsArrayAddress,ThreadIsIoPending,ThreadHideFromDebugger // 17 (0x11)} THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;typedef NTSTATUS (WINAPI* ZWSETINFORMATIONTHREAD)(HANDLE ThreadHandle,THREAD_INFORMATION_CLASS ThreadInformationClass,PVOID ThreadInformation,ULONG ThreadInformationLength);ZwSetInformationThread()函數(shù)是一個系統(tǒng)原生API(System Native API),它是用來為線程設(shè)置信息的。該函數(shù)有2個參數(shù),第一個參數(shù)ThreadHandle用來接收當前線程的句柄,第二個參數(shù)ThreadInformationClass表示線程信息類型,若其值設(shè)置為ThreadHideFromDebugger(0x11),調(diào)用該函數(shù)后,調(diào)試進程就會被分離出來。ZwSetInformationThread() API不會對正常的程序(非調(diào)試運行)產(chǎn)生任何影響,但若運行的是調(diào)試程序,調(diào)用該API將使調(diào)試器終止運行,同時終止自身進程。
破解方法
簡單的破解思路就是:調(diào)用0x00DA1823 地址處的ntdll.ZwSetInformationThreadAPI前,查找存儲在棧中的第二個參數(shù)ThreadInformationClass值,若值為ThreadHideFromDebugger(0x11),則修改為0后繼續(xù)運行即可
當然也可以勾取ZwSetInformationThread()API,并以同樣的方式操作函數(shù)的參數(shù)。
提示:
利用ZwSetInformationThread()進行反調(diào)試的工作原理是:將線程隱藏起來,調(diào)試器就接受不到信息,從而無法調(diào)試。另外,Windows XP 以后新增了 DebugActiveProcessStop()API
DebugActiveProcessStop()API用來分離調(diào)試器和被調(diào)試進程從而停止調(diào)試。而前面介紹的ZwSetInformationThread()API則是用來隱藏當前線程,使調(diào)試器無法再接收到該線程的調(diào)試事件,最終停止調(diào)試。
調(diào)試程序代碼:
#include "stdio.h" #include "windows.h" #include "tchar.h"void DetachDebugger() {//強制分離被調(diào)試者和調(diào)試器的技術(shù)。調(diào)試器與被調(diào)試進程同時終止typedef enum _THREAD_INFORMATION_CLASS {ThreadBasicInformation,ThreadTimes,ThreadPriority,ThreadBasePriority,ThreadAffinityMask,ThreadImpersonationToken,ThreadDescriptorTableEntry,ThreadEnableAlignmentFaultFixup,ThreadEventPair,ThreadQuerySetWin32StartAddress,ThreadZeroTlsCell,ThreadPerformanceCount,ThreadAmILastThread,ThreadIdealProcessor,ThreadPriorityBoost,ThreadSetTlsArrayAddress,ThreadIsIoPending,<span style="color:#ff0000;"> ThreadHideFromDebugger // 17 (0x11)</span>} THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;typedef NTSTATUS (WINAPI* ZWSETINFORMATIONTHREAD)(HANDLE ThreadHandle,THREAD_INFORMATION_CLASS ThreadInformationClass,PVOID ThreadInformation,ULONG ThreadInformationLength);ZWSETINFORMATIONTHREAD pZwSetInformationThread = NULL;pZwSetInformationThread = (ZWSETINFORMATIONTHREAD)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwSetInformationThread");pZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);printf("ZwSetInformationThread() -> Debugger detached!!!\n\n"); }int _tmain(int argc, TCHAR* argv[]) {DetachDebugger();printf("\npress any key to quit...\n");_gettch();return 0; }利用TLS回調(diào)函數(shù)(詳情通過以下鏈接查看)
https://blog.csdn.net/CSNN2019/article/details/113094488
ETC
判斷當前系統(tǒng)是否為逆向分析系統(tǒng)(非常規(guī)系統(tǒng)),若是,則直接停止程序。這些技術(shù)都能從系統(tǒng)中輕松獲取各種信息(進程,文件,窗口,注冊表,主機名,計算機名,用戶名,環(huán)境變量等)。這些都可以借助Win32API獲取系統(tǒng)信息來實現(xiàn),舉幾個例子:
破解之法
要想讓FindWindow()失效,只需要在調(diào)用FindWindow()把棧中0xC7F668這個名稱字符串用NULL來覆蓋即可,那么FindWindow()API將無法探測到相應(yīng)的調(diào)試器
修改后:
接下來要讓GetWindowText() API失效,調(diào)用GetWindowText() API的代碼在0xDA192D處。若想正常調(diào)用GetWindowText(),就不能執(zhí)行0xDA1912地址處的條件跳轉(zhuǎn)指令。要實現(xiàn)這點,可以直接操作條件跳轉(zhuǎn)語句,也可以將其上GetDesktopWindow()和GetWindow()API的返回值修改為NULL
總之呢,見招拆招,加油
調(diào)試程序代碼:
#include "stdio.h" #include "windows.h" #include "tchar.h"void FindDebuggerWindow() {BOOL bDebugging = FALSE;// using ClassNameif( FindWindow(L"OllyDbg", NULL) || // OllyDbgFindWindow(L"TIdaWindow", NULL) || // IDA ProFindWindow(L"WinDbgFrameClass", NULL) ) // WindbgbDebugging = TRUE;printf("FindWindow()\n");if( bDebugging ) printf(" => Found a debugger window!!!\n\n");else printf(" => Not found a debugger window...\n\n");// using WindowNamebDebugging = FALSE;TCHAR szWindow[MAX_PATH] = {0,};HWND hWnd = GetDesktopWindow();hWnd = GetWindow(hWnd, GW_CHILD);hWnd = GetWindow(hWnd, GW_HWNDFIRST);while( hWnd ){if( GetWindowText(hWnd, szWindow, MAX_PATH) ){if( _tcsstr(szWindow, L"IDA") ||_tcsstr(szWindow, L"OllyDbg") ||_tcsstr(szWindow, L"WinDbg") ){bDebugging = TRUE;break;}}hWnd = GetWindow(hWnd, GW_HWNDNEXT);}printf("GetWindowText()\n");if( bDebugging ) printf(" => Found a debugger window!!!\n\n");else printf(" => Not found a debugger window...\n\n"); }int _tmain(int argc, TCHAR* argv[]) {FindDebuggerWindow();printf("\npress any key to quit...\n");_gettch();return 0; }篇章大總結(jié):
1.PEB結(jié)構(gòu)中有幾個重要的成員標示了進程是否處于被調(diào)試狀態(tài)。(以下以32位為例)
+0x002 BeingDebugged : UChar
+0x00c Ldr : _PEB_LDR_DATA
+0x018 ProcessHeap : Ptr32 Void
+0x068 NtGlobalFlag : Uint4B
BeingDebugged成員在被調(diào)試狀態(tài)會顯示1,正常情況下為0。解決方法:更改該值為0。
Ldr:進程在被調(diào)試狀態(tài)時堆內(nèi)存區(qū)域會出現(xiàn)一些特殊的標志,未使用的堆內(nèi)存區(qū)域全部填充著0xFEEEFEEE,而Ldr正好在堆內(nèi)存中被創(chuàng)建(只在XP系統(tǒng)中有,Vista之后的系統(tǒng)沒有這種標志)。解決方法:將該區(qū)域覆蓋為NULL即可。
ProcessHeap:ProcessHeap.Flags(+0x00c)在正常情況下值為0x2,ProcessHeap.ForceFlags成員(+0x10)的值為0x0,當調(diào)試時,該值會改變。(只在XP中有效)。解決辦法:只需將值改回去即可。
NtGlobalFalg:PEB.NtGlobalFlag會在調(diào)試情況下顯示0x70,該值是以下Flags值or運算的結(jié)果:(附加到進程無效,只有啟動調(diào)試有效)
FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
FLG_HEAP_ENABLE_FREE_CHECK (0x20)
FLG_HEAP_VALIDATE_PARAMETERS (0x40)
解決方法:重置0即可
2.NtQueryInformationProcess()可以查詢ProcessDebugPort(0x7),ProcessDebugObjectHandle(0x1E)ProcessDebugFlags(0x1F)
當進程處于調(diào)試狀態(tài)時,系統(tǒng)會給它分配一個調(diào)試端口(Debug Port),正常狀態(tài)dwDebugPort為,調(diào)試狀態(tài)為0xFFFFFFFF
當進程處于調(diào)試狀態(tài)時,ProcessDebugObjectHandle一個句柄值,正常狀態(tài)為NULL
當進程處于調(diào)試狀態(tài)時,ProcessDebugFlags為0,正常為1
3.NtQueryObject()函數(shù)可以枚舉系統(tǒng)所有對象,通過觀察系統(tǒng)是否有調(diào)試對象句柄,就可以知道是否有進程在被調(diào)試。(wcscmp(L"DebugObject",pObjectTypeInfo->TypeName.Buffer)==0),解決方法,改變該函數(shù)參數(shù)
4.ZwSetInformationThread()該函數(shù)顧名思義是給線程設(shè)置信息的。該函數(shù)有兩個參數(shù),第一個參數(shù)ThreadHandle為線程句柄,第二參數(shù)ThreadInformationClass表示線程信息類型,其值設(shè)置為ThreadHideFromDebugger(0x11),調(diào)用該函數(shù)后,調(diào)試進程將會與調(diào)試器分離開來,使調(diào)試器終止調(diào)試,同時終止自身進程。該函數(shù)對正常運行程序沒有影響。
6.TLS(線程局部存儲)TLS函數(shù)代碼會先于main()函數(shù)執(zhí)行,于是我們可以在這里進行對程序是否被調(diào)試的判斷,例如判斷是否被下了int3斷點,PEB中的BeingDebugged是否為1等。
7.ETC利用API獲得進程窗口,進程,計算機名稱,虛擬機是否在運行狀態(tài)等判斷運行環(huán)境是否安全。
反調(diào)試技術(shù)系列:
靜態(tài)反調(diào)試技術(shù)(1)https://blog.csdn.net/CSNN2019/article/details/113105292
靜態(tài)反調(diào)試技術(shù)(2)https://blog.csdn.net/CSNN2019/article/details/113147820
靜態(tài)反調(diào)試技術(shù)(3)https://blog.csdn.net/CSNN2019/article/details/113178232
動態(tài)反調(diào)試技術(shù) https://blog.csdn.net/CSNN2019/article/details/113181558
高級反調(diào)試技術(shù) https://blog.csdn.net/CSNN2019/article/details/113263215
總結(jié)
以上是生活随笔為你收集整理的静态反调试技术(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TLS回调函数(1)
- 下一篇: 动态反调试技术