Windows TLS原理
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows TLS原理相关的知识,希望对你有一定的参考价值。
显示TLS
首先看代码
#include <iostream>
#include<Windows.h>
#include<tchar.h>
#include<winnt.h>
DWORD dwIdx = 0;
int GLOBAL_VAR = 4;
DWORD threadFun(
LPVOID lpThreadParameter
)
Sleep(10);
DWORD dwVa2 = (DWORD)TlsGetValue(dwIdx);
printf("threadFun\\r\\n");
return 0;
int main()
printf("main\\r\\n");
dwIdx = TlsAlloc();
TlsSetValue(dwIdx, (LPVOID)0x111);
DWORD dwVa = (DWORD)TlsGetValue(dwIdx);
HANDLE tHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFun, NULL, 0, 0);
CloseHandle(tHandle);
DWORD dwVa2 = (DWORD)TlsGetValue(dwIdx);
Sleep(100000000);
TlsFree(dwIdx);
printf("main end\\r\\n");
return 0;
利用windbg来一探究竟。
首先明白一个基础知识 fs:[0]
在用户态情况下指向如下数据结构:
其中+0x018
指向TEB
数据结构
其中TEB的 0xe10
偏移指向一个数组
我们断点TlsSetValue函数内部
隐式TLS
#include <iostream>
#include<Windows.h>
#include<tchar.h>
#include<winnt.h>
__declspec(thread) int g_nVal = 0x111111;
DWORD threadFun(
LPVOID lpThreadParameter
)
Sleep(10);
printf("threadFun g_nVal %x \\r\\n", g_nVal);
g_nVal = 0x333;
printf("threadFun g_nVal222 %x \\r\\n", g_nVal);
return 0;
int main()
printf("g_nVal %x \\r\\n",g_nVal);
g_nVal = 0x22222;
printf("g_nVal2222 %x \\r\\n", g_nVal);
HANDLE tHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFun, NULL, 0, 0);
WaitForSingleObject(tHandle, INFINITE);
CloseHandle(tHandle);
printf("g_nVal333 %x \\r\\n", g_nVal);
printf("main end\\r\\n");
return 0;
我们反汇编对其赋值语句
我们首先不需要管_tls_index是写死在PE中的地址.
其中fs:[2ch]
指向TEB中的一个关于tls指针。这块区域用于存储TLS隐士变量数据
需要特别注意每个线程的 02c返回数据地址都是不一样的
我们首先看main线程的teb中的地址为004b3ef0
再看看子线程
上面的这些信息全部记录在PE中。
在PE
文件目录中的第十项便是TLS信息记录
这个地址指向的数据结构为如下所示:
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;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;
其中AddressOfIndex就是我们汇编中的_tls_index。
其他字段我们使用更复杂的例子来说明。
#include <iostream>
#include<Windows.h>
#include<tchar.h>
#include<winnt.h>
__declspec(thread) int g_nVal = 0x111111;
__declspec(thread) int g_n2Val =0x222223;
DWORD threadFun(
LPVOID lpThreadParameter
)
Sleep(10);
printf("threadFun g_nVal %x \\r\\n", g_nVal);
g_nVal = 0x333;
g_n2Val = 0x333;
printf("threadFun g_nVal222 %x \\r\\n", g_nVal);
return 0;
int main()
printf("g_nVal %x \\r\\n",g_nVal);
g_nVal = 0x22222;
g_n2Val = 0x3333;
printf("g_nVal2222 %x \\r\\n", g_nVal);
HANDLE tHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFun, NULL, 0, 0);
WaitForSingleObject(tHandle, INFINITE);
CloseHandle(tHandle);
printf("g_nVal333 %x \\r\\n", g_nVal);
printf("main end\\r\\n");
return 0;
上面的例子举例了两个TLS变量
上图高亮区域就是StartAddressOfRawData
和EndAddressOfRawData
标记区域,每个线程都一块这块区域的拷贝因此可以防止线程间干扰。你可以发现里面有两个初始化数据
TLS 回调
基础知识
1、https://www.cnblogs.com/dliv3/p/6489629.html
2、https://bbs.pediy.com/thread-267175.htm
#include <iostream>
#include<Windows.h>
#include<tchar.h>
#include<winnt.h>
//如果没有使用tls隐式变量是不会生产IMAGE_TLS_DIRECTORY32
#pragma comment(linker, "/INCLUDE:__tls_used")
void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
printf("TLS_CALLBACK2 main tid %08x \\r\\n", GetCurrentThreadId());
switch (Reason)
case DLL_PROCESS_ATTACH:
printf("DLL_PROCESS_ATTACH tid %08x \\r\\n",GetCurrentThreadId());
break;
case DLL_PROCESS_DETACH:
printf("DLL_PROCESS_DETACH tid %08x \\r\\n", GetCurrentThreadId());
break;
case DLL_THREAD_ATTACH:
printf("DLL_THREAD_ATTACH tid %08x \\r\\n", GetCurrentThreadId());
break;
case DLL_THREAD_DETACH:
printf("DLL_THREAD_DETACH tid %08x \\r\\n", GetCurrentThreadId());
break;
default:
break;
/*
注册TLS函数
.CRT$XLX的作用
CRT表示使用C Runtime 机制
X表示表示名随机
L表示TLS Callback section
X也可以换成B~Y任意一个字符
*/
#pragma data_seg(".CRT$XLX")
//存储回调函数地址
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = TLS_CALLBACK2, 0 ;
#pragma data_seg()
DWORD threadFun(
LPVOID lpThreadParameter
)
printf("thread start\\r\\n");
printf("thread end\\r\\n");
return 0;
int main()
printf("main start\\r\\n");
HANDLE tHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadFun, NULL, 0, 0);
WaitForSingleObject(tHandle, INFINITE);
//CloseHandle(tHandle);
printf("main end\\r\\n");
return 0;
为什么需要注册在.CRT$XL
节中?
这个问题我们需要翻阅运行时支持库中的tlssup.cpp
源码
其中需要明白相似名称节会按照字典顺序加载
/***
*tlssup.cpp - Thread Local Storage run-time support module
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*
****/
#undef CRTDLL
#undef MRTDLL
#include <internal_shared.h>
#include <Windows.h>
extern "C"
/* Thread Local Storage index for this .EXE or .DLL */
ULONG _tls_index = 0;
/* Special symbols to mark start and end of Thread Local Storage area. */
#pragma data_seg(".tls")
#if defined (_M_X64)
_CRTALLOC(".tls")
#endif /* defined (_M_X64) */
char _tls_start = 0;
#pragma data_seg(".tls$ZZZ")
#if defined (_M_X64)
_CRTALLOC(".tls$ZZZ")
#endif /* defined (_M_X64) */
char _tls_end = 0;
#pragma data_seg()
/* Start section for TLS callback array examined by the OS loader code.
* If dynamic TLS initialization is used, then a pointer to __dyn_tls_init
* will be placed in .CRT$XLC by inclusion of tlsdyn.obj. This will cause
* the .CRT$XD? array of individual TLS variable initialization callbacks
* to be walked.
*/
_CRTALLOC(".CRT$XLA") PIMAGE_TLS_CALLBACK __xl_a = 0;
/* NULL terminator for TLS callback array. This symbol, __xl_z, is never
* actually referenced anywhere, but it must remain. The OS loader code
* walks the TLS callback array until it finds a NULL pointer, so this makes
* sure the array is properly terminated.
*/
_CRTALLOC(".CRT$XLZ") PIMAGE_TLS_CALLBACK __xl_z = 0;
#ifdef _WIN64
_CRTALLOC(".rdata$T")
extern const IMAGE_TLS_DIRECTORY64 _tls_used =
(ULONGLONG) &_tls_start, // start of tls data
(ULONGLONG) &_tls_end, // end of tls data
(ULONGLONG) &_tls_index, // address of tls_index
(ULONGLONG) (&__xl_a+1), // pointer to call back array
(ULONG) 0, // size of tls zero fill
(ULONG) 0 // characteristics
;
#else /* _WIN64 */
_CRTALLOC(".rdata$T")
extern const IMAGE_TLS_DIRECTORY _tls_used =
(ULONG)(ULONG_PTR) &_tls_start, // start of tls data
(ULONG)(ULONG_PTR) &_tls_end, // end of tls data
(ULONG)(ULONG_PTR) &_tls_index, // address of tls_index
(ULONG)(ULONG_PTR) (&__xl_a+1), // pointer to call back array
(ULONG) 0, // size of tls zero fill
(ULONG) 0 // characteristics
;
#endif /* _WIN64 */
// extern "C"
你会发现上面代码中__xl_a+1
就是取.CRT$XLA
下一个节,如果用户没有定义就指向__xl_z
以上是关于Windows TLS原理的主要内容,如果未能解决你的问题,请参考以下文章