一、呼叫系統的IsDebuggerPresent函式
(1)實現程式
最簡單也是最基礎的,Windows提供的API介面:IsDebuggerPresent(),這API實際上就是訪問PEB的BeingDebugged標誌來判斷是否處於除錯狀態。
使用vs除錯此段程式碼,彈出"檢測到偵錯程式"。
#include <stdio.h> #include <Windows.h> DWORD WINAPI ThreadFunctionCallBack(LPVOID lp) { while(true) { if (IsDebuggerPresent()) { printf("檢測到偵錯程式\n"); } } } int main() { CreateThread(NULL, NULL, ThreadFunctionCallBack, NULL, NULL, NULL); // 啟動一個執行緒進行實時檢測 while(TRUE) { printf("主執行緒在執行"); } system("pause"); return 0; }
(2)分析IsDebuggerPresent原理:
通過dbg進行簡單地逆向分析,進入到 IsDebuggerPresent 函式內部。x86下 FS:[30] 指向PEB,訪問 PEB 的 BeingDebugged 標誌來判斷是否處於除錯狀態。
IsDebuggerPresent:
// x86下PEB結構
NTDLL_Test!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar // (BOOL)被除錯時,被置為1
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 SparePtr1 : Ptr32 Void
+0x024 SparePtr2 : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 ExecuteOptions : Pos 0, 2 Bits
+0x034 SpareBits : Pos 2, 30 Bits
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B // 被除錯時,會被置為x070
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 _RTL_CRITICAL_SECTION
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x208 MinimumStackCommit : Uint4B
+0x20c FlsCallback : Ptr32 Ptr32 Void
+0x210 FlsListHead : _LIST_ENTRY
+0x218 FlsBitmap : Ptr32 Void
+0x21c FlsBitmapBits : [4] Uint4B
+0x22c FlsHighIndex : Uint4B
(3)IsDebuggerPresent函式的反反除錯
方法1: 在dbg中直接對IsDebuggerPresent函式進行修改,讓其返回即可。
方法2: 對Kernel32.dll中的IsDebuggerPresent函式實現MiniHook(或InlineHook),在FakeIsDebuggerPresent函式中返回FALSE即可。編寫一個dll,在載入到程式時,執行MiniHook。這裡因為MiniHook框架太大,就不貼出相關程式了,有需要可以留言,此處只附出DLL的編寫。
#include "MiniHook.h" typedef BOOL (WINAPI* LPFN_ISDEBUGGERPRESENT)(); LPFN_ISDEBUGGERPRESENT __OriginalIsDebuggerPresent = NULL; BOOL WINAPI FakeIsDebuggerPresent(VOID) { return FALSE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: if (SunInitialize() != STATUS_SUCCESS) { MessageBox(0,"初始化失敗","提示",0); } if (SunCreateHook(&IsDebuggerPresent, &FakeIsDebuggerPresent, reinterpret_cast<LPVOID*>(&__OriginalIsDebuggerPresent)) != STATUS_SUCCESS) { MessageBox(0,"初始化Hook失敗","提示",0); } if (SunEnableHook(&IsDebuggerPresent) != STATUS_SUCCESS) { MessageBox(0, "建立Hook失敗", "提示", 0); } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: //SunRemoveHook(&IsDebuggerPresent); break; } return TRUE; }
二、自定義實現IsDebuggerPresent函式(x86)
自己用匯編實現實現IsDebuggerPresent,如何獲得PEB呢? 在應用層,fs暫存器是指向當前執行緒的TEB結構的,而在核心層,fs暫存器指向PCR(Processor Control Region)的記憶體,其資料型別是KPCR。所以可以通過當前執行緒的TEB獲得PEB,再取出BeingDebugged的值。
原理:
mov eax, fs:18h // TEB Self指標
mov eax, [eax+30h] // PEB
movzx eax, [eax+2] // PEB->BeingDebugged
實現:
_asm { push eax; mov eax, fs:[0x30]; // PEB movzx eax, byte ptr[eax + 2]; //BeingDebugged mov dword ptr[Value], eax; //取值 pop eax; } if (Value) //判斷 { MessageBox(0,L"檢測到偵錯程式",L"提示",0); } else { MessageBox(0,L"未檢測到偵錯程式",L"提示",0); }
三、深度解析除錯中各標誌位的變化
這部分轉載於:https://blog.csdn.net/qq_35713009/article/details/86603668/
加密與解密中提供了從外部程式碼去除某程式BeingDebugged標誌的方式,可以作為偵錯程式的外掛去除這種反除錯方法。但BeingDebugged標記在生成時還留下了一些後患。
那麼是否可以簡單認為將 PEB 的 BeingDebugged 標誌篡改就可以達到越過IsDebuggerPresent函式的目的呢?當然不能。因為除錯時不僅僅這一位被置值了,而是連鎖反應... ...
(1) PEB 的 BeingDebugged 標誌(BOOL型),被修改為1,表示正在被除錯
(2)NtGlobalFlag的變化
LdrpInitialize函式是一個新程式的初始執行緒開始在使用者態執行的最早程式碼,在這個函式內部有一段這樣的程式碼:
if (Peb->BeingDebugged) { Peb->NtGlobalFlag |= FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_VALIDATE_PARAMETERS; }
這說明BeingDebugged被設定為1後,NtGlobalFlag也被設定了一個標記,通過除錯可發現這個標記為0x70。
(3)RtlCreateHeap中建立除錯堆時的變化
那麼 NtGlobalFlag 有沒有向 BeingDebugged 一樣留下痕跡呢?是有的,在初始化堆的函式 RtlCreateHeap 中可以找到相應程式碼。
if (RtlpGetMode() == UserMode) { /* Also check these flags if in usermode */ if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL) Flags |= HEAP_VALIDATE_ALL_ENABLED; if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS) Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED; if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB) Flags |= HEAP_CAPTURE_STACK_BACKTRACES; } /* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); FORCEINLINE BOOLEAN RtlpHeapIsSpecial(ULONG Flags) { if (Flags & HEAP_SKIP_VALIDATION_CHECKS) return FALSE; if (Flags & (HEAP_FLAG_PAGE_ALLOCS | HEAP_VALIDATE_ALL_ENABLED | HEAP_VALIDATE_PARAMETERS_ENABLED | HEAP_CAPTURE_STACK_BACKTRACES | HEAP_CREATE_ENABLE_TRACING)) { /* This is a special heap */ return TRUE; } /* No need for a special treatment */ return FALSE; }
RtlDebugCreateHeap內部實際上又呼叫了RtlCreateHeap,不過這次Flags引數又多了幾個屬性,分別是HEAP_SKIP_VALIDATION_CHECKS, HEAP_TAIL_CHECKING_ENABLED, HEAP_FREE_CHECKING_ENABLED。第一個屬性使得 RtlCreateHeap和RtlDebugCreateHeap不會再繼續重複的相互呼叫。
/* All validation performed, now call the real routine with skip validation check flag */ Flags |= HEAP_SKIP_VALIDATION_CHECKS | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED; Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters);
與後兩個標記有關的有這樣一段程式碼,
if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { RtlFillMemoryUlong((PCHAR)(BusyBlock + 1), Size & ~0x3, ALLOC_HEAP_FILL); } if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) { RtlFillMemory((PCHAR)ReturnValue + Size, CHECK_HEAP_TAIL_SIZE, CHECK_HEAP_TAIL_FILL) BusyBlock->Flags |= HEAP_ENTRY_FILL_PATTERN; } #define ALLOC_HEAP_FILL 0xBAADF00D #define FREE_HEAP_FILL 0xFEEEFEEE #define CHECK_HEAP_TAIL_FILL 0xAB
意思是會用這3種資料來填堆,即當堆中多次重複出現這3種資料時,就表示在除錯狀態中。這些東西通常被稱為HeapMagic。
除此之外,Flags在RltCreateHeap函式後面還有一些感染操作,使得 Heap->Flags 和 Heap->ForceFlags 都附帶了除錯資訊,通常在程式正常執行的情況下,Flags為2,ForceFlags為0;而在除錯狀態下,Flags為0x50000062,ForceFlags為0x40000060。
// x86下PEB結構
NTDLL_Test!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar // (BOOL)被除錯時,被置為1
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 SparePtr1 : Ptr32 Void
+0x024 SparePtr2 : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 ExecuteOptions : Pos 0, 2 Bits
+0x034 SpareBits : Pos 2, 30 Bits
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B // 被除錯時,會被置為x070
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 _RTL_CRITICAL_SECTION
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x208 MinimumStackCommit : Uint4B
+0x20c FlsCallback : Ptr32 Ptr32 Void
+0x210 FlsListHead : _LIST_ENTRY
+0x218 FlsBitmap : Ptr32 Void
+0x21c FlsBitmapBits : [4] Uint4B
+0x22c FlsHighIndex : Uint4B