溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++的反調試技術與繞過方法

發(fā)布時間:2021-06-24 09:00:58 來源:億速云 閱讀:143 作者:chen 欄目:開發(fā)技術

這篇文章主要介紹“C++的反調試技術與繞過方法”,在日常操作中,相信很多人在C++的反調試技術與繞過方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++的反調試技術與繞過方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

反調試技術的實現(xiàn)方式有很多,最簡單的一種實現(xiàn)方式莫過于直接調用Windows系統(tǒng)提供給我們的API函數(shù),這些API函數(shù)中有些專門用來檢測調試器的,有些則是可被改造為用于探測調試器是否存在的工具,多數(shù)情況下,調用系統(tǒng)API函數(shù)實現(xiàn)反調試是不明智的,原因很簡單,目標主機通常會安裝主動防御系統(tǒng),而作為主動防御產品默認會加載RootKit驅動掛鉤這些敏感函數(shù)的使用,如果被非法調用則會提示錯誤信息,病毒作者通常會使用匯編自行實現(xiàn)這些類似于系統(tǒng)提供給我們的反調試函數(shù),并不會使用系統(tǒng)的API,這樣依附于API的主動防御的系統(tǒng)將會失效。

1.加載調試符號鏈接文件并放入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;
}

通過標志反調試:

下方的調試標志BeingDebugged是Char類型,為1表示調試狀態(tài).為0表示沒有調試.可以用于反調試.

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];    // 獲取調試標志
        mov dwIsDebug,eax
    }

    if (1 == dwIsDebug)
    {
        printf("正在被調試");
    }
    else
    {
        printf("沒有被調試");
    }
    return 0;
}

通過API反調試:

#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("程序沒有被反調試");
    return 0;
}

反調試與繞過思路

BeingDebugged 屬性反調試:

進程運行時,位置FS:[30h]指向PEB的基地址,為了實現(xiàn)反調試技術,惡意代碼通過這個位置來檢查BeingDebugged標志位是否為1,如果為1則說明進程被調試。

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則說明程序正在被調試,為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

我們手動來驗證一下,首先線程環(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

梳理一下知識點我們可以寫出一下反調試代碼,本代碼單獨運行程序不會出問題,一旦被調試器附加則會提示正在被調試。

#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
	}
	/* 另一種反調試實現(xiàn)方式
	__asm{
		push dword ptr fs:[0x30]
		pop edx
		mov al, [edx + 2]
		mov IsDebug,al
	}
	*/

	if (IsDebug != 0)
		printf("本程序正在被調試. %d", IsDebug);
	else
		printf("程序沒有被調試.");
	getchar();
	return 0;
}

如果惡意代碼中使用該種技術阻礙我們正常調試,該如何繞過呢?如下我們只需要在命令行中執(zhí)行dump fs:[30]+2來定位到BeingDebugged的位置,并將其數(shù)值改為0然后運行程序,會發(fā)現(xiàn)反調試已經(jīng)被繞過了。

C++的反調試技術與繞過方法

ProcessHeap 屬性反調試:

該屬性是一個未公開的屬性,它被設置為加載器為進程分配的第一個堆的位置,ProcessHeap位于PEB結構的0x18處,第一個堆頭部有一個屬性字段,這個屬性叫做ForceFlags和Flags屬性偏移為10,該屬性為0說明程序沒有被調試,非0則說明被調試。

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)反反調試,只需要將 00ca0000 + 10 位置的值修改為0即可,執(zhí)行dump ds:[fs:[30] + 0x18] + 0x10 定位到修改即可。

到此,關于“C++的反調試技術與繞過方法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

c++
AI