Thread Local Storage

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 17:54   2001   0

1. 什么是TLS?

TLS是Thread Local Storage(线程局部存储)的简称,是一项解决多线程内部变量使用问题的技术。

用于将某些数据和一特定线程关联起来,即,这些数据为关联线程所独有(私有)。

在多线程编程中, 同一个变量, 如果要让多个线程共享访问, 那么这个变量可以使用关键字volatile进行声明;

而如果一个变量不想被多个线程共享访问, 那么就应该使用TLS。

2. 如何使用TLS编程?

TLS使用非常简单, 只要对变量声明时使用__declspec(thread)修饰就可以了。

例如:

__declspec(thread) int g_nData = 0;

3. 一个使用TLS的例子

//-------------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>


#define THREADCOUNT 4

DWORD dwTlsIndex;

int iNUM_OF_CALL_COMMON=0;

int iNUM_OF_CALL_THREAD=0;

VOID ErrorExit(LPSTR);

VOID CommonFunc(VOID) {

LPVOID lpvData;

// Retrieve a data pointer for the current thread.

iNUM_OF_CALL_COMMON++; l

pvData = TlsGetValue(dwTlsIndex);

if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))

ErrorExit("TlsGetValue error");

// Use the data stored for the current thread.

printf("common: thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);

Sleep(5000);

}

DWORD WINAPI ThreadFunc(VOID) {

LPVOID lpvData;

// Initialize the TLS index for this thread.

iNUM_OF_CALL_THREAD++;

lpvData = (LPVOID) LocalAlloc(LPTR, 256);

if (! TlsSetValue(dwTlsIndex, lpvData))

ErrorExit("TlsSetValue error");

printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);

CommonFunc();

// Release the dynamic memory before the thread returns.

lpvData = TlsGetValue(dwTlsIndex);

if (lpvData != 0)

LocalFree((HLOCAL) lpvData);

return 0;

}

int main(VOID) {

DWORD IDThread;

HANDLE hThread[THREADCOUNT];

int i;

// Allocate a TLS index.

if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)

ErrorExit("TlsAlloc failed"); // Create multiple threads.

for (i = 0; i < THREADCOUNT; i++) {

hThread[i] = CreateThread(NULL, // default security attributes

0, // use default stack size

(LPTHREAD_START_ROUTINE) ThreadFunc, // thread function

NULL, // no thread function argument

0, // use default creation flags

&IDThread); // returns thread identifier

// Check the return value for success.

if (hThread[i] == NULL)

ErrorExit("CreateThread error\n");

}

// printf("All threads were created.\n");

for (i = 0; i < THREADCOUNT; i++)

WaitForSingleObject(hThread[i], INFINITE);

TlsFree(dwTlsIndex);

printf("The nums of thread is: %d\n",iNUM_OF_CALL_THREAD);

printf("The nums of call is: %d\n",iNUM_OF_CALL_COMMON);

return 0;

}

VOID ErrorExit (LPSTR lpszMessage) {

fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);

}

//------------------------------------------------------------------------------

4. T L S的内部数据结构

每个标志均可设置为FREE或者INUSE,表示TLS槽( s l o t )是否正在使用。

Microsoft保证至少TLS_MINIMUM_AVAILABLE位标志是可供使用的。

5. 相关API Windows TLS的API: TlsAlloc, TlsFree, TlsSetValue, TlsGetValue。

● DWORD TlsAlloc(); //(若要使用动态T L S,首先必须调用TlsAlloc函数) 这个函数命令系统对进程中的位标志进行扫描,并找出一个FREE标志。

然后系统将该标志从FREE改为INUSE,并且TlsAlloc返回位数组中的标志的索引。

DLL(或APP)通常将该索引保存在一个全局变量中,因为它的值是每个进程而不是每个线程使用的值。

● BOOL TlsSetValue( //将一个值放入线程的数组中 DWORD dwTlsIndex, PVOID pvTlsValue);

● PVOID TlsGetValue(DWORD dwTlsIndex); //要从线程的数组中检索一个值

● BOOL TlsFree(DWORD dwTlsIndex); //当在所有线程中不再需要保留TLS槽时

参考资料:Jeffrey Richter《《Programming Applications for Microsoft Windows (4th Ed.)》》Chapter 21

6. TLS目录

TLS Callback Functions这是线程建立和退出时的回调函数,

包括主线程和其他线程.AddressOfCallBacks 是指向函数指针数组的指针,

以 0 结束.

typedef struct _TEB {

NT_TIB Tib;

PVOID EnvironmentPointer;

CLIENT_ID Cid;

PVOID ActiveRpcInfo;

PVOID ThreadLocalStoragePointer;

; 2chPPEB Peb;

; 30hULONG LastErrorValue; ; 34h…}

TLS目录 #define IMAGE_DIRECTORY_ENTRY_TLS 9 (第十个目录) Tls隐藏的入口点利用

就是利用Address of Callbacks字段,写入要执行的代码的地址就可以了.

测试对象:

test.exe 程序类型:

delphi编写 PeEditor检测Tls信息(文件偏移59400H处)如下:

TLS信息 ------------------------------------------ 目录表:

0045D000 TLS数据: 0045D010 索引变量: 004570A0 调用返回地址: 0045E010 (调用返回地址)

------------------------------------------

我们打算让系统在0045E010处执行我们的代码,就需要在这个地方对应的文件偏移处写入我们的代码。

观察节表信息 VA=0045E010,按照文件不需要重定位去计算则RVA=0005E010 观察节表信息,这个值处于.rdata节内。

.rdata节起始RVA=0005E000,起始Offset为00059400 则callback地址对应的Offset=(0005E010 - 0005E000)+ 00059400 =00059410

注意在这里(00059410H)应该写入一个函数地址。打造一个简单的代码: ;---------------------------------------------------------------- .

386 .model flat,stdcall option casemap:none include windows.inc include user32.inc includelib user32.lib .

code start: jmp @F db 'Run thru Tls entry point.',0 @@:

mov eax,$ sub eax,26 call @delta @delta: pop ebp sub ebp,offset @delta add eax,ebp invoke MessageBox,NULL,eax,NULL,0 ret end start ;---------------------------------------------------------------- 编译生成可执行文件,

这只是一个简单的弹出对话框的程序。

打开16进制工具,载入msgbox.exe,复制代码:

注意00402000H处存放的是MessageBox的入口地址,我把它固化下来,动态跟踪发现是8A05D577H。(当然在编程时可以动态搜寻我们需要的API,为简单起见这里Hard-code一下。) 打开16进制工具,载入艺术字体2.exe,定位到00059410。 我们把代码复制在偏移00059410处,对应的VA=0045E030H,则把0045E030存入偏移00059410。 还要注意最后的 MessageBox的入口地址我们使用硬编码的方式,我们把这个地址写入在00059480处了。保存修改,然后运行程序。程序首先弹出两个对话框(一个是TLS模板,一个是主线程创建),结束程序运行还会弹出一个对话框,不过这个对话框太丑了:

说明:我在看一本介绍病毒知识的书籍时看到作者提及到这么一句话,大概意思是说他们公司某人发现了这个秘密,一直没有公布,不过后来由于rgb利用此技术制造了病毒,也就无所谓秘密可言了。由于Tls入口要比OEP先执行,所以在加壳与脱壳中都有利用的价值。

以下的网址是关于TLS与DLL的:

http://msdn.microsoft.com/en-us/library/ms686997.aspx

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP