日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
26種對(duì)付反調(diào)試的方法

目前主要有3種分析軟件的方法:

10年積累的網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有阿合奇免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

1.數(shù)據(jù)交換分析,研究人員使用數(shù)據(jù)包嗅探工具來分析網(wǎng)絡(luò)數(shù)據(jù)交換。

2.對(duì)軟件的二進(jìn)制代碼進(jìn)行反匯編,然后以匯編語言列出。

3.字節(jié)碼解碼或二進(jìn)制解碼,然后以高級(jí)編程語言重新創(chuàng)建源代碼。

本文針對(duì)的是Windows操作系統(tǒng)中常用的防破解及防逆向工程保護(hù)技術(shù),即反調(diào)試方法,各種防逆向工程技術(shù)的主要目標(biāo)是盡可能多的使逆變工具盡可能失效。

本文的對(duì)付反調(diào)試方法,總共涉及26種:

1. IsDebuggerPresent

2. PEB(進(jìn)程環(huán)境塊)

3.如何避開IsDebuggerPresent的檢查

4.TLS回調(diào)

5.NtGlobalFlag

6.如何避開NtGlobalFlag檢查

7.NtGlobalFlag和IMAGE_LOAD_CONFIG_DIRECTORY

8.HeapFlag和ForceFlags

9.如何避開HeapFlag和ForceFlags

10.陷阱標(biāo)識(shí)檢查

11.如何避開陷阱標(biāo)識(shí)檢查

12.CheckRemoteDebuggerPresent和NtQueryInformationProcess

13.如何避開CheckRemoteDebuggerPresent和NtQueryInformationProcess

14.基于NtQueryInformationProcess的其他反調(diào)試保護(hù)技術(shù)

15.如何避開NtQueryInformationProcess檢查

16.軟件和硬件的斷點(diǎn)反應(yīng)

17.SEH(結(jié)構(gòu)化異常處理)

18.如何避開SHE檢查

19.VEH(向量化異常處理)

20.如何避開硬件斷點(diǎn)檢查和VEH

21.NtSetInformationThread ,在調(diào)試工具中隱藏線程

22.如何避開從調(diào)試工具中隱藏線程

23.NtCreateThreadEx

24. 如何避開NtCreateThreadEx

25.處理跟蹤

26.堆棧段操作

建議你在閱讀本文時(shí),先具備一定的Assembler知識(shí),一些Windbg操作經(jīng)驗(yàn)以及使用API函數(shù)開發(fā)Windows的經(jīng)驗(yàn)。

IsDebuggerPresent

也許最簡單的方法是調(diào)用IsDebuggerPresent函數(shù),用此函數(shù)檢測用戶模式的調(diào)試器是否正在調(diào)試調(diào)用進(jìn)程。下面的代碼就是一個(gè)基本的保護(hù)案例:

 
 
 
  1. int main() 
  2.     if (IsDebuggerPresent()) 
  3.     { 
  4.         std::cout << "Stop debugging program!" << std::endl; 
  5.         exit(-1); 
  6.     } 
  7.     return 0; 

如果我們來看看IsDebuggerPresent函數(shù),我們會(huì)發(fā)現(xiàn)這樣的代碼:

 
 
 
  1. 0:000< u kernelbase!IsDebuggerPresent L3 
  2. KERNELBASE!IsDebuggerPresent: 
  3. 751ca8d0 64a130000000    mov     eax,dword ptr fs:[00000030h] 
  4. 751ca8d6 0fb64002        movzx   eax,byte ptr [eax+2] 
  5. 751ca8da c3              ret 

