TLS反调试
0x01 TLS反调试简介
TLS(Thread Local Storage)原本的目的是解决多线程程序中变量同步的问题。线程本身有独立于其他线程的栈空间,因此线程中的局部变量不用考虑同步问题。多线程同步问题在于对全局变量的访问,TLS在操作系统的支持下,通过把全局变量打包到一个特殊的节,当每次创建线程时把这个节中的数据当做副本,拷贝到进程空闲的地址空间中。以后线程可以像访问局部变量一样访问该异于其他线程的全局变量的副本,而不用加同步控制。
基于TLS的反调试,原理实为在实际的入口点代码执行之前执行检测调试器代码,实现方式便是使用TLS回调函数实现。通过TLS反调试实现的效果。
0x02 实现原理
(1)TLS回调函数
当用户选择使用自己编写的回调函数时,在应用程序初始化阶段,系统将要调用一个由用户编写的回调函数以完成相应的初始化以及其他的一些初始化工作。此调用将在程序真正开始执行到入口点之前就完成,以保证程序执行的正确性。
TLS回掉函数:
1 | void NTAPI TLS_CALLBACK( PVOID DllHandle, DWORD Reason, PVOID Reserved); |
第一个参数为模块句柄,即加载地址,第二个参数为调用原因,调用原因有四种:
1 2 3 4 | #define DLL_PROCESS_ATTACH 1 #define DLL_THREAD_ATTACH 2 #define DLL_THREAD_DETACH 3 #define DLL_PROCESS_ATTACH 0 |
主线程调用main前调用TLS回调函数,调用原因为DLL_PROCESS_ATTACH
子线程启动前调用TLS,原因为DLL_THREAD_ATTACH
子线程结束后调用TLS,原因为DLL_THREAD_DETACH
主线程结束后调用TLS的原因为DLL_PROCESS_DETACH
TLS回掉函数注册:
1 | PIMAGE_TLS_CALLBACK _tls_callback[] = { SH_TLS_CALLBACKA, SH_TLS_CALLBACKB, 0 }; |
(2)TLS数据所在的节(Section)
PE文件中,专门为TLS数据开辟了一个节存储,它的RVA和大小记录在PE文件可选头的数据目录表中:IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]。
DataDirectory的数据结构:
1 2 3 4 | typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; |
(3)TLS数据段的建立
要使用TLS,必须为TLS数据单独建一个数据段,用相关数据填充此段,并通知链接器为TLS数据在PE文件头中添加数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //linker spec通知链接器PE文件要创建TLS目录,X86和X64有所区别 #ifdef _M_IX86 #pragma comment (linker, "/INCLUDE:__tls_used") #pragma comment (linker, "/INCLUDE:__tls_callback") #else #pragma comment (linker, "/INCLUDE:_tls_used") #pragma comment (linker, "/INCLUDE:_tls_callback") #endif //创建TLS段 EXTERN_C #ifdef _M_X64 #pragma const_seg (".CRT$XLB") const #else #pragma data_seg (".CRT$XLB") #endif |
1 2 3 4 5 6 7 8 | /* 注册TLS函数 .CRT$XLX的作用 CRT表示使用C Runtime 机制 X表示表示名随机 L表示TLS Callback section X也可以换成B~Y任意一个字符 */ |
0x03 源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | // TLS反调试.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <tchar.h> #pragma comment(lib,"ntdll.lib") extern "C" NTSTATUS NTAPI NtQueryInformationProcess( HANDLE hProcess, ULONG InfoClass, PVOID Buffer, ULONG Length, PULONG ReturnLength); #define NtCurrentProcess() (HANDLE)-1 void NTAPI __stdcall SH_TLS_CALLBACKA( PVOID DllHandle, DWORD Reason, PVOID Reserved) { if (IsDebuggerPresent()) { MessageBoxA(NULL, "TLS_CALLBACK: Debugger Detected!" , "TLS Callback" , MB_OK); // ExitProcess(1); } else { MessageBoxA(NULL, "TLS_CALLBACK: No Debugger Present!..." , "TLS Callback" , MB_OK); } } void NTAPI __stdcall SH_TLS_CALLBACKB( PVOID DllHandle, DWORD Reason, PVOID Reserved) { HANDLE DebugPort = NULL; if (!NtQueryInformationProcess( NtCurrentProcess(), 7, // ProcessDebugPort &DebugPort, // If debugger is present, it will be set to -1 | Otherwise, it is set to NULL sizeof ( HANDLE ), NULL)) { if (DebugPort) { MessageBoxA(NULL, "TLS_CALLBACK2: Debugger detected!" , "TLS callback" , MB_ICONSTOP); } else { MessageBoxA(NULL, "TLS_CALLBACK2: No debugger detected" , "TLS callback" , MB_ICONINFORMATION); } } } //linker spec通知链接器PE文件要创建TLS目录,X86和X64有所区别 #ifdef _M_IX86 #pragma comment (linker, "/INCLUDE:__tls_used") #pragma comment (linker, "/INCLUDE:__tls_callback") #else #pragma comment (linker, "/INCLUDE:_tls_used") #pragma comment (linker, "/INCLUDE:_tls_callback") #endif //创建TLS段 EXTERN_C #ifdef _M_X64 #pragma const_seg (".CRT$XLB") const #else #pragma data_seg (".CRT$XLB") #endif //end linker //tls import定义多个回调函数 PIMAGE_TLS_CALLBACK _tls_callback[] = { SH_TLS_CALLBACKA, SH_TLS_CALLBACKB, 0 }; #pragma data_seg () #pragma const_seg () //end int main() { MessageBoxA(NULL, "SH TLS ANTI-DEBUG" , "MAIN" , MB_OK); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