Technology

IsDebuggerPresent

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

int main()
{
    if (IsDebuggerPresent()) {
        std::cout << "Debugger found!\\n" << std::endl;
        exit(-1);
    }
    std::cout << "Hello World!\\n";
}

直接看IsDebuggerPresent 函数的汇编可以看到

image.png

在x64下,gs指向TIB块,所以gs:[60h] 实际上是PEB

image.png

后面去访问byte ptr [rax+2] 实际上访问的是PEBBeingDebugged 字段

image.png

绕过这个反调试可以通过将BeingDebugged 字段设为0绕过

DWORD64 dwpeb = __readgsqword(0x60);
*((PBYTE)(dwpeb + 2)) = 0;

TLS callback

这个的核心原理是利用Windows PE的TLS Callback,在程序真正进入入口点/CRT main 之前执行反调试检查

Windows Loader 加载 EXE
-> 映射 PE、修复导入表、初始化 TLS
-> 调用 TLS Callback
-> 调用模块入口点,例如 CRT startup
-> CRT 初始化
-> 调用 main / WinMain

所以如果把 IsDebuggerPresent() 放在 main 里,逆向人员很容易在 main 附近看到并 NOP 掉。而 TLS Callback 发生得更早,很多人如果只盯着 main、WinMain 或入口点附近,可能会先被这个检查拦住。

example

#pragma section(".CRT$XLY", long, read)
__declspec(thread) int var = 0xDEADBEEF;
VOID NTAnopPI TlsCallback(PVOID DllHandle, DWORD Reason, VOID Reserved)
{
    var = 0xB15BADB0; // Required for TLS Callback call
    if (IsDebuggerPresent())
    {
        MessageBoxA(NULL, "Stop debugging program!", "Error", MB_OK | MB_ICONERROR);
        TerminateProcess(GetCurrentProcess(), 0xBABEFACE);
    }
}
__declspec(allocate(".CRT$XLY"))PIMAGE_TLS_CALLBACK g_tlsCallback = TlsCallback;
#pragma section(".CRT$XLY", long, read)

这一行的作用是声明一个特殊节段。MSVC/CRT会把.CRT$XL* 这类节按照名字排序合并,用来形成TLS Callback表

__declspec(thread) int var = 0xDEADBEEF;