TLS回调函数反调试技术

0x00 什么是TLS 回调函数

TLS是各线程的独立的数据存储空间,使用TLS技术可在线程内部独立使用或修改进程的全局数据或静态数据,就像对待自身的局部变量一样,多用于线程间同步。
TLS (Thread Local Storage 线程局部存储 )回调函数每当创建/终止进程的线程时会自动调用执行的函数。

void NTAPI TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved)//回调函数的声明,和DllMain类似

0x01 如何反调试

由于在创建主线程的时候会自动调用TLS回调函数,并且调用的时机早于OEP的执行,所以可以在程序入口点之前在TLS回调函数内部完成对调试器的检测。

反调试Demo代码如下;

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

typedef NTSTATUS(NTAPI* pfnNtQueryInformationProcess)(
    _In_      HANDLE           ProcessHandle,
    _In_      UINT             ProcessInformationClass,
    _Out_     PVOID            ProcessInformation,
    _In_      ULONG            ProcessInformationLength,
    _Out_opt_ PULONG           ReturnLength
    );
const UINT ProcessDebugPort = 7;


typedef NTSTATUS(NTAPI* pfnNtSetInformationThread)(
    _In_ HANDLE ThreadHandle,
    _In_ ULONG  ThreadInformationClass,
    _In_ PVOID  ThreadInformation,
    _In_ ULONG  ThreadInformationLength
    );
const ULONG ThreadHideFromDebugger = 0x11;



//告知连接器使用TLS
#pragma comment(linker, "/INCLUDE:__tls_used")

DWORD isDebuggerPresent = 0;
void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    if (Reason == DLL_PROCESS_ATTACH) //和DLL类似
    {
        MessageBox(NULL, _T("TLS_CALLBACK 1 函数执行"), _T("提示"), MB_OK);
        //利用NtQueryInformationProcess执行反调试
        HMODULE hNtDll = LoadLibrary(_T("ntdll.dll"));
        pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
        NTSTATUS status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &isDebuggerPresent, sizeof(DWORD), NULL);

        if (status == 0x00000000 && isDebuggerPresent != 0)
        {
            MessageBox(NULL, _T("Debug!!!"), _T("提示"), MB_OK);
            exit(-1);
        }
    }

}

void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    if (Reason == DLL_PROCESS_ATTACH)
    {
        MessageBox(NULL, _T("TLS_CALLBACK 2 函数执行"), _T("提示"), MB_OK);

        //利用NtSetInformationThread执行反调试
        HMODULE hNtDll = LoadLibrary(_T("ntdll.dll"));
        pfnNtSetInformationThread NtSetInformationThread = (pfnNtSetInformationThread)GetProcAddress(hNtDll, "NtSetInformationThread");
        NTSTATUS status = NtSetInformationThread(GetCurrentThread(),ThreadHideFromDebugger, NULL, 0);//停止发送关于调试事件的通知
    }

}

int main(void)
{
    MessageBox(NULL, _T("Main函数执行"), _T("提示"), MB_OK);
    return 0;
}

#pragma data_seg(".CRT$XLX")
//存储回调函数地址
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, NULL };
#pragma data_seg()

0x02 如何反反调试

我们发现直接拖入OD中去,程序自动跑了起来,运行结束就终止了毫无调试的空间,此时就要使用OD上的一个非常好用的插件了 StrongOD 并勾选上 Break on Tls

image-20210119161712692

重新加载程序就会断在TLS回调函数1上了

image-20210119162342923

image-20210119162027803

在根据IDA和OD动静配合解掉反调试

0x03 参考资料

TLS回调函数

Windows平台常见反调试技术梳理(上)

Windows平台常见反调试技术梳理(下)

0x04 TLS反调试实例:

18年网鼎杯第二场 give_a_try