由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习),本文仅作技术研究。
IsDebuggerPresent
具体链接:https://learn.microsoft.com/zh-cn/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent
该函数可以用于确定是否正在由用户模式调试器调试调用进程。

#include 

BOOL IsDebug() {

    return IsDebuggerPresent();
}

int main() {
    if (IsDebug()) {
        MessageBox(NULL, "Debug", "Debug", NULL);
        return 1;
    }
    else
    {
        MessageBox(NULL, "Not", "Not", NULL);
        MessageBox(NULL, "Hello", "Hello", NULL);
    }

}


NtCurrentPeb()
IsDebuggerPresent函数是调用NtCurrentPeb()返回当前线程的Peb的指针,然后调用BeingDebugged来判断,当前进程是否处于调式模式

通过windbg也可以看到

#include <windows.h>
#include <iostream>
#include "ntdll.h"

using namespace std;

BOOL WINAPI lIsDebuggerPresent(VOID)
{
    return (BOOL)NtCurrentPeb()->BeingDebugged;
}

int main() {

    if (lIsDebuggerPresent()) {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    else
    {
        cout << "Not Debug" << endl;
    }
}

除了使用NtCurrentPeb()去获取Peb,我们也可以直接从寄存器去读取

#include <windows.h>
#include <iostream>
#include "ntdll.h"

using namespace std;

BOOL lBeingDebugged() {
    //获取PEB 地址
#if defined(_WIN64)
    PEB* pPEB = (PEB*)__readgsqword(0x60);
#else
    PEB* pPEB = (PEB*)__readfsdword(0x30);
#endif

    BOOL Flag = pPEB->BeingDebugged;

    return Flag;
}

int main() {
    if (lBeingDebugged()) {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    else
    {
        cout << "Not Debug" << endl;
    }
}

CheckRemoteDebuggerPresent
CheckRemoteDebuggerPresent函数可以判断指定的进程是否处于调试状态

#include <windows.h>
#include <iostream>

using namespace std;

int main() {
    BOOL pbDebuggerPresent;
    BOOL Check = CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);
    if (!Check) {
        cout << "CheckRemoteDebuggerPresent Error" << GetLastError() << endl;
        return 1;
    }

    cout << "CheckRemoteDebuggerPresent Success" << endl;
    if (pbDebuggerPresent) {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    else
    {
        cout << "Not Debug" << endl;
        cout << "Hello" << endl;
    }

}


NtQueryInformationProcess
通过在reactos查看,可以看到CheckRemoteDebuggerPresent函数的实现方式,

可以看到主要就是调用的NtQueryInformationProcess这个函数去实现,好的直接复制过来吧

#include <windows.h>
#include <iostream>
#include "ntdll.h"

#pragma comment(lib, "ntdll.lib")

using namespace std;

DWORD lBaseSetLastNTError(IN NTSTATUS   Status)
{
    DWORD dwErrCode;
    dwErrCode = RtlNtStatusToDosError(Status);
    SetLastError(dwErrCode);
    return dwErrCode;
}

BOOL WINAPI lCheckRemoteDebuggerPresent(IN HANDLE hProcess, OUT PBOOL pbDebuggerPresent)
{
    HANDLE DebugPort;
    NTSTATUS Status;

    /* Make sure we have an output and process*/
    if (!(pbDebuggerPresent) || !(hProcess))
    {
        /* Fail */
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    /* Check if the process has a debug object/port */
    Status = NtQueryInformationProcess(hProcess,ProcessDebugPort,&DebugPort,sizeof(DebugPort),NULL);
    if (NT_SUCCESS(Status))
    {
        /* Return the current state */
        *pbDebuggerPresent = DebugPort != NULL;
        return TRUE;
    }

    lBaseSetLastNTError(Status);
    return FALSE;
}

int main() {
    BOOL pbDebuggerPresent;
    BOOL Check = lCheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);
    if (!Check) {
        cout << "CheckRemoteDebuggerPresent Error" << GetLastError() << endl;
        return 1;
    }

    cout << "CheckRemoteDebuggerPresent Success" << endl;
    if (pbDebuggerPresent) {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    else
    {
        cout << "Not Debug" << endl;
        cout << "Hello" << endl;
    }

}

NtGlobalFlag
NtGlobalFlag位于PEB中

#include <windows.h>
#include <iostream>
#include "ntdll.h"

using namespace std;

BOOL lNtGlobalFlag() {
    //获取PEB 地址
#if defined(_WIN64)
    PEB* pPEB = (PEB*)__readgsqword(0x60);
#else
    PEB* pPEB = (PEB*)__readfsdword(0x30);
#endif

    DWORD Flag = pPEB->NtGlobalFlag;
    if (Flag == 0x70) {
        return TRUE;
    }

    return FALSE;
}

int main() {
    if (lNtGlobalFlag()) {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    else
    {
        cout << "Not Debug" << endl;
        cout << "Hello" << endl;
    }
}

当程序处于调试状态下的时候,其返回值是0x70

父进程检测
一般情况下,新建的进程(除开服务进程)其父进程基本都是explorer.exe,这里以记事本为例子

当程序用调试器启动的时候,其父进程就是调试器的进程

我们可以利用NtQueryInformationProcess来判断当前进程的父进程是否是explorer.exe,以此来判断是否在被调试状态

主要就是利用以下这个结构体

typedef struct _PROCESS_BASIC_INFORMATION
{
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG_PTR AffinityMask;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
  • UniqueProcessId:本进程pid
  • InheritedFromUniqueProcessId:父进程pid

拿到父进程的pid,根据pid获得父进程的句柄,获得父进程的进程名,然后再判断是否为explorer.exe进程

#include <windows.h>
#include <iostream>
#include "ntdll.h"
#pragma comment(lib, "ntdll.lib")

using namespace std;

int main() {

    PROCESS_BASIC_INFORMATION Pro;
    NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &Pro, sizeof(Pro), NULL);

    HANDLE Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)Pro.InheritedFromUniqueProcessId);

    wchar_t path[MAX_PATH];
    DWORD maxpath = MAX_PATH;
    QueryFullProcessImageNameW(Handle, 0, path, &maxpath);
    if (wcsstr(path, L"explorer.exe") != NULL) {
        cout << "Not Debug" << endl;
    }
    else
    {
        cout << "Is Debug Now" << endl;
        return 1;
    }
    system("pause");
}


TLS反调试
Thread Local Storage(TLS),即线程本地存储,是Windows为解决一个进程中多个线程同时访问全局变量而提供的机制。TLS回调函数会在main函数之前执行,因此我们可以利用此来进行相关的反调试。

#include<Windows.h>
#include <stdio.h>

void NTAPI __stdcall TLS_CALLBACK(PVOID Dllhandle, DWORD Reason, PVOID Reserved);

//编译器声明使用TLS
#ifdef _WIN64      
#pragma comment (linker, "/INCLUDE:_tls_used")  
#pragma comment (linker, "/INCLUDE:_tls_callback") 
#else               
#pragma comment (linker, "/INCLUDE:__tls_used") 
#pragma comment (linker, "/INCLUDE:__tls_callback")
#endif

//注册TLS回调函数
#ifdef _WIN64              
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")   
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK _tls_callback[] = { TLS_CALLBACK, 0 };
#ifdef _WIN64                       
#pragma const_seg()
#else
#pragma data_seg()              
#endif

//定义TLS回调函数
void NTAPI __stdcall TLS_CALLBACK(PVOID Dllhandle, DWORD Reason, PVOID Reserved) {
    printf("TLS\n");
}

DWORD WINAPI ThreadF(LPVOID param) {
    while (true) {
        printf("Thread Running\n");
        Sleep(200);
    }
    return 0;
}

int main() {
    CreateThread(0, 0, ThreadF, 0, 0, 0);
    while (true)
    {
        Sleep(-1);
    }
}

程序运行后如下,可以看见TLS回调函数在main函数之前就运行。

反调试实现,利用IsDebuggerPresent()检测是否正在调试程序,如果是则结束进程

#include<Windows.h>
#include <stdio.h>

void NTAPI __stdcall TLS_CALLBACK(PVOID Dllhandle, DWORD Reason, PVOID Reserved);

//编译器声明使用TLS
#ifdef _WIN64       
#pragma comment (linker, "/INCLUDE:_tls_used")  
#pragma comment (linker, "/INCLUDE:_tls_callback") 
#else               
#pragma comment (linker, "/INCLUDE:__tls_used") 
#pragma comment (linker, "/INCLUDE:__tls_callback")
#endif

//注册TLS回调函数
#ifdef _WIN64              
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")   
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK _tls_callback[] = { TLS_CALLBACK, 0 };
#ifdef _WIN64                       
#pragma const_seg()
#else
#pragma data_seg()              
#endif 

//定义TLS回调函数
void NTAPI __stdcall TLS_CALLBACK(PVOID Dllhandle, DWORD Reason, PVOID Reserved) {
    if (IsDebuggerPresent()) {
        MessageBox(NULL, "Debug", "Debug", NULL);
        ExitProcess(0);
    }
    else
    {
        MessageBox(NULL, "No Debug", "No Debug", NULL);
    }
}

int main() {
    MessageBox(NULL, "Hello", "Hello", NULL);
}


但是使用PE bear查看时可以看见很明显的TLS

NtSetInformationThread
如果程序正在被调试,则强制将自己从调试器里分离出来。其中的ThreadHideFromDebugger标志,如果为线程设置了此标志,则该线程将停止发送有关调试事件的通知。

#include <windows.h>
#include <iostream>
#include "ntdll.h"

using namespace std;


void main()
{
    NTSTATUS status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);

    cout << "Hello" << endl;
}

调试信息
有两种异常类型:DBG_PRINTEXCEPTION_C (0x40010006)和 DBG_PRINTEXCEPTION_W(0x4001000A)它们可用于程序是否处于调试状态。

#include <windows.h>
#include <iostream>

using namespace std;
#define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A

int main() {
    WCHAR* outputString = (WCHAR*)"Any text";
    ULONG_PTR args[4] = { 0 };
    args[0] = (ULONG_PTR)wcslen(outputString) + 1;
    args[1] = (ULONG_PTR)outputString;
    __try
    {
        RaiseException(DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args);
        MessageBox(NULL, "Debugging", "Debugging", MB_OK);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        MessageBox(NULL, "Hello", "Hello", MB_OK);
    }
}