8.2 BeingDebugged

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就能定位到線程環境塊TEBBeingDebugged的標誌,此處的標誌位如果爲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 許可協議。轉載請註明出處!

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