惡意樣本分析--16Windows中一些常用的反調試記錄

Windows中一些常用的反調試記錄

這裏筆記會記錄一些關於Windows中的反調試技術的彙編代碼,方便後續分析程序時回來查看。反調試技術包括

  • Windows APIs 調用
  • 其他手段檢測調試器

Windows APIs

IsDebuggerPresent

這個函數主要檢測的是PEBIsDebugged標誌,如果返回非零值則表示被調試

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++樣本有使用·

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章