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!\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调试状态:


 

转载于:https://www.cnblogs.com/Toring/p/6628264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值