Post

Anti-Debug Technique with TLS Callback

Explore the use of TLS callbacks in Windows PE files as an anti-debugging technique.

Anti-Debug Technique with TLS Callback

TLS Callbacks

TLS (Thread Local Storage) callbacks are a mechanism in Windows that are stored in the PE header(IMAGE_DIRECTORY_ENTRY_TLS) and allows a program to define a function that will be called when the process starts, terminates, a thread is created or terminated. These callbacks can be used to perform various tasks, such as initializing thread-specific data or modifying the behavior of the thread.

They are invoked just before the Original Entry Point (OEP) of the program makeing them suitable for anti-debugging technique.

Anti-dbg TLS Callback Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <windows.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
// Force the inclusion of the TLS directory.
#ifdef _WIN64
#pragma comment(linker, "/INCLUDE:_tls_used")
#define readPEB ((PBOOLEAN)((PBYTE)__readgsqword(0x60) + 2))
#else
#pragma comment(linker, "/INCLUDE:__tls_used")
#define readPEB ((PBOOLEAN)((PBYTE)__readfsdword(0x30) + 2))
#endif


// Declaration of NtQueryInformationProcess from ntdll.dll.
extern "C" NTSTATUS NTAPI NtQueryInformationProcess(
    HANDLE hProcess,
    ULONG ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength,
    PULONG ReturnLength
);

// For output without CRT dependencies.
void SafePrint(const char* msg) {
    DWORD bytesWritten;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteFile(hStdOut, msg, (DWORD)strlen(msg), &bytesWritten, NULL);
}

// TLS callback function.
void NTAPI DebuggerDetect(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
    if (Reason != DLL_PROCESS_ATTACH)
        return;


    // Check the PEB for the BeingDebugged flag.
    PBOOLEAN BeingDebugged = readPEB;
    if (BeingDebugged && *BeingDebugged) {
        SafePrint("Debugger Detected via PEB!\n");
        Sleep(2000);
        TerminateProcess(GetCurrentProcess(), 1);
    }

    // Check via NtQueryInformationProcess (ProcessDebugPort, InfoClass 7).
    HANDLE DebugPort = NULL;
    NTSTATUS status = NtQueryInformationProcess(
        GetCurrentProcess(), 7, &DebugPort, sizeof(HANDLE), NULL
    );
    if (NT_SUCCESS(status) && DebugPort) {
        SafePrint("Debugger Detected via NtQuery!\n");
        Sleep(2000);
        TerminateProcess(GetCurrentProcess(), 1);
    }
}

// Place the array in a known TLS section (commonly ".CRT$XLC").

#ifdef _WIN64
#pragma const_seg(".CRT$XLC")
EXTERN_C const PIMAGE_TLS_CALLBACK pTlsCallbacks[] = { DebuggerDetect, 0 };
#pragma const_seg()
#else
#pragma data_seg(".CRT$XLC")
EXTERN_C PIMAGE_TLS_CALLBACK pTlsCallbacks[] = { DebuggerDetect, 0 };
#pragma data_seg()
#endif

int main() {
    printf("Entrypoint Executed!\n");
    return 0;
}
This post is licensed under CC BY 4.0 by the author.