zoukankan      html  css  js  c++  java
  • TLS(线程局部存储)以及基于TLS技术的反调试技术

    在说TLS反调试技术之前,我们先看一下TLS技术是什么。

    TLS是各线程的独立的数据存储空间,使用TLS技术可在线程内部独立使用或者修改进程的全局数据或是静态数据,就像对待自身的局部变量一样。TLS回调函数常用于反调试,因为TLS回调函数运行会先于EP代码执行。

    若在编程中使用了TLS功能,PE头文件中就会设置TLS表项目

    IMAGE_TLS_DIRECTORY结构体定义如下:

    ypedef struct _IMAGE_TLS_DIRECTORY64 {
        ULONGLONG StartAddressOfRawData;
        ULONGLONG EndAddressOfRawData;
        ULONGLONG AddressOfIndex;         // PDWORD
        ULONGLONG AddressOfCallBacks;     // PIMAGE_TLS_CALLBACK *;
        DWORD SizeOfZeroFill;
        union {
            DWORD Characteristics;
            struct {
                DWORD Reserved0 : 20;
                DWORD Alignment : 4;
                DWORD Reserved1 : 8;
            } DUMMYSTRUCTNAME;
        } DUMMYUNIONNAME;
    } IMAGE_TLS_DIRECTORY64;
    typedef struct _IMAGE_TLS_DIRECTORY32 {
        DWORD   StartAddressOfRawData;
        DWORD   EndAddressOfRawData;
        DWORD   AddressOfIndex;             // PDWORD
        DWORD   AddressOfCallBacks;         // PIMAGE_TLS_CALLBACK *
        DWORD   SizeOfZeroFill;
        union {
            DWORD Characteristics;
            struct {
                DWORD Reserved0 : 20;
                DWORD Alignment : 4;
                DWORD Reserved1 : 8;
            } DUMMYSTRUCTNAME;
        } DUMMYUNIONNAME;
    } IMAGE_TLS_DIRECTORY32;
    
    需要注意的是,AddressOfCallBack成员指向的是一个含有TLS回调函数的数组,这表明可以注册多个TLS回调函数。
    TLS回调函数定义如下:
    typedef VOID
    (NTAPI *PIMAGE_TLS_CALLBACK) (
        PVOID DllHandle,    //模块句柄
        DWORD Reason,       //调用TLS回调函数时机
        PVOID Reserved      //
        );
    

    第二参数有四个状态:

    #define DLL_PROCESS_ATTACH   1   
    #define DLL_THREAD_ATTACH    2   
    #define DLL_THREAD_DETACH    3   
    #define DLL_PROCESS_DETACH   0   

    我们可以看到TLS回调函数很像DllMain()函数。下面我们来一个例子加深对TLS的印象。

    #include "stdafx.h"
    #include <Windows.h>
    #pragma comment(linker,"/INCLUDE:__tls_used")
    void PrintAtShell(WCHAR* wzMessage);
    DWORD WINAPI ThreadProc(LPVOID lParam);
    void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved);
    void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved);
    int main()
    {
           HANDLE ThreadHandle = NULL;
           PrintAtShell(L"main() start!
    ");
           ThreadHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
           WaitForSingleObject(ThreadHandle, 60 * 1000);
           CloseHandle(ThreadHandle);
           PrintAtShell(L"main() end!
    ");
        return 0;
    }
    void PrintAtShell(WCHAR* wzMessage)
    {
           //检索指定设备句柄,STD_OUTPUT_HANDLE指示标准输出设备
           HANDLE StdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
           //将输入内容显示到控制台屏幕上,类似于printf,但是因为TLS回调函数先于main()函数执行,所以有可能printf()函数无法正常使用
           WriteConsole(StdHandle, wzMessage,lstrlen(wzMessage), NULL, NULL);
    }
    DWORD WINAPI ThreadProc(LPVOID lParam)
    {
           PrintAtShell(L"ThreadProc() start!
    ");
           PrintAtShell(L"ThreadProc() end!
    ");
           return 0;
    }
    void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
    {
           WCHAR wzMessage[80] = { 0 };
           wsprintf(wzMessage, L"TLS_CALLBACK1():DllHandle = %X,Reason = %d
    ", DllHandle, Reason);
           PrintAtShell(wzMessage);
    }
    void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
    {
           WCHAR wzMessage[80] = { 0 };
           wsprintf(wzMessage, L"TLS_CALLBACK2():DllHandle = %X,Reason = %d
    ", DllHandle, Reason);
           PrintAtShell(wzMessage);
    }
    #pragma data_seg(".CRT$XLX")
    PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1,TLS_CALLBACK2,0 };
    #pragma data_seg()

    输出结果:

    TLS_CALLBACK1():DllHandle = 930000,Reason = 1
    TLS_CALLBACK2():DllHandle = 930000,Reason = 1
    main() start!
    TLS_CALLBACK1():DllHandle = 930000,Reason = 2
    TLS_CALLBACK2():DllHandle = 930000,Reason = 2
    ThreadProc() start!
    ThreadProc() end!
    TLS_CALLBACK1():DllHandle = 930000,Reason = 3
    TLS_CALLBACK2():DllHandle = 930000,Reason = 3
    main() end!
    TLS_CALLBACK1():DllHandle = 930000,Reason = 0
    TLS_CALLBACK2():DllHandle = 930000,Reason = 0

    因为我们定义了两个TLS回调函数,当进程刚刚启动时调用TLS回调函数,打印出1,然后main函数启动,之后又启动一个线程,触发TLS回调函数2,然后线程启动,关闭线程之后,调用TLS3,最后进程结束,触发0事件。

    相信这时候对TLS回调函数有了一定理解了吧,现在我们开始正题,如何利用TLS回调函数进行反调试。

    我们知道了TLS回调函数在main()函数执行之前执行,所以,我们可以定义一个TLS回调函数去检测当前进程有没有被调试。我们知道调试器一般会下int 3断点进行调试,我们只需在函数入口处检查是否下了int 3断点即可判断是否被调试。所以我们直接看代码吧:

    #include "stdafx.h"
    #include <Windows.h>
    #pragma comment(linker,"/INCLUDE:__tls_used")
    void NTAPI MY_TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved);
    int main()
    {
           MessageBox(NULL, L"运行", L"警告", 0);
        return 0;
    }
    void NTAPI MY_TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved)
    {
           if (Reason == DLL_PROCESS_ATTACH)
           {
                  //获得当前Exe模块基地址
                  PIMAGE_DOS_HEADER   pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
                  PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew);
                  //执行函数入口
                  BYTE* OEP = (BYTE*)(pNtHeader->OptionalHeader.AddressOfEntryPoint + (DWORD)pDosHeader);
                  //判断函数入口处有没有被调试器下int3断点
                  for (int i = 0; i < 200; i++)
                  {
                         if (OEP[i] == 0xCC)
                         {
                               MessageBox(NULL, L"调试", L"警告", 0);
                               ExitProcess(0);
                         }
                  }
           }
    }
    #pragma data_seg(".CRT$XLX")
    PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { MY_TLS_CALLBACK,0 };
    #pragma data_seg()
    

    运行结果:

    未调试状态:


    用x32dbg或者OD调试状态:


  • 相关阅读:
    Different ways how to escape an XML string in C# (zz)
    sql server 中nvarchar(max)性能
    使用 access 的一些限制条件 (zz)
    js 常用属性和方法
    js 常用关键字及方法
    <推荐>35个优秀的电子商务网站界面 (转)
    ASP.NET底层架构 22
    JSON 学习总结(1)
    学习记录
    asp.net原理(总结整理 2)
  • 原文地址:https://www.cnblogs.com/Toring/p/6628264.html
Copyright © 2011-2022 走看看