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变量


上图高亮区域就是StartAddressOfRawDataEndAddressOfRawData标记区域,每个线程都一块这块区域的拷贝因此可以防止线程间干扰。你可以发现里面有两个初始化数据

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原理的主要内容,如果未能解决你的问题,请参考以下文章

TLS 协议-对称加密原理

服务器环境:windows server 2016 信息泄露漏洞问题

SSL与TLS原理(未完成)

SSL与TLS原理(未完成)

jquery的隐士迭代是啥?高手解释一下?

SSL/TLS协议原理解读