【逆向学习】TLS_Callback TLS回调函数学习以及出题思路
浏览 64 | 评论 0 | 字数 3345
Xunflash
2023年03月08日
  • TLS_Callback

    TLS_Callback(Thread-local storage,线程本地存储)是WindowsAPI下的一个回调函数,它会在程序进程开始,结束以及线程开始,结束的时候调用。

    MSDN地址

    TLS在逆向中

    TLS_Callback在逆向中有一个特点: 先于main函数执行/在main结束后执行。因此也经常被用作反调试,或者是藏匿变量修改,以防止动态和静态分析。

    代码实现

    参考此处

    #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_s(ptr ,200, "DLL_PROCESS_ATTACH\n");
            break;
        case DLL_PROCESS_DETACH:
            strcat_s(ptr, 200, "DLL_PROCESS_DETACH\n");
            break;
        case DLL_THREAD_ATTACH:
            strcat_s(ptr, 200, "DLL_THREAD_ATTACH\n");
            break;
        case DLL_THREAD_DETACH:
            strcat_s(ptr, 200, "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_s(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_s(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;
    }

    执行效果:

    image-20230308150152221

    TLS回调函数调用顺序分析

    其中,tls_callback的第二个参数Reason可以有四个值分别是DLL_PROCESS_ATTACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH、DLL_PROCESS_DETACH

    分别对应1,2,3,0

    因此几次调用TLS回调函数的顺序也是1-2-3-0即DLL_PROCESS_ATTACH->DLL_THREAD_ATTACH->DLL_THREAD_DETACH->DLL_PROCESS_DETACH

    在ida中反编译可以看到:

    image-20230308145211171

    一些逆向出题思路

    因此可以在进程启动时,对程序进行反调试,例如

    image-20230308145625408

    在CTF逆向中,也可以利用DLL_PROCESS_DETACH后于main函数结束执行,在main函数结束后继续调用某些函数。

    通过配合CreateThread,对程序中某些变量重复修改也可以打出一套组合拳。

    本文作者:Xunflash
    本文链接:http://xunflash.top/index.php/archives/TLS_Callback.html
    最后修改时间:2023-03-08 16:01:11
    本站未注明转载的文章均为原创,并采用 CC BY-NC-SA 4.0 授权协议,转载请注明来源,谢谢!
    评论
    114514
    textsms
    支持 Markdown 语法
    email
    link
    评论列表
    暂无评论