BeingDebugged 是`Windows`系統`PEB`結構體中的一個成員,它是一個標誌位,用於標識當前進程是否正在被調試。BeingDebugged的值為0表示當前進程未被調試,值為1表示當前進程正在被調試。由於`BeingDebugged`是在`PEB`結構體中存儲的,因此可以通過訪問`P... ...
BeingDebugged 是Windows
系統PEB
結構體中的一個成員,它是一個標誌位,用於標識當前進程是否正在被調試。BeingDebugged的值為0表示當前進程未被調試,值為1表示當前進程正在被調試。由於BeingDebugged
是在PEB
結構體中存儲的,因此可以通過訪問PEB
結構體來獲取BeingDebugged
的值。惡意軟體可以使用BeingDebugged
來判斷自己是否正在被調試,以此來防止被反病毒工程師或調試程式進行分析。反病毒工程師們也可以通過檢查BeingDebugged
的值來判斷程式是否正被調試從而進行惡意軟體的檢測和分析。
進程在運行時,位置FS:[30h]
指向PEB的基地址,為了實現反調試,惡意代碼通過這個位置來檢查BeingDebugged
標誌位是否為1,如果為1則說明進程被調試。
首先我們可以使用dt _teb
命令解析一下TEB
的結構,如下TEB
結構的起始偏移為0x0,而0x30
的位置指向的是ProcessEnvironmentBlock
也就是指向了進程環境塊PEB。
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 // PEB 進程環境塊
只需要在進程環境塊的基礎上+0x2
就能定位到線程環境塊TEB
中BeingDebugged
的標誌,此處的標誌位如果為1則說明程式正在被調試,為0則說明沒有被調試。
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
我們手動來驗證一下,首先線程環境塊地址是007f1000
,在此基礎上加0x30
即可得到進程環境快的基地址,位置FS:[0x30]
指向PEB的基地址,007ee000
繼續加0x2即可得到BeingDebugged
的狀態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
有了上述知識點的理解,寫出一段反調試代碼來將變得很容易,如下代碼片段所示,如果獨立運行則會提示正常程式,一旦進程被調試則會提示異常,此處分別使用三段實現方式,讀者可通過向IsDebug()
傳入不同的參數啟用。
#include <stdio.h>
#include <Windows.h>
// 判斷程式是否被調試
int IsDebug(DWORD x)
{
BYTE Debug = 0;
if (x == 1)
{
__asm
{
mov eax, dword ptr fs : [0x30]
mov bl, byte ptr[eax + 0x2]
mov Debug, bl
}
}
if (x == 2)
{
__asm
{
push dword ptr fs : [0x30]
pop edx
mov al, [edx + 2]
mov Debug, al
}
}
if (x == 3)
{
__asm
{
mov eax, fs:[0x18] // TEB Self指針
mov eax, [eax + 0x30] // PEB
movzx eax, [eax + 2] // PEB->BeingDebugged
mov Debug, al
}
}
return Debug;
}
int main(int argc, char* argv[])
{
if (IsDebug(1) && IsDebug(2) && IsDebug(3))
{
printf("[-] 進程正在被調試器調試 \n");
}
else
{
printf("[*] 正常運行 \n");
}
system("pause");
return 0;
}
上述程式被運行,一旦處於調試器模式則會觸發被調試的告警,如果惡意代碼中使用該種技術阻礙我們正常調試,只需要在x64dbg
的命令行中執行dump fs:[30]+2
來定位到BeingDebugged()
的位置,並將其數值改為0然後運行程式,會發現反調試已經被繞過了。
這裡補充一個知識點,通過運用IsDebuggerPresent()
調試函數同樣可實現此類功能,IsDebuggerPresent 返回一個布爾值,用於指示調用進程是否正在被調試器調試。該函數不接受參數,並且如果進程正在被調試,則返回 TRUE,否則返回 FALSE。該函數的實現原理同樣應用了BeingDebugged
標誌位的檢測方法。
#include <stdio.h>
#include <Windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
while (TRUE)
{
// 檢測用ActiveDebugProcess()來創建調試關係
if (IsDebuggerPresent() == TRUE)
{
printf("當前進程正在被調試 \r\n");
// 產生int3異常
DebugBreak();
break;
}
Sleep(1000);
}
return 0;
}
int main(int argc, char * argv[])
{
HANDLE hThread = CreateThread(0, 0, ThreadProc, 0, 0, 0);
if (hThread == NULL)
{
return -1;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
system("pause");
return 0;
}
上述代碼中我們通過使用CreateThread()
函數創建了一個子線程用於每隔1000
毫秒就檢測一次是否被調試了,如果被調試則直接產生一個DebugBreak()
也就是int3斷點,其反調試效果如下圖所示;
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/800bf906.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
文章出處:https://www.cnblogs.com/LyShark/p/17729824.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!