TLS回调

@lzeroyuee  October 25, 2020

TLS CallBack

TLS(Thread-local storage,线程本地存储)回调属于一种特殊的回调函数,其会在进程加载前、进程退出前、线程加载前、线程退出前执行

其函数原型为:

void NTAPI tls_callback_1(
    PVOID DllHandle,        // 模块句柄 
    DWORD Reason,             // DllMain的fdwReason参数一样
    PVOID Reserved            // 保留
);

使用TLS Callback Function

在vs中,可用直接使用编译器功能来设置TLS回调

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")                    // 指明使用TLS回调
#pragma comment (linker, "/INCLUDE:thread_callback_base")        // 强制编译器生成对thread_callback_base的引用,防止优化
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:_thread_callback_base")
#endif

注意:x64下的_tls_used比x86下的__tls_used少一个_thread_callback_base也是同理

  • _tls_used指明使用TLS回调,将在PE中创建一个.tls的节
  • thread_callback_base则是强制编译器生成对thread_callback_base的引用,防止在Release下的优化导致TLS回调被优化掉,此处的thread_callback_base是一个TLS回调数组,以NULL结尾

之后设置TLS回调,节名.CRT$XL的含义如下:

  • CRT表示使用C Runtime机制
  • $X表示随机标识
  • L表示TLS CallBack Section
  • 最后的F替换可以为B ~ Y任意一个字符
#ifdef _WIN64
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK thread_callback_base[] = { tls_callback_1, tls_callback_2, 0 };        // 设置TLS回调
#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64

示例

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

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:thread_callback_base")
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:_thread_callback_base")
#endif

void generate_str(char *ptr, DWORD reason)
{
    switch (reason) {
    case DLL_PROCESS_ATTACH:
        strcat(ptr, "DLL_PROCESS_ATTACH\n");
        break;
    case DLL_PROCESS_DETACH:
        strcat(ptr, "DLL_PROCESS_DETACH\n");
        break;
    case DLL_THREAD_ATTACH:
        strcat(ptr, "DLL_THREAD_ATTACH\n");
        break;
    case DLL_THREAD_DETACH:
        strcat(ptr, "DLL_THREAD_DETACH\n");
        break;
    }
}

// TLS回调
void NTAPI tls_callback_1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    char ptr[256] { 0 };
    strcpy(ptr, "    TLS Callback1: ");


    generate_str(ptr, Reason);

    WriteConsoleA(hStdout, ptr, strlen(ptr), NULL, NULL);
}

// TLS回调
void NTAPI tls_callback_2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    char ptr[256] { 0 };
    strcpy(ptr, "    TLS Callback2: ");

    generate_str(ptr, Reason);

    WriteConsoleA(hStdout, ptr, strlen(ptr), NULL, NULL);
}

#ifdef _WIN64
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK thread_callback_base[] = { tls_callback_1, tls_callback_2, 0 };
#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64

#define OK_PRINT_FLAG(s) ("\033[1;40;32m[+]\033[0m "##s)
#define ERR_PRINT_FLAG(s) ("\033[1;40;31m[-]\033[0m "##s)

// 线程回调
DWORD WINAPI thread_func(
    _In_ LPVOID lpParameter
)
{
    printf(OK_PRINT_FLAG("Enter thread func...\n"));
    Sleep(5000);
    printf(OK_PRINT_FLAG("Leave thread func...\n"));
    return 0;
}

int main(void)
{
    printf(OK_PRINT_FLAG("Enter main...\n"));

    HANDLE hThread = CreateThread(NULL, 0, thread_func, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    printf(OK_PRINT_FLAG("Leave main...\n"));
    return 0;
}

![](1.png)

注:x64dbg中,设置 - 事件选项卡中勾选TLS回调函数,则可用在TLS回调执行时断下

![](2.png)

TLS记录存储在.tls节中,其回调在.rdata

![](3.png)


添加新评论