Windows中一些常用的反調試記錄
這裏筆記會記錄一些關於Windows中的反調試技術的彙編代碼,方便後續分析程序時回來查看。反調試技術包括
- Windows APIs 調用
- 其他手段檢測調試器
Windows APIs
IsDebuggerPresent
這個函數主要檢測的是PEB
的IsDebugged
標誌,如果返回非零值則表示被調試
CheckRemoteDebuggerPresent
這個函數和IsDebuggerPresent
函數一樣也是檢測PEB->IsDebugged
狀態值, 不過還可以檢測其他進程是否被調試,此時需要傳入的是目標進程的句柄。
對抗方法:
1、找一些輔助插件過掉
2、動態調試階段修改指令
**3、動態調試階段改變狀態寄存器 **
4、修改二進制的指令
NtQueryInformationProcess
這個ntdll.dll
內的一個原生API,目的是用來提取指定進程的信息
函數原型
__kernel_entry NTSTATUS NtQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
);
這裏的ProcessInformatinoClass
是一個枚舉量,當ProcessInformationClass=ProcessDebugport
,例如ProcessDebugport=0x70或者別的調試器端口號
就能判斷指定的進程是否被調試,如果是則返回調試端口,否則返回0.
對抗方法:
1、找一些輔助插件過掉
2、動態調試階段修改指令
**3、動態調試階段改變狀態寄存器 **
4、修改二進制的指令
OutputDebugString
函數的目的是在調試器中顯示一個字符串以檢測調試器是否存在。
使用方法:
- 調用SetLastError函數設置一個任意值,例如0x111
- 調用OutputDebugString 函數,如果這個函數調用失敗,SetLastError函數就會被系統分配一個新的值
- 調用GetLastError來判斷是否是設定的值,例如0x111
代碼片段如下
DWORD errorValue=0x111;
SetLastError(errorValue);
OutputDebugString("Test for Debugger!");
if(GetLastError() == errorValue)
{
printf("Debugger was attached !\n");
ExistProcess();
}
printf("Normal procedure!!\n");
如下使用OD調試時的截圖,檢測出調試器附加
注:直接使用VisualStudio2015編譯執行也是會提示debugger存在,估計是環境不對?
不過需要記住這個檢測手段即可,再之後分析中遇到多注意。
對抗方法:
1、找一些輔助插件過掉
2、動態調試階段修改指令
**3、動態調試階段改變狀態寄存器 **
4、修改二進制的指令
檢測數據結構PEB
檢測BeingDebugged
這個反調試檢測或多或少都遇到了,例如
mov eax,dword ptr fs:[30];
mov ebx,byte ptr [eax+0x2];
test ebx,ebx;檢測這個是否爲零
jz NoDebuggerDetected
或者
push dword ptr fs:[30];
pop ebx;
cmp byte ptr [ebx+0x2],1;檢測是否大於1,正常應該是小於1
je DebuggerDetected;跳轉到調試器檢測
這裏的PEB
是存在fs:[30]
段寄存器中,結構爲
0:000> dt nt!_PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 BitField : UChar
+0x003 ImageUsesLargePages : Pos 0, 1 Bit
+0x003 IsProtectedProcess : Pos 1, 1 Bit
+0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
+0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
+0x003 IsPackagedProcess : Pos 4, 1 Bit
+0x003 IsAppContainer : Pos 5, 1 Bit
+0x003 IsProtectedProcessLight : Pos 6, 1 Bit
+0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
+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 AtlThunkSListPtr : Ptr32 _SLIST_HEADER
+0x024 IFEOKey : Ptr32 Void
+0x028 CrossProcessFlags : Uint4B
+0x028 ProcessInJob : Pos 0, 1 Bit
+0x028 ProcessInitializing : Pos 1, 1 Bit
+0x028 ProcessUsingVEH : Pos 2, 1 Bit
+0x028 ProcessUsingVCH : Pos 3, 1 Bit
+0x028 ProcessUsingFTH : Pos 4, 1 Bit
+0x028 ProcessPreviouslyThrottled : Pos 5, 1 Bit
+0x028 ProcessCurrentlyThrottled : Pos 6, 1 Bit
+0x028 ProcessImagesHotPatched : Pos 7, 1 Bit
+0x028 ReservedBits0 : Pos 8, 24 Bits
+0x02c KernelCallbackTable : Ptr32 Void
+0x02c UserSharedInfoPtr : Ptr32 Void
+0x030 SystemReserved : Uint4B
+0x034 AtlThunkSListPtr32 : Ptr32 _SLIST_HEADER
+0x038 ApiSetMap : Ptr32 Void
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 SharedData : 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
+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 ActiveProcessAffinityMask : 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 SparePointers : [4] Ptr32 Void
+0x21c SpareUlongs : [5] Uint4B
+0x230 WerRegistrationData : Ptr32 Void
+0x234 WerShipAssertPtr : Ptr32 Void
+0x238 pUnused : Ptr32 Void
+0x23c pImageHeaderHash : Ptr32 Void
+0x240 TracingFlags : Uint4B
+0x240 HeapTracingEnabled : Pos 0, 1 Bit
+0x240 CritSecTracingEnabled : Pos 1, 1 Bit
+0x240 LibLoaderTracingEnabled : Pos 2, 1 Bit
+0x240 SpareTracingBits : Pos 3, 29 Bits
+0x248 CsrServerReadOnlySharedMemoryBase : Uint8B
+0x250 TppWorkerpListLock : Uint4B
+0x254 TppWorkerpList : _LIST_ENTRY
+0x25c WaitOnAddressHashTable : [128] Ptr32 Void
+0x45c TelemetryCoverageHeader : Ptr32 Void
+0x460 CloudFileFlags : Uint4B
+0x464 CloudFileDiagFlags : Uint4B
+0x468 PlaceholderCompatibilityMode : Char
+0x469 PlaceholderCompatibilityModeReserved : [7] Char
+0x470 LeapSecondData : Ptr32 _LEAP_SECOND_DATA
+0x474 LeapSecondFlags : Uint4B
+0x474 SixtySecondEnabled : Pos 0, 1 Bit
+0x474 Reserved : Pos 1, 31 Bits
+0x478 NtGlobalFlag2 : Uint4B
檢測NtGlobalFlag
正常情況下NtGlobalFlag是0,當有調試器附加上之後就變爲了0x70
如下是彙編指令檢測
mov eax,large fs:30h
cmp dword ptr ds:[eax+0x68],0x70;判斷是否調試器
jz DebuggerDetected;發現調試器
##可以參考
https://www.aldeid.com/wiki/PEB-Process-Environment-Block/NtGlobalFlag
對抗方法:
1、找一些輔助插件過掉
2、動態調試階段修改指令
**3、動態調試階段改變狀態寄存器 **
4、修改二進制的指令
檢測ProcessHeap標誌
這個標誌也是PEB結構中的一個變量用來描述進程的創建是否通過調試器創建,如果被調試器創建則不爲0,反之爲0,如下是彙編指令
mov eax,large fs:30h;-->struct PEB* lpPeb;
mov eax,dword ptr [eax+0x18]; lpPeb-->NtGlobaleFlag
cpm dword ptr ds:[eax+0x10],0;
jne DebuggerDetected
其他檢測手段
行爲監測
INT類型的指令檢測
0xcc
是軟件斷點INT 3
的指令,通過在每條指令前設置一0xcc
實現斷點。如下是檢測方法
call %5;
pop edi
sub edi,5
mov ecx,0x400;
mov eax,0xcc;
repne scasb ;循環每個字節和eax做比較
jz DebuggerDetected
對抗方法:1、設置硬件斷點 2、設置內存斷點
時間檢測rdstc指令的使用
調試程序的運行速度和正常程序的執行速度時間差異很大,可以通過判斷時間來檢測是否被調試。rdstc
來作爲判斷時間的不同,還有別的方式來檢測時間的不同,這裏記錄的是書內的內容,這個方式我還沒見過,估計是逆向的太少
rdstc
指令返回系統重啓開始的時鐘數,並且將64位的值保存到👂 edx:eax內,通過執行兩次rdstc
指令來獲取時間差
rdstc
xor ecx,ecx
add ecx,eax
rdstc
sub eax,ecx ;判斷第二次的時鐘和第一次獲取的差值
cmp eax,0xff;;判斷是否差值小於0xff
jb NoDebuggerDetected;
rdstc ;再次執行時鐘獲取指令
push eax ;將時鐘數返回
ret
時間檢測-QueryPerformanceCounter和GetTickCount函數的使用
-
QueryPerformanceCounter函數
這裏調用兩次函數來獲取時間差。
-
GetTickCount函數
獲取最近一次系統重啓的時間和當前時間的差值,如果兩個值相差很大,則可以判斷存在調試器。
干擾調試器
TLS的回調妙用
PE結構中的.tls
段通常會是值得關注的點,反調試手段會在這裏實現,可以使用ida來分析程序,注意TLS的回調函數。爲了能在執行回調函數之前中斷在調試器內,可以設置中斷選項,以x64dbg爲例,設置option-->events-->SystemBreakpoints
這個在默認的x64dbg
內存在設置,可以檢查一下相關的配置。
SEH異常反調試
通常異常是一個調用鏈的關係,如果發生異常,程序會一個一個調用異常處理,直到最後一個異常被處理。但調試器如果在異常發生時沒有正確處理返回給程序,那麼被調試的程序會檢測到這個過程,那麼就能檢測反調試了。正常可以設置異常忽略即可。x64dbg
需要指定區間忽略異常
異常信息通常保存在了fs:[0]
上,每個異常都是一個鏈表結構。如下是檢測異常
push offset continue
push dwor fs:[0];將SEH壓入棧棧
int 3;主動觸發一個斷點異常,如果這個被調試器捕獲並處理沒有返回給程序,則程序會檢測到調試器
call DebuggerDetected;
continue:
call NoDebuggerDetected;
INT類型的檢測
- INT 3通常是設置軟件斷點,例如
int 3;在執行eax=1之前,設置一個軟件斷點
mov eax,1;
這個指令是一個單字節的,如果變爲了雙字節0xCD03
這個也能產生一個斷點被windbg調試器
捕獲到,這裏建議使用x64dbg或者ollydbg
即可
-
INT 2D
這個指令也是一個斷點指令,不過是用來檢測內核調試器,這個在惡意樣
Dr' Fu 的課程中使用的Max++樣本有使用
·