詳解C++的反調(diào)試技術與繞過手法
反調(diào)試技術的實現(xiàn)方式有很多,最簡單的一種實現(xiàn)方式莫過于直接調(diào)用Windows系統(tǒng)提供給我們的API函數(shù),這些API函數(shù)中有些專門用來檢測調(diào)試器的,有些則是可被改造為用于探測調(diào)試器是否存在的工具,多數(shù)情況下,調(diào)用系統(tǒng)API函數(shù)實現(xiàn)反調(diào)試是不明智的,原因很簡單,目標主機通常會安裝主動防御系統(tǒng),而作為主動防御產(chǎn)品默認會加載RootKit驅(qū)動掛鉤這些敏感函數(shù)的使用,如果被非法調(diào)用則會提示錯誤信息,病毒作者通常會使用匯編自行實現(xiàn)這些類似于系統(tǒng)提供給我們的反調(diào)試函數(shù),并不會使用系統(tǒng)的API,這樣依附于API的主動防御的系統(tǒng)將會失效。
1.加載調(dià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結構的指針,接著我們分析如何得到的該指針,并通過通配符找到TEB結構的名稱.
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.接著可通過dt命令,查詢下ntdll!_TEB結構,如下可看到0x30處ProcessEnvironmentBlock存放的正是PEB結構.
0:000> dt -rv ntdll!_TEB struct _TEB, 66 elements, 0xfb8 bytes +0x000 NtTib : struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB結構 +0x018 Self : Ptr32 to struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB結構 +0x020 ClientId : struct _CLIENT_ID, 2 elements, 0x8 bytes # 保存進程與線程ID +0x02c ThreadLocalStoragePointer : Ptr32 to Void +0x030 ProcessEnvironmentBlock : Ptr32 to struct _PEB, 65 elements, 0x210 bytes # PEB結構
偏移地址0x18是_NT_TIB結構,也就是指向自身偏移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的位置,可以使用如下命令驗證.
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地址
上方的查詢結果可得知偏移位置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 # 這里保存進程與線程信息 0:000> dt _CLIENT_ID 0x7ffdf000 # 查看進程詳細結構 ntdll!_CLIENT_ID +0x000 UniqueProcess : 0x0012fd0c Void # 獲取進程PID +0x004 UniqueThread : 0x00130000 Void # 獲取線程PID
上方TEB首地址我們知道是fs:[0x18],接著我們通過以下公式計算得出本進程的進程ID.
在Windows系統(tǒng)中如果想要獲取到PID進程號,可以使用NtCurrentTeb()這個系統(tǒng)API來實現(xiàn),但這里我們手動實現(xiàn)該API的獲取過程.
獲取進程PID:
#include "stdafx.h"
#include <Windows.h>
DWORD GetPid(){
DWORD dwPid=0;
__asm
{
mov eax,fs:[0x18] // 獲取PEB地址
add eax,0x20 // 加0x20得到進程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得到進程PID
add eax,0x04 // 加0x04得到線程PID
mov eax,[eax]
mov dwPid,eax
}
return dwPid;
}
int main()
{
printf("%d\n",GetPid());
return 0;
}
通過標志反調(diào)試:
下方的調(diào)試標志BeingDebugged是Char類型,為1表示調(diào)試狀態(tài).為0表示沒有調(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)試標志
mov dwIsDebug,eax
}
if (1 == dwIsDebug)
{
printf("正在被調(diào)試");
}
else
{
printf("沒有被調(diào)試");
}
return 0;
}
通過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("程序沒有被反調(diào)試");
return 0;
}
反調(diào)試與繞過思路
BeingDebugged 屬性反調(diào)試:
進程運行時,位置FS:[30h]指向PEB的基地址,為了實現(xiàn)反調(diào)試技術,惡意代碼通過這個位置來檢查BeingDebugged標志位是否為1,如果為1則說明進程被調(diào)試。
1.首先我們可以使用 dt _teb 命令解析一下TEB的結構,如下TEB結構的起始偏移為0x0,而0x30的位置指向的是 ProcessEnvironmentBlock 也就是指向了進程環(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 // 此處是進程環(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
只需要在進程環(huán)境塊的基礎上 +0x2 就能定位到線程環(huán)境塊TEB中 BeingDebugged 的標志,此處的標志位如果為1則說明程序正在被調(diào)試,為0則說明沒有被調(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
我們手動來驗證一下,首先線程環(huán)境塊地址是007f1000在此基礎上加0x30即可得到進程環(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
梳理一下知識點我們可以寫出一下反調(diào)試代碼,本代碼單獨運行程序不會出問題,一旦被調(diào)試器附加則會提示正在被調(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)試實現(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("程序沒有被調(diào)試.");
getchar();
return 0;
}
如果惡意代碼中使用該種技術阻礙我們正常調(diào)試,該如何繞過呢?如下我們只需要在命令行中執(zhí)行dump fs:[30]+2來定位到BeingDebugged的位置,并將其數(shù)值改為0然后運行程序,會發(fā)現(xiàn)反調(diào)試已經(jīng)被繞過了。

ProcessHeap 屬性反調(diào)試:
該屬性是一個未公開的屬性,它被設置為加載器為進程分配的第一個堆的位置,ProcessHeap位于PEB結構的0x18處,第一個堆頭部有一個屬性字段,這個屬性叫做ForceFlags和Flags屬性偏移為10,該屬性為0說明程序沒有被調(diào)試,非0則說明被調(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
要實現(xiàn)反反調(diào)試,只需要將 00ca0000 + 10 位置的值修改為0即可,執(zhí)行dump ds:[fs:[30] + 0x18] + 0x10 定位到修改即可。
文章出處:https://www.cnblogs.com/lyshark
以上就是詳解C++的反調(diào)試技術與繞過手法的詳細內(nèi)容,更多關于C++ 反調(diào)試技術與繞過手法的資料請關注腳本之家其它相關文章!
- Linux搭建C++開發(fā)調(diào)試環(huán)境的方法步驟
- c++代碼調(diào)試方式的幾點建議
- 解決vscode下調(diào)試c/c++程序一閃而過的問題(Windows)
- vscode C++遠程調(diào)試運行(學習C++用)
- vscode配置遠程開發(fā)環(huán)境并遠程調(diào)試運行C++代碼的教程
- VSCode遠程開發(fā)調(diào)試服務器c/c++代碼
- ubunt18.04LTS+vscode+anaconda3下的python+C++調(diào)試方法
- C++運算符重載實例代碼詳解(調(diào)試環(huán)境 Visual Studio 2019)
- 詳解AndroidStudio3.0開發(fā)調(diào)試安卓NDK的C++代碼
- C++調(diào)試記錄與心得分享
相關文章
C語言實現(xiàn)的統(tǒng)計素數(shù)并求和代碼分享
這篇文章主要介紹了C語言實現(xiàn)的統(tǒng)計素數(shù)并求和代碼分享,來自PAT平臺(浙江大學計算機程序設計能力考試系統(tǒng))的一個題目,需要的朋友可以參考下2014-08-08

