詳解C++的反調(diào)試技術(shù)與繞過(guò)手法
反調(diào)試技術(shù)的實(shí)現(xiàn)方式有很多,最簡(jiǎn)單的一種實(shí)現(xiàn)方式莫過(guò)于直接調(diào)用Windows系統(tǒng)提供給我們的API函數(shù),這些API函數(shù)中有些專門用來(lái)檢測(cè)調(diào)試器的,有些則是可被改造為用于探測(cè)調(diào)試器是否存在的工具,多數(shù)情況下,調(diào)用系統(tǒng)API函數(shù)實(shí)現(xiàn)反調(diào)試是不明智的,原因很簡(jiǎn)單,目標(biāo)主機(jī)通常會(huì)安裝主動(dòng)防御系統(tǒng),而作為主動(dòng)防御產(chǎn)品默認(rèn)會(huì)加載RootKit驅(qū)動(dòng)掛鉤這些敏感函數(shù)的使用,如果被非法調(diào)用則會(huì)提示錯(cuò)誤信息,病毒作者通常會(huì)使用匯編自行實(shí)現(xiàn)這些類似于系統(tǒng)提供給我們的反調(diào)試函數(shù),并不會(huì)使用系統(tǒng)的API,這樣依附于API的主動(dòng)防御的系統(tǒng)將會(huì)失效。
1.加載調(diào)試符號(hào)鏈接文件并放入d:/symbols目錄下.
0:000> .sympath srv*d:\symbols*http://msdl.microsoft.com/download/symbols 0:000> .reload Reloading current modules
2.位于fs:[0x30]的位置就是PEB結(jié)構(gòu)的指針,接著我們分析如何得到的該指針,并通過(guò)通配符找到TEB結(jié)構(gòu)的名稱.
0:000> dt ntdll!*teb* ntdll!_TEB ntdll!_GDI_TEB_BATCH ntdll!_TEB_ACTIVE_FRAME ntdll!_TEB_ACTIVE_FRAME_CONTEXT ntdll!_TEB_ACTIVE_FRAME_CONTEXT
3.接著可通過(guò)dt命令,查詢下ntdll!_TEB結(jié)構(gòu),如下可看到0x30處ProcessEnvironmentBlock存放的正是PEB結(jié)構(gòu).
0:000> dt -rv ntdll!_TEB struct _TEB, 66 elements, 0xfb8 bytes +0x000 NtTib : struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB結(jié)構(gòu) +0x018 Self : Ptr32 to struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB結(jié)構(gòu) +0x020 ClientId : struct _CLIENT_ID, 2 elements, 0x8 bytes # 保存進(jìn)程與線程ID +0x02c ThreadLocalStoragePointer : Ptr32 to Void +0x030 ProcessEnvironmentBlock : Ptr32 to struct _PEB, 65 elements, 0x210 bytes # PEB結(jié)構(gòu)
偏移地址0x18是_NT_TIB結(jié)構(gòu),也就是指向自身偏移0x0的位置.
0:000> r $teb $teb=7ffdf000 0:000> dd $teb+0x18 7ffdf018 7ffdf000 00000000 00001320 00000c10 7ffdf028 00000000 00000000 7ffd9000 00000000
而!teb地址加0x30正是PEB的位置,可以使用如下命令驗(yàn)證.
0:000> dd $teb+0x30 7ffdf030 7ffd9000 00000000 00000000 00000000 7ffdf040 00000000 00000000 00000000 00000000 0:000> !teb TEB at 7ffdf000 ExceptionList: 0012fd0c StackBase: 00130000 StackLimit: 0012e000 SubSystemTib: 00000000 FiberData: 00001e00 ArbitraryUserPointer: 00000000 Self: 7ffdf000 EnvironmentPointer: 00000000 ClientId: 00001320 . 00000c10 RpcHandle: 00000000 Tls Storage: 00000000 PEB Address: 7ffd9000 # 此處teb地址
上方的查詢結(jié)果可得知偏移位置fs:[0x18]正是TEB的基址TEB:7ffdf000
0:000> dd fs:[0x18] 003b:00000018 7ffdf000 00000000 000010f4 00000f6c 003b:00000028 00000000 00000000 7ffda000 00000000 0:000> dt _teb 0x7ffdf000 ntdll!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : (null) +0x020 ClientId : _CLIENT_ID # 這里保存進(jìn)程與線程信息 0:000> dt _CLIENT_ID 0x7ffdf000 # 查看進(jìn)程詳細(xì)結(jié)構(gòu) ntdll!_CLIENT_ID +0x000 UniqueProcess : 0x0012fd0c Void # 獲取進(jìn)程PID +0x004 UniqueThread : 0x00130000 Void # 獲取線程PID
上方TEB首地址我們知道是fs:[0x18],接著我們通過(guò)以下公式計(jì)算得出本進(jìn)程的進(jìn)程ID.
在Windows系統(tǒng)中如果想要獲取到PID進(jìn)程號(hào),可以使用NtCurrentTeb()這個(gè)系統(tǒng)API來(lái)實(shí)現(xiàn),但這里我們手動(dòng)實(shí)現(xiàn)該API的獲取過(guò)程.
獲取進(jìn)程PID:
#include "stdafx.h" #include <Windows.h> DWORD GetPid(){ DWORD dwPid=0; __asm { mov eax,fs:[0x18] // 獲取PEB地址 add eax,0x20 // 加0x20得到進(jìn)程PID mov eax,[eax] mov dwPid,eax } return dwPid; } int main() { printf("%d\n",GetPid()); return 0; }
獲取線程PID:
#include "stdafx.h" #include <Windows.h> DWORD GetPid(){ DWORD dwPid=0; __asm { mov eax,fs:[0x18] // 獲取PEB地址 add eax,0x20 // 加0x20得到進(jìn)程PID add eax,0x04 // 加0x04得到線程PID mov eax,[eax] mov dwPid,eax } return dwPid; } int main() { printf("%d\n",GetPid()); return 0; }
通過(guò)標(biāo)志反調(diào)試:
下方的調(diào)試標(biāo)志BeingDebugged是Char類型,為1表示調(diào)試狀態(tài).為0表示沒(méi)有調(diào)試.可以用于反調(diào)試.
0:000> dt _peb ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void
#include "stdafx.h" #include <Windows.h> int main() { DWORD dwIsDebug = 0; __asm { mov eax, fs:[0x18]; // 獲取TEB mov eax, [eax + 0x30]; // 獲取PEB movzx eax, [eax + 2]; // 獲取調(diào)試標(biāo)志 mov dwIsDebug,eax } if (1 == dwIsDebug) { printf("正在被調(diào)試"); } else { printf("沒(méi)有被調(diào)試"); } return 0; }
通過(guò)API反調(diào)試:
#include <stdio.h> #include <stdlib.h> #include <Windows.h> int main() { STARTUPINFO temp; temp.cb = sizeof(temp); GetStartupInfo(&temp); if (temp.dwFlags != 1) { ExitProcess(0); } printf("程序沒(méi)有被反調(diào)試"); return 0; }
反調(diào)試與繞過(guò)思路
BeingDebugged 屬性反調(diào)試:
進(jìn)程運(yùn)行時(shí),位置FS:[30h]指向PEB的基地址,為了實(shí)現(xiàn)反調(diào)試技術(shù),惡意代碼通過(guò)這個(gè)位置來(lái)檢查BeingDebugged標(biāo)志位是否為1,如果為1則說(shuō)明進(jìn)程被調(diào)試。
1.首先我們可以使用 dt _teb 命令解析一下TEB的結(jié)構(gòu),如下TEB結(jié)構(gòu)的起始偏移為0x0,而0x30的位置指向的是 ProcessEnvironmentBlock 也就是指向了進(jìn)程環(huán)境塊。
0:000> dt _teb ntdll!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void +0x030 ProcessEnvironmentBlock : Ptr32 _PEB // 此處是進(jìn)程環(huán)境塊 +0x034 LastErrorValue : Uint4B +0x038 CountOfOwnedCriticalSections : Uint4B +0x03c CsrClientThread : Ptr32 Void +0x040 Win32ThreadInfo : Ptr32 Void +0x044 User32Reserved : [26] Uint4B +0x0ac UserReserved : [5] Uint4B +0x0c0 WOW32Reserved : Ptr32 Void
只需要在進(jìn)程環(huán)境塊的基礎(chǔ)上 +0x2 就能定位到線程環(huán)境塊TEB中 BeingDebugged 的標(biāo)志,此處的標(biāo)志位如果為1則說(shuō)明程序正在被調(diào)試,為0則說(shuō)明沒(méi)有被調(diào)試。
0:000> dt _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
我們手動(dòng)來(lái)驗(yàn)證一下,首先線程環(huán)境塊地址是007f1000在此基礎(chǔ)上加0x30即可得到進(jìn)程環(huán)境快的基地址007ee000繼續(xù)加0x2即可得到BeingDebugged的狀態(tài) ffff0401 需要 byte=1
0:000> r $teb $teb=007f1000 0:000> dd 007f1000 + 0x30 007f1030 007ee000 00000000 00000000 00000000 007f1040 00000000 00000000 00000000 00000000 0:000> r $peb $peb=007ee000 0:000> dd 007ee000 + 0x2 007ee002 ffff0401 0000ffff 0c400112 19f0775f 007ee012 0000001b 00000000 09e0001b 0000775f
梳理一下知識(shí)點(diǎn)我們可以寫出一下反調(diào)試代碼,本代碼單獨(dú)運(yùn)行程序不會(huì)出問(wèn)題,一旦被調(diào)試器附加則會(huì)提示正在被調(diào)試。
#include <stdio.h> #include <windows.h> int main() { BYTE IsDebug = 0; __asm{ mov eax, dword ptr fs:[0x30] mov bl, byte ptr [eax+ 0x2] mov IsDebug, bl } /* 另一種反調(diào)試實(shí)現(xiàn)方式 __asm{ push dword ptr fs:[0x30] pop edx mov al, [edx + 2] mov IsDebug,al } */ if (IsDebug != 0) printf("本程序正在被調(diào)試. %d", IsDebug); else printf("程序沒(méi)有被調(diào)試."); getchar(); return 0; }
如果惡意代碼中使用該種技術(shù)阻礙我們正常調(diào)試,該如何繞過(guò)呢?如下我們只需要在命令行中執(zhí)行dump fs:[30]+2來(lái)定位到BeingDebugged的位置,并將其數(shù)值改為0然后運(yùn)行程序,會(huì)發(fā)現(xiàn)反調(diào)試已經(jīng)被繞過(guò)了。
ProcessHeap 屬性反調(diào)試:
該屬性是一個(gè)未公開(kāi)的屬性,它被設(shè)置為加載器為進(jìn)程分配的第一個(gè)堆的位置,ProcessHeap位于PEB結(jié)構(gòu)的0x18處,第一個(gè)堆頭部有一個(gè)屬性字段,這個(gè)屬性叫做ForceFlags和Flags屬性偏移為10,該屬性為0說(shuō)明程序沒(méi)有被調(diào)試,非0則說(shuō)明被調(diào)試。
0:000> dt !_peb ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x018 ProcessHeap : Ptr32 Void +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION 0:000> r $peb $peb=006e1000 0:000> dd 006e1000+18 006e1018 00ca0000 775f09e0 00000000 00000000 0:000> dd 00ca0000 + 10 00ca0010 00ca00a4 00ca00a4 00ca0000 00ca0000
要實(shí)現(xiàn)反反調(diào)試,只需要將 00ca0000 + 10 位置的值修改為0即可,執(zhí)行dump ds:[fs:[30] + 0x18] + 0x10 定位到修改即可。
文章出處:https://www.cnblogs.com/lyshark
以上就是詳解C++的反調(diào)試技術(shù)與繞過(guò)手法的詳細(xì)內(nèi)容,更多關(guān)于C++ 反調(diào)試技術(shù)與繞過(guò)手法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Linux搭建C++開(kāi)發(fā)調(diào)試環(huán)境的方法步驟
- c++代碼調(diào)試方式的幾點(diǎn)建議
- 解決vscode下調(diào)試c/c++程序一閃而過(guò)的問(wèn)題(Windows)
- vscode C++遠(yuǎn)程調(diào)試運(yùn)行(學(xué)習(xí)C++用)
- vscode配置遠(yuǎn)程開(kāi)發(fā)環(huán)境并遠(yuǎn)程調(diào)試運(yùn)行C++代碼的教程
- VSCode遠(yuǎn)程開(kāi)發(fā)調(diào)試服務(wù)器c/c++代碼
- ubunt18.04LTS+vscode+anaconda3下的python+C++調(diào)試方法
- C++運(yùn)算符重載實(shí)例代碼詳解(調(diào)試環(huán)境 Visual Studio 2019)
- 詳解AndroidStudio3.0開(kāi)發(fā)調(diào)試安卓NDK的C++代碼
- C++調(diào)試記錄與心得分享
相關(guān)文章
C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖示例
這篇文章主要為大家介紹了C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖的代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05C++控制臺(tái)實(shí)現(xiàn)密碼管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++控制臺(tái)實(shí)現(xiàn)密碼管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11DSP中浮點(diǎn)轉(zhuǎn)定點(diǎn)運(yùn)算--浮點(diǎn)與定點(diǎn)概述
本文主要介紹DSP中浮點(diǎn)與定點(diǎn)概述,很值得學(xué)習(xí)一下,需要的朋友可以參考一下。2016-06-06VS2022連接sqlserver數(shù)據(jù)庫(kù)教程
本文主要介紹了VS2022連接sqlserver數(shù)據(jù)庫(kù)教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07