#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 函数的汇编可以看到

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

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

绕过这个反调试可以通过将BeingDebugged 字段设为0绕过
DWORD64 dwpeb = __readgsqword(0x60);
*((PBYTE)(dwpeb + 2)) = 0;
这个的核心原理是利用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;