Windows X64里的進(jìn)程如下:

 
 
 
  1. 0:000< u kernelbase!IsDebuggerPresent L3 
  2. KERNELBASE!IsDebuggerPresent: 
  3. 00007ffc`ab6c1aa0 65488b042560000000 mov   rax,qword ptr gs:[60h] 
  4. 00007ffc`ab6c1aa9 0fb64002           movzx eax,byte ptr [rax+2] 
  5. 00007ffc`ab6c1aad c3                 ret 

在FS寄存器的偏移量30h處存在PEB(進(jìn)程環(huán)境塊),而在X64上,PEB(進(jìn)程環(huán)境塊)存在于GS段寄存器的偏移量60h處。在PEB中的2個(gè)偏移量處,我們將找到BeingDebugged字段:

 
 
 
  1. 0:000< dt _PEB 
  2. ntdll!_PEB 
  3.    +0x000 InheritedAddressSpace : UChar 
  4.    +0x001 ReadImageFileExecOptions : UChar 
  5.    +0x002 BeingDebugged    : UChar 

即IsDebuggerPresent函數(shù)讀取BeingDebugged字段的值。如果進(jìn)程被調(diào)試,值為1,否則為0。

PEB(進(jìn)程環(huán)境塊)

PEB是在操作系統(tǒng)內(nèi)使用的封閉結(jié)構(gòu)。在不同地運(yùn)行環(huán)境下,大家應(yīng)該以不同的方式獲取PEB結(jié)構(gòu)指針。如下所示,你可以在下圖中找到x32和x64系統(tǒng)的PEB指針:

 
 
 
  1. // Get PEB for WOW64 Process 
  2. PVOID GetPEB64() 
  3.     PVOID pPeb = 0; 
  4. #ifndef _WIN64 
  5.     // 1. There are two copies of PEB - PEB64 and PEB32 in WOW64 process 
  6.     // 2. PEB64 follows after PEB32 
  7.     // 3. This is true for version less then Windows 8, else __readfsdword returns address of real PEB64 
  8.     if (IsWin8OrHigher()) 
  9.     { 
  10.         BOOL isWow64 = FALSE; 
  11.         typedef BOOL(WINAPI *pfnIsWow64Process)(HANDLE hProcess, PBOOL isWow64); 
  12.         pfnIsWow64Process fnIsWow64Process = (pfnIsWow64Process) 
  13.             GetProcAddress(GetModuleHandleA("Kernel32.dll"), "IsWow64Process"); 
  14.         if (fnIsWow64Process(GetCurrentProcess(), &isWow64)) 
  15.         { 
  16.             if (isWow64) 
  17.             { 
  18.                 pPeb = (PVOID)__readfsdword(0x0C * sizeof(PVOID)); 
  19.                 pPeb = (PVOID)((PBYTE)pPeb + 0x1000); 
  20.             } 
  21.         } 
  22.     } 
  23. #endif 
  24.     return pPeb; 

檢查操作系統(tǒng)版本的功能代碼如下:

 
 
 
  1. WORD GetVersionWord() 
  2.     OSVERSIONINFO verInfo = { sizeof(OSVERSIONINFO) }; 
  3.     GetVersionEx(&verInfo); 
  4.     return MAKEWORD(verInfo.dwMinorVersion, verInfo.dwMajorVersion); 
  5. BOOL IsWin8OrHigher() { return GetVersionWord() >= _WIN32_WINNT_WIN8; } 
  6. BOOL IsVistaOrHigher() { return GetVersionWord() >= _WIN32_WINNT_VISTA; }        

如何避開IsDebuggerPresent檢查

為了做到這一點(diǎn),在執(zhí)行檢查代碼之前,需要將0置于BeingDebugged。例如,可以使用DLL注入:

 
 
 
  1. mov eax, dword ptr fs:[0x30]   
  2. mov byte ptr ds:[eax+2], 0 

Windows X64里的進(jìn)程如下:

 
 
 
  1. DWORD64 dwpeb = __readgsqword(0x60); 
  2. *((PBYTE)(dwpeb + 2)) = 0; 

TLS回調(diào)

其實(shí),在主函數(shù)中檢查調(diào)試器的存在不是最好的方法,因?yàn)門LS回調(diào)處于反匯編列表時(shí)反向工具的第一個(gè)位置。它實(shí)施的檢查可以由nop指令擦除,從而解除保護(hù)。如果使用CRT庫,則在將控制權(quán)轉(zhuǎn)移到主函數(shù)之前,主線程就已經(jīng)有一個(gè)調(diào)用堆棧了。執(zhí)行調(diào)試器存在檢查的一個(gè)方法便是TLS回調(diào)。如下圖所示,在可執(zhí)行模塊入口調(diào)用之前就已經(jīng)調(diào)用回調(diào)函數(shù)。

 
 
 
  1. #pragma section(".CRT$XLY", long, read) 
  2. __declspec(thread) int var = 0xDEADBEEF; 
  3. VOID NTAnopPI TlsCallback(PVOID DllHandle, DWORD Reason, VOID Reserved) 
  4.     var = 0xB15BADB0; // Required for TLS Callback call 
  5.     if (IsDebuggerPresent()) 
  6.     { 
  7.         MessageBoxA(NULL, "Stop debugging program!", "Error", MB_OK | MB_ICONERROR); 
  8.         TerminateProcess(GetCurrentProcess(), 0xBABEFACE); 
  9.     } 
  10. __declspec(allocate(".CRT$XLY"))PIMAGE_TLS_CALLBACK g_tlsCallback = TlsCallback; 

NtGlobalFlag

在Windows NT中,存在一組標(biāo)識(shí),它們存儲(chǔ)在全局變量NtGlobalFlag中。在系統(tǒng)啟動(dòng)時(shí),NtGlobalFlag全局系統(tǒng)變量將使用系統(tǒng)注冊(cè)表項(xiàng)中的值進(jìn)行初始化:

 
 
 
  1. [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerGlobalFlag] 

該變量值用于系統(tǒng)跟蹤,調(diào)試和控制。雖然變量標(biāo)識(shí)未記錄,但SDK包括gflags實(shí)用程序,它允許對(duì)一個(gè)全局標(biāo)識(shí)值進(jìn)行編輯。 PEB結(jié)構(gòu)還包括NtGlobalFlag字段,其位結(jié)構(gòu)不對(duì)應(yīng)于NtGlobalFlag全局系統(tǒng)變量。在調(diào)試期間,這些標(biāo)識(shí)在NtGlobalFlag字段中的設(shè)置如下:

 
 
 
  1. FLG_HEAP_ENABLE_TAIL_CHECK (0x10) 
  2. FLG_HEAP_ENABLE_FREE_CHECK (0x20) 
  3. FLG_HEAP_VALIDATE_PARAMETERS (0x40) 

要檢查進(jìn)程是否使用了調(diào)試器啟動(dòng),你應(yīng)該檢查PEB結(jié)構(gòu)的NtGlobalFlag字段的值。在x32和x64系統(tǒng)中,該字段位于PEB結(jié)構(gòu)的開始處的0x068和0x0bc偏移處。

 
 
 
  1. 0:000> dt _PEB NtGlobalFlag @$peb  
  2. ntdll!_PEB 
  3.    +0x068 NtGlobalFlag : 0x70 

Windows X64里的進(jìn)程如下:

 
 
 
  1. 0:000> dt _PEB NtGlobalFlag @$peb 
  2. ntdll!_PEB 
  3.    +0x0bc NtGlobalFlag : 0x70 

以下代碼片段就是基于NtGlobalFlag標(biāo)識(shí)檢查的反調(diào)試保護(hù):

 
 
 
  1. #define FLG_HEAP_ENABLE_TAIL_CHECK   0x10 
  2. #define FLG_HEAP_ENABLE_FREE_CHECK   0x20 
  3. #define FLG_HEAP_VALIDATE_PARAMETERS 0x40 
  4. #define NT_GLOBAL_FLAG_DEBUGGED (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS) 
  5. void CheckNtGlobalFlag() 
  6.     PVOID pPeb = GetPEB(); 
  7.     PVOID pPeb64 = GetPEB64(); 
  8.     DWORD offsetNtGlobalFlag = 0; 
  9. #ifdef _WIN64 
  10.     offsetNtGlobalFlag = 0xBC; 
  11. #else 
  12.     offsetNtGlobalFlag = 0x68; 
  13. #endif 
  14.     DWORD NtGlobalFlag = *(PDWORD)((PBYTE)pPeb + offsetNtGlobalFlag); 
  15.     if (NtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED) 
  16.     { 
  17.         std::cout << "Stop debugging program!" << std::endl; 
  18.         exit(-1); 
  19.     } 
  20.     if (pPeb64) 
  21.     { 
  22.         DWORD NtGlobalFlagWow64 = *(PDWORD)((PBYTE)pPeb64 + 0xBC); 
  23.         if (NtGlobalFlagWow64 & NT_GLOBAL_FLAG_DEBUGGED) 
  24.         { 
  25.             std::cout << "Stop debugging program!" << std::endl; 
  26.             exit(-1); 
  27.         } 
  28.     } 

如何避開NtGlobalFlag檢查

在執(zhí)行該檢查之前,應(yīng)該在通過反調(diào)試保護(hù)檢查該值之前,將0調(diào)整為調(diào)試過程中PEB結(jié)構(gòu)的NtGlobalFlag字段。

NtGlobalFlag和IMAGE_LOAD_CONFIG_DIRECTORY

可執(zhí)行文件既包括IMAGE_LOAD_CONFIG_DIRECTORY結(jié)構(gòu),也包括系統(tǒng)加載程序的其他配置參數(shù)。不過在默認(rèn)情況下,此結(jié)構(gòu)不會(huì)內(nèi)置到可執(zhí)行文件中,需要使用補(bǔ)丁添加。此結(jié)構(gòu)具有GlobalFlagsClear字段,對(duì)PEB結(jié)構(gòu)中要重置的NtGlobalFlag字段進(jìn)行了標(biāo)識(shí)。如果最初沒有對(duì)該結(jié)構(gòu)或GlobalFlagsClear = 0創(chuàng)建可執(zhí)行文件,那么在磁盤或內(nèi)存中,該字段就具有非零值,隱藏的調(diào)試器就會(huì)正常運(yùn)行。下面就是檢查運(yùn)行進(jìn)程的內(nèi)存和磁盤上的GlobalFlagsClear字段的代碼,這是一種流行的反調(diào)試技術(shù):

 
 
 
  1. PIMAGE_NT_HEADERS GetImageNtHeaders(PBYTE pImageBase) 
  2.     PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pImageBase; 
  3.     return (PIMAGE_NT_HEADERS)(pImageBase + pImageDosHeader->e_lfanew); 
  4. PIMAGE_SECTION_HEADER FindRDataSection(PBYTE pImageBase) 
  5.     static const std::string rdata = ".rdata"; 
  6.     PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pImageBase); 
  7.     PIMAGE_SECTION_HEADER pImageSectionHeader = IMAGE_FIRST_SECTION(pImageNtHeaders); 
  8.     int n = 0; 
  9.     for (; n < pImageNtHeaders->FileHeader.NumberOfSections; ++n) 
  10.     { 
  11.         if (rdata == (char*)pImageSectionHeader[n].Name) 
  12.         { 
  13.             break; 
  14.         } 
  15.     } 
  16.     return &pImageSectionHeader[n]; 
  17. void CheckGlobalFlagsClearInProcess() 
  18.     PBYTE pImageBase = (PBYTE)GetModuleHandle(NULL); 
  19.     PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pImageBase); 
  20.     PIMAGE_LOAD_CONFIG_DIRECTORY pImageLoadConfigDirectory = (PIMAGE_LOAD_CONFIG_DIRECTORY)(pImageBase 
  21.         + pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress); 
  22.     if (pImageLoadConfigDirectory->GlobalFlagsClear != 0) 
  23.     { 
  24.         std::cout << "Stop debugging program!" << std::endl; 
  25.         exit(-1); 
  26.     } 
  27. void CheckGlobalFlagsClearInFile() 
  28.     HANDLE hExecutable = INVALID_HANDLE_VALUE; 
  29.     HANDLE hExecutableMapping = NULL; 
  30.     PBYTE pMappedImageBase = NULL; 
  31.     __try 
  32.     { 
  33.         PBYTE pImageBase = (PBYTE)GetModuleHandle(NULL); 
  34.         PIMAGE_SECTION_HEADER pImageSectionHeader = FindRDataSection(pImageBase); 
  35.         TCHAR pszExecutablePath[MAX_PATH]; 
  36.         DWORD dwPathLength = GetModuleFileName(NULL, pszExecutablePath, MAX_PATH); 
  37.         if (0 == dwPathLength) __leave; 
  38.         hExecutable = CreateFile(pszExecutablePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 
  39.         if (INVALID_HANDLE_VALUE == hExecutable) __leave; 
  40.         hExecutableMapping = CreateFileMapping(hExecutable, NULL, PAGE_READONLY, 0, 0, NULL); 
  41.         if (NULL == hExecutableMapping) __leave; 
  42.         pMappedImageBase = (PBYTE)MapViewOfFile(hExecutableMapping, FILE_MAP_READ, 0, 0, 
  43.             pImageSectionHeader->PointerToRawData + pImageSectionHeader->SizeOfRawData); 
  44.         if (NULL == pMappedImageBase) __leave; 
  45.         PIMAGE_NT_HEADERS pImageNtHeaders = GetImageNtHeaders(pMappedImageBase); 
  46.         PIMAGE_LOAD_CONFIG_DIRECTORY pImageLoadConfigDirectory = (PIMAGE_LOAD_CONFIG_DIRECTORY)(pMappedImageBase  
  47.             + (pImageSectionHeader->PointerToRawData 
  48.                 + (pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress - pImageSectionHeader->VirtualAddress))); 
  49.         if (pImageLoadConfigDirectory->GlobalFlagsClear != 0) 
  50.         { 
  51.             std::cout << "Stop debugging program!" << std::endl; 
  52.             exit(-1); 
  53.         } 
  54.     } 
  55.     __finally 
  56.     { 
  57.         if (NULL != pMappedImageBase) 
  58.             UnmapViewOfFile(pMappedImageBase); 
  59.         if (NULL != hExecutableMapping) 
  60.             CloseHandle(hExecutableMapping); 
  61.         if (INVALID_HANDLE_VALUE != hExecutable) 
  62.             CloseHandle(hExecutable); 
  63.     }  

在此代碼中,CheckGlobalFlagsClearInProcess函數(shù)會(huì)通過加載當(dāng)前運(yùn)行的進(jìn)程地址查找PIMAGE_LOAD_CONFIG_DIRECTORY結(jié)構(gòu),并檢查GlobalFlagsClear字段值。如果不是0,那么該進(jìn)程可能被調(diào)試。 CheckGlobalFlagsClearInFile函數(shù)也會(huì)執(zhí)行相同的檢查,但僅僅針對(duì)的是磁盤上的可執(zhí)行文件。

HeapFlag和ForceFlags

PEB結(jié)構(gòu)包含指向進(jìn)程堆的指針— _HEAPP結(jié)構(gòu):

 
 
 
  1. 0:000> dt _PEB ProcessHeap @$peb 
  2. ntdll!_PEB 
  3.    +0x018 ProcessHeap : 0x00440000 Void0:000> dt _HEAP Flags ForceFlags 00440000 ntdll!_HEAP 
  4.    +0x040 Flags      : 0x40000062 
  5.    +0x044 ForceFlags : 0x40000060 

Windows X64里的進(jìn)程如下:

 
 
 
  1. 0:000> dt _PEB ProcessHeap @$peb 
  2. ntdll!_PEB 
  3.    +0x030 ProcessHeap : 0x0000009d`94b60000 Void 
  4. 0:000> dt _HEAP Flags ForceFlags 0000009d`94b60000 
  5. ntdll!_HEAP 
  6.    +0x070 Flags      : 0x40000062 
  7.    +0x074 ForceFlags : 0x40000060 

如果正在調(diào)試進(jìn)程,則兩個(gè)字段Flags和ForceFlags都具有特定的調(diào)試值:

1.如果Flags字段沒有設(shè)置HEAP_GROWABLE(0x00000002)標(biāo)識(shí),則正在調(diào)試進(jìn)程。

2.如果ForceFlags!= 0,則正在調(diào)試進(jìn)程。

不過要注意的是,_HEAP結(jié)構(gòu)并未記錄,并且Flags和ForceFlags字段的偏移值可能因操作系統(tǒng)版本而異。以下代碼就是基于HeapFlag檢查的反調(diào)試保護(hù):

 
 
 
  1. int GetHeapFlagsOffset(bool x64) 
  2.     return x64 ? 
  3.         IsVistaOrHigher() ? 0x70 : 0x14: //x64 offsets 
  4.         IsVistaOrHigher() ? 0x40 : 0x0C; //x86 offsets 
  5. int GetHeapForceFlagsOffset(bool x64) 
  6.     return x64 ? 
  7.         IsVistaOrHigher() ? 0x74 : 0x18: //x64 offsets 
  8.         IsVistaOrHigher() ? 0x44 : 0x10; //x86 offsets 
  9. void CheckHeap() 
  10.     PVOID pPeb = GetPEB(); 
  11.     PVOID pPeb64 = GetPEB64(); 
  12.     PVOID heap = 0; 
  13.     DWORD offsetProcessHeap = 0; 
  14.     PDWORD heapFlagsPtr = 0, heapForceFlagsPtr = 0; 
  15.     BOOL x64 = FALSE; 
  16. #ifdef _WIN64 
  17.     x64 = TRUE; 
  18.     offsetProcessHeap = 0x30; 
  19. #else 
  20.     offsetProcessHeap = 0x18; 
  21. #endif 
  22.     heap = (PVOID)*(PDWORD_PTR)((PBYTE)pPeb + offsetProcessHeap); 
  23.     heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(x64)); 
  24.     heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(x64)); 
  25.     if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0) 
  26.     { 
  27.         std::cout << "Stop debugging program!" << std::endl; 
  28.         exit(-1); 
  29.     } 
  30.     if (pPeb64) 
  31.     { 
  32.         heap = (PVOID)*(PDWORD_PTR)((PBYTE)pPeb64 + 0x30); 
  33.         heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(true)); 
  34.         heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(true)); 
  35.         if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0) 
  36.         { 
  37.             std::cout << "Stop debugging program!" << std::endl; 
  38.             exit(-1); 
  39.         } 
  40.     } 

如何避開HeapFlag和ForceFlags檢查

為了避開基于HeapFlag檢查的反調(diào)試保護(hù),應(yīng)該為Flags字段設(shè)置HEAP_GROWABLE標(biāo)識(shí),并將ForceFlags的值設(shè)置為0.。但要注意的是,字段值的重新定義應(yīng)該在HeapFlag檢查之前執(zhí)行。

陷阱標(biāo)識(shí)檢查

Trap Flag(陷阱標(biāo)識(shí))位于EFLAGS寄存器內(nèi),如果TF設(shè)置為1,CPU將在每個(gè)指令執(zhí)行后產(chǎn)生INT 01h或單步異常(single-step exception)。以下就是基于TF設(shè)置和異常調(diào)用檢查的反調(diào)試:

 
 
 
  1. BOOL isDebugged = TRUE; 
  2. __try 
  3.     __asm 
  4.     { 
  5.         pushfd 
  6.         or dword ptr[esp], 0x100 // set the Trap Flag  
  7.         popfd                    // Load the value into EFLAGS register 
  8.         nop 
  9.     } 
  10. __except (EXCEPTION_EXECUTE_HANDLER) 
  11.     // If an exception has been raised – debugger is not present 
  12.     isDebugged = FALSE; 
  13. if (isDebugged) 
  14.     std::cout << "Stop debugging program!" << std::endl; 
  15.     exit(-1); 

這里TF有意設(shè)置為生成異常。如果正在調(diào)試進(jìn)程,則異常將被調(diào)試器捕獲。

如何避開陷阱標(biāo)識(shí)檢查

為了在調(diào)試過程中避開TF標(biāo)識(shí)檢查,應(yīng)該將pushfd指令傳遞給單步異常,但要跳過它,將斷點(diǎn)置后,繼續(xù)執(zhí)行程序。斷點(diǎn)后,跟蹤可以繼續(xù)。

CheckRemoteDebuggerPresent和NtQueryInformationProcess

與IsDebuggerPresent函數(shù)不同,CheckRemoteDebuggerPresent會(huì)檢查一個(gè)進(jìn)程是否被另一個(gè)同步進(jìn)程調(diào)試。下圖就是一個(gè)基于CheckRemoteDebuggerPresent的反調(diào)試技術(shù):

 
 
 
  1. int main(int argc, char *argv[]) 
  2.     BOOL isDebuggerPresent = FALSE; 
  3.     if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent )) 
  4.     { 
  5.         if (isDebuggerPresent ) 
  6.         { 
  7.             std::cout << "Stop debugging program!" << std::endl; 
  8.             exit(-1); 
  9.         } 
  10.     } 
  11.     return 0; 

在CheckRemoteDebuggerPresent的內(nèi)部,調(diào)用NtQueryInformationProcess函數(shù):

 
 
 
  1. 0:000> uf kernelbase!CheckRemotedebuggerPresent 
  2. KERNELBASE!CheckRemoteDebuggerPresent: 
  3. ... 
  4. 75207a24 6a00            push    0 
  5. 75207a26 6a04            push    4 
  6. 75207a28 8d45fc          lea     eax,[ebp-4] 
  7. 75207a2b 50              push    eax 
  8. 75207a2c 6a07            push    7 
  9. 75207a2e ff7508          push    dword ptr [ebp+8] 
  10. 75207a31 ff151c602775    call    dword ptr [KERNELBASE!_imp__NtQueryInformationProcess (7527601c)] 
  11. 75207a37 85c0            test    eax,eax 
  12. 75207a39 0f88607e0100    js      KERNELBASE!CheckRemoteDebuggerPresent+0x2b (7521f89f) 
  13. ... 

如果我們來看看NtQueryInformationProcess文檔,那么這個(gè)Assembler列表將向我們展示CheckRemoteDebuggerPresent函數(shù)獲取DebugPort值,因?yàn)镻rocessInformationClass參數(shù)值(第二個(gè))為7,以下反調(diào)試代碼就是基于調(diào)用NtQueryInformationProcess:

 
 
 
  1. typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)( 
  2.     _In_      HANDLE           ProcessHandle, 
  3.     _In_      UINT             ProcessInformationClass, 
  4.     _Out_     PVOID            ProcessInformation, 
  5.     _In_      ULONG            ProcessInformationLength, 
  6.     _Out_opt_ PULONG           ReturnLength 
  7.     ); 
  8. const UINT ProcessDebugPort = 7; 
  9. int main(int argc, char *argv[]) 
  10.     pfnNtQueryInformationProcess NtQueryInformationProcess = NULL; 
  11.     NTSTATUS status; 
  12.     DWORD isDebuggerPresent = 0; 
  13.     HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll")); 
  14.      
  15.     if (NULL != hNtDll) 
  16.     { 
  17.         NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess"); 
  18.         if (NULL != NtQueryInformationProcess) 
  19.         { 
  20.             status = NtQueryInformationProcess( 
  21.                 GetCurrentProcess(), 
  22.                 ProcessDebugPort, 
  23.                 &isDebuggerPresent, 
  24.                 sizeof(DWORD), 
  25.                 NULL); 
  26.             if (status == 0x00000000 && isDebuggerPresent != 0) 
  27.             { 
  28.                 std::cout << "Stop debugging program!" << std::endl; 
  29.                 exit(-1); 
  30.             } 
  31.         } 
  32.     } 
  33.     return 0; 

如何避開CheckRemoteDebuggerPresent和NtQueryInformationProcess

應(yīng)該替換NtQueryInformationProcess函數(shù)返回的值。如果要使用mhook,就要先設(shè)置一個(gè)鉤子,可以將DLL注入到調(diào)試過程中,并使用mhook在DLLMain中設(shè)置一個(gè)鉤子。以下就是一個(gè)mhook用法的例子:

 
 
 
  1. #include  
  2. #include "mhook.h" 
  3. typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)( 
  4.     _In_      HANDLE           ProcessHandle, 
  5.     _In_      UINT             ProcessInformationClass, 
  6.     _Out_     PVOID            ProcessInformation, 
  7.     _In_      ULONG            ProcessInformationLength, 
  8.     _Out_opt_ PULONG           ReturnLength 
  9.     ); 
  10. const UINT ProcessDebugPort = 7; 
  11. pfnNtQueryInformationProcess g_origNtQueryInformationProcess = NULL; 
  12. NTSTATUS NTAPI HookNtQueryInformationProcess( 
  13.     _In_      HANDLE           ProcessHandle, 
  14.     _In_      UINT             ProcessInformationClass, 
  15.     _Out_     PVOID            ProcessInformation, 
  16.     _In_      ULONG            ProcessInformationLength, 
  17.     _Out_opt_ PULONG           ReturnLength 
  18.     ) 
  19.     NTSTATUS status = g_origNtQueryInformationProcess( 
  20.         ProcessHandle, 
  21.         ProcessInformationClass, 
  22.         ProcessInformation, 
  23.         ProcessInformationLength, 
  24.         ReturnLength); 
  25.     if (status == 0x00000000 && ProcessInformationClass == ProcessDebugPort) 
  26.     { 
  27.         *((PDWORD_PTR)ProcessInformation) = 0; 
  28.     } 
  29.     return status; 
  30. DWORD SetupHook(PVOID pvContext) 
  31.     HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll")); 
  32.     if (NULL != hNtDll) 
  33.     { 
  34.         g_origNtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess"); 
  35.         if (NULL != g_origNtQueryInformationProcess) 
  36.         { 
  37.             Mhook_SetHook((PVOID*)&g_origNtQueryInformationProcess, HookNtQueryInformationProcess); 
  38.         } 
  39.     } 
  40.     return 0; 
  41. BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) 
  42.     switch (fdwReason) 
  43.     { 
  44.     case DLL_PROCESS_ATTACH: 
  45.         DisableThreadLibraryCalls(hInstDLL); 
  46.         CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)SetupHook, NULL, NULL, NULL); 
  47.         Sleep(20); 
  48.     case DLL_PROCESS_DETACH: 
  49.         if (NULL != g_origNtQueryInformationProcess) 
  50.         { 
  51.             Mhook_Unhook((PVOID*)&g_origNtQueryInformationProcess); 
  52.         } 
  53.         break; 
  54.     } 
  55.     return TRUE; 

基于NtQueryInformationProcess的其他反調(diào)試保護(hù)技術(shù)

可以從NtQueryInformationProcess函數(shù)提供的信息知道,還有更多的調(diào)試器檢測技術(shù):

1.ProcessDebugPort 0x07,已在上面討論過。

2.ProcessDebugObjectHandle 0x1E

3.ProcessDebugFlags 0x1F

4.ProcessBasicInformation 0x00

ProcessDebugObjectHandle

從Windows XP開始,研究人員就為調(diào)試過程創(chuàng)建了調(diào)試對(duì)象。以下就是檢查當(dāng)前進(jìn)程調(diào)試對(duì)象的案例:

 
 
 
  1. status = NtQueryInformationProcess( 
  2.             GetCurrentProcess(), 
  3.             ProcessDebugObjectHandle, 
  4.             &hProcessDebugObject, 
  5.             sizeof(HANDLE), 
  6.             NULL); 
  7. if (0x00000000 == status && NULL != hProcessDebugObject) 
  8.     std::cout << "Stop debugging program!" << std::endl; 
  9.     exit(-1); 

如果有調(diào)試對(duì)象,則正在調(diào)試該進(jìn)程。

ProcessDebugFlags

當(dāng)檢查該標(biāo)識(shí)時(shí),它會(huì)返回到EPROCESS內(nèi)核結(jié)構(gòu)的NoDebugInherit位的反轉(zhuǎn)值。如果NtQueryInformationProcess函數(shù)的返回值為0,則正在調(diào)試進(jìn)程。以下就是一個(gè)這樣的反調(diào)試檢查的例子:

 
 
 
  1. status = NtQueryInformationProcess( 
  2.     GetCurrentProcess(), 
  3.     ProcessDebugObjectHandle, 
  4.     &debugFlags, 
  5.     sizeof(ULONG), 
  6.     NULL); 
  7. if (0x00000000 == status && NULL != debugFlags) 
  8.     std::cout << "Stop debugging program!" << std::endl; 
  9.     exit(-1); 
  10. }    

ProcessBasicInformation

當(dāng)使用ProcessBasicInformation標(biāo)識(shí)調(diào)用NtQueryInformationProcess函數(shù)時(shí),會(huì)返回PROCESS_BASIC_INFORMATION結(jié)構(gòu):

 
 
 
  1. typedef struct _PROCESS_BASIC_INFORMATION { 
  2.     NTSTATUS ExitStatus; 
  3.     PVOID PebBaseAddress; 
  4.     ULONG_PTR AffinityMask; 
  5.     KPRIORITY BasePriority; 
  6.     HANDLE UniqueProcessId; 
  7.     HANDLE InheritedFromUniqueProcessId; 
  8. } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; 

該結(jié)構(gòu)中最有趣的是InheritedFromUniqueProcessId字段。在這里,我們需要獲取父進(jìn)程的名稱并將其與流行調(diào)試器的名稱進(jìn)行比較,以下是這種反調(diào)試檢查的列表:

 
 
 
  1. std::wstring GetProcessNameById(DWORD pid) 
  2.     HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
  3.     if (hProcessSnap == INVALID_HANDLE_VALUE) 
  4.     { 
  5.         return 0; 
  6.     } 
  7.     PROCESSENTRY32 pe32; 
  8.     pe32.dwSize = sizeof(PROCESSENTRY32); 
  9.     std::wstring processName = L""; 
  10.     if (!Process32First(hProcessSnap, &pe32)) 
  11.     { 
  12.         CloseHandle(hProcessSnap); 
  13.         return processName; 
  14.     } 
  15.     do 
  16.     { 
  17.         if (pe32.th32ProcessID == pid) 
  18.         { 
  19.             processName = pe32.szExeFile; 
  20.             break; 
  21.         } 
  22.     } while (Process32Next(hProcessSnap, &pe32)); 
  23.      
  24.     CloseHandle(hProcessSnap); 
  25.     return processName; 
  26. status = NtQueryInformationProcess( 
  27.     GetCurrentProcess(), 
  28.     ProcessBasicInformation, 
  29.     &processBasicInformation, 
  30.     sizeof(PROCESS_BASIC_INFORMATION), 
  31.     NULL); 
  32. std::wstring parentProcessName = GetProcessNameById((DWORD)processBasicInformation.InheritedFromUniqueProcessId); 
  33. if (L"devenv.exe" == parentProcessName) 
  34.     std::cout << "Stop debugging program!" << std::endl; 
  35.     exit(-1); 

如何避開NtQueryInformationProcess檢查

避開是非常簡單的, NtQueryInformationProcess函數(shù)返回的值應(yīng)該更改為那些不指示調(diào)試器存在的值:

1.將ProcessDebugObjectHandle設(shè)置為0

2.將ProcessDebugFlags設(shè)置為1

3.對(duì)于ProcessBasicInformation,將InheritedFromUniqueProcessId值更改為另一個(gè)進(jìn)程ID,例如, Explorer.exe的

斷點(diǎn)

斷點(diǎn),調(diào)試器的功能之一,可以讓程序中斷在需要的地方,從而方便其分析。兩種類型的斷點(diǎn):

1.軟件斷點(diǎn)

2.硬件斷點(diǎn)

在沒有斷點(diǎn)的情況下很難進(jìn)行逆向工程,所以目前流行的反逆向工程策略都是基于檢測斷點(diǎn),然后提供一系列相應(yīng)的反調(diào)試方法。

軟件斷點(diǎn)

在IA-32架構(gòu)中,有一個(gè)特定的指令 – int 3h,帶有0xCC操作碼,用于調(diào)用調(diào)試句柄。當(dāng)CPU執(zhí)行該指令時(shí),會(huì)產(chǎn)生中斷并將控制傳輸?shù)秸{(diào)試器。為了達(dá)到控制的目的,調(diào)試器必須將int 3h指令注入到代碼中。要檢測斷點(diǎn),我們可以計(jì)算函數(shù)的校驗(yàn)和。

 
 
 
  1. DWORD CalcFuncCrc(PUCHAR funcBegin, PUCHAR funcEnd) 
  2.     DWORD crc = 0; 
  3.     for (; funcBegin < funcEnd; ++funcBegin) 
  4.     { 
  5.         crc += *funcBegin; 
  6.     } 
  7.     return crc; 
  8. #pragma auto_inline(off) 
  9. VOID DebuggeeFunction() 
  10.     int calc = 0; 
  11.     calc += 2; 
  12.     calc <<= 8; 
  13.     calc -= 3; 
  14. VOID DebuggeeFunctionEnd() 
  15. }; 
  16. #pragma auto_inline(on) 
  17. DWORD g_origCrc = 0x2bd0; 
  18. int main() 
  19.     DWORD crc = CalcFuncCrc((PUCHAR)DebuggeeFunction, (PUCHAR)DebuggeeFunctionEnd); 
  20.     if (g_origCrc != crc)  當(dāng)前名稱:26種對(duì)付反調(diào)試的方法
    文章URL:http://www.dlmjj.cn/article/cdjgddc.html