TLS(线程局部存储)以及基于TLS技术的反调试技术

来源:互联网 发布:windows Api编程过程 编辑:程序博客网 时间:2024/05/18 02:29

在说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!\r\n");       ThreadHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);       WaitForSingleObject(ThreadHandle, 60 * 1000);       CloseHandle(ThreadHandle);       PrintAtShell(L"main() end!\r\n");    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!\r\n");       PrintAtShell(L"ThreadProc() end!\r\n");       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\r\n", 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\r\n", 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调试状态:


0 0
原创粉丝点击