【PE文件】TLS

TLS表位置

IMAGE_NT_HEADERS.IMAGE_OPTIONAL_HEADER32.IMAGE_DATA_DIRECTORY_ARRAY[9].VirtualAddress

TLS表结构

TLS(Thread Local Storage,线程局部存储)以一个 IMAGE_TLS_DIRECTORY 结构体开始,TLS是各线程独立的数据存储空间,可以将数据与特定线程进行关联。

 1 struct _IMAGE_TLS_DIRECTORY 
 2 {
 3     0x00 DWORD StartAddressOfRawData;                 //内存起始地址
 4     0x04 DWORD EndAddressOfRawData;                   //内存终止地址
 5     0x08 LPDWORD AddressOfIndex;                      //地址索引,运行库用该索引来定位线程局部数据
 6     0x0c PIMAGE_TLS_CALLBACK *AddressOfCallBacks;     //地址数组,保存所有TLS回调函数地址,数组以NULL结尾
 7     0x10 DWORD SizeOfZeroFill;                        //后面跟0的个数
 8     0x14 DWORD Characteristics;                       //保留
 9 };
10 
11 //回调函数原型:
12 typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK)
13 (
14     PVOID DllHandle,                                  //模块基址
15     DWORD Reason,                                     //调用原因
16     PVOID Reserved                                    //保留
17 );
18 
19 //调用原因
20 {
21     DLL_PROCESS_ATTACH                                //1: 进程创建
22     DLL_THREAD_ATTACH                                 //2: 线程创建
23     DLL_THREAD_DETACH                                 //3: 线程结束
24     DLL_PROCESS_DETACH                                //0: 进程结束
25 }
26 
27 //注意:
28 1、_IMAGE_TLS_DIRECTORY结构中的地址都是VA(虚拟地址)而不是RVA,如果PE文件开启基址重定位则这些地址也需要进行修正。
29 2、AddressOfCallBacks是线程建立和退出时的回调函数,当一个线程被创建或销毁时,数组中的函数都会被调用。
30 3、程序运行时,TLS数据的初始化和回调函数都会在入口点(EntryPoint)之前执行。恶意程序经常使用该特性执行反调试等操作。

示例代码

 1 // TLS Tset.cpp : 定义控制台应用程序的入口点。
 2 //
 3 
 4 #include "stdafx.h"
 5 #include <windows.h>
 6 #pragma comment(linker, "/INCLUDE:__tls_used")
 7 
 8 
 9 // TLS回调_1
10 void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
11 {
12     char szMsg[80] = { 0, };
13     wsprintfA(szMsg, "TLS_CALLBACK1() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
14     OutputDebugString(szMsg);
15 }
16 
17 // TLS回调_2
18 void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
19 {
20     char szMsg[80] = { 0, };
21     wsprintfA(szMsg, "TLS_CALLBACK2() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
22     OutputDebugString(szMsg);
23 }
24 
25 // 注册TLS回调
26 #pragma data_seg(".CRT$XLX")
27     PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
28 #pragma data_seg()
29 
30 // 线程回调
31 DWORD WINAPI ThreadProc(LPVOID lParam)
32 {
33     OutputDebugString("ThreadProc() start\n");
34     OutputDebugString("ThreadProc() end\n");
35     return 0;
36 }
37 
38 // Main
39 int _tmain(int argc, _TCHAR* argv[])
40 {
41     OutputDebugString("main() start\n");
42 
43     //创建测试线程
44     HANDLE hThread = NULL;
45     hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
46     WaitForSingleObject(hThread, 60 * 1000);
47     CloseHandle(hThread);
48 
49     OutputDebugString("main() end\n");
50     
51     system("pause");
52     return 0;
53 }

2个TLS回调分别被调用执行4次,共8次:

 1 // 进程创建
 2 [15920] TLS_CALLBACK1() : DllHandle = 520000, Reason = 1
 3 [15920] TLS_CALLBACK2() : DllHandle = 520000, Reason = 1
 4 [15920] main() start
 5 
 6 // 线程创建
 7 [15920] TLS_CALLBACK1() : DllHandle = 520000, Reason = 2
 8 [15920] TLS_CALLBACK2() : DllHandle = 520000, Reason = 2
 9 [15920] ThreadProc() start
10 [15920] ThreadProc() end
11 // 线程结束
12 [15920] TLS_CALLBACK1() : DllHandle = 520000, Reason = 3
13 [15920] TLS_CALLBACK2() : DllHandle = 520000, Reason = 3
14 [15920] main() end
15 
16 // 进程结束
17 [15920] TLS_CALLBACK1() : DllHandle = 520000, Reason = 0
18 [15920] TLS_CALLBACK2() : DllHandle = 520000, Reason = 0
posted @ 2022-02-17 23:38  SunsetR  阅读(274)  评论(0)    收藏  举报