线程局部存储(TLS)

这个东西并不陌生了,之前写过了一个关于这个的应用,利用静态TLS姿势实现代码段静态加密免杀或者所谓的加壳思路。地址在这:http://blog.csdn.net/u013761036/article/details/53967943今天就简单的整理下TLS的相关概念和常规应用。一开始说了一大堆的Windows的进程与线程啥啥啥的概念和原理,这里直接省略。

什么是线程局部存储?

线程局部存储(Thread Local Storage,TLS)很好的解决了多线程设计中变量同步问题,比如你写一个exe里面有N个线程,你可以放弃使用TLS,因为你对自己设计的程序有比较全面的把握。你清楚自己设计的进程里总共有多少个线程,每个线程使用了哪些数据结构,内存空间申请、释放都在你的掌控之下,全局变量的访问全部都采用了同步技术,那是没问题的。如果你是一个DLL开发者,你无法确定调用这个DLL的素质程序里到底有多少个线程,每个线程的数据是如何定义的,这时,可以考虑使用线程据存储技术。

TLS分为静态和动态两种:

动态TLS,主要是使用这几个API TlsAlloc ,TlsGetValue,TlsSetValue,TlsFree

下面是微软MSDN上的一个例子,看下理解下:

#include <windows.h>
#include <stdio.h> #define THREADCOUNT 4
DWORD dwTlsIndex; VOID ErrorExit(LPSTR); VOID CommonFunc(VOID)
{ LPVOID lpvData; // Retrieve a data pointer for the current thread. lpvData = 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. 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"); } for (i = 0; i < THREADCOUNT; i++) WaitForSingleObject(hThread[i], INFINITE); TlsFree(dwTlsIndex);return 0;
} VOID ErrorExit (LPSTR lpszMessage)
{ fprintf(stderr, "%s\n", lpszMessage); ExitProcess(0);
}


静态TLS
    静态线程局部存储是操作系统提供的另外一种线程与数据绑定的技术。它与动态TLS的区别在于,通过静态线程局部存储指定的数据无需使用专门的API函数,随意在易用性上会更好一些。
    静态线程局部存储预先将变量定义在PE文件内部,一般使用.tls节存储,对于相关API的调用由操作系统来完成。这种方式的有点就是从高级语言程序员角度来看更简单了。这种实现方式使得TLS数据的定义与初始化就像程序中使用普通的静态变量那样。
    对静态TLS变量的定义不需要想动态线程局部存储一样,调用相关API,只需要做如下声明即可:
_declspec(thread) int tlsFlag=1;
     为了支持这种编程模式。PE中的.tls节会包含一下信息:
初始化数据
用于每个线程初始化和终止的回调函数
TLS索引


可执行代码访问静态TLS数据一般需要经过一下几个步骤:
1.在链接的时候,连接器设置TLS目录中的AddressOfIndex字段。这个字段指向一个位置,在这个位置保存程序用到的TLS索引。
2.当创建线程是,加载器通过将线程环境块TEB的地址放入FS寄存器来传递线程的TLS数组地址。距TEB开头0x2c的位置处的字段ThreadLocalStoragePointer指向TLS数组。
3.加载器将TLS索引值保存到AddressOfIndex字段指向的位置处。
4.可执行代码获取TLS索引以及TLS数组的位置。
5.可执行代码将索引乘以4,并将该值作为这个数组内的偏移来使用。通过以上方法获取给定程序和模块的TLS数据区的地址。每个线程拥有他自己的TLS数据区,但这对于线程是透明的,它并不需要知道怎为单个线程分配数据的。
6.单个的TLS数据对象都位于TLS数据区的某个固定偏移处,因此可以用这种方式访问。
静态的TLS主要的应用是TLS回调函数。


关于静态TLS的代码相关就直接去开头我说的那个网址去看吧,里面我写了一个代码内存加密的代码例子。有静态加载TLS的姿势。

Windows PE第九章 线程局部存储相关推荐

  1. 第九章 - 线程安全集合类

    第九章 - 线程安全集合类 线程安全集合类概述 线程安全集合类可以分为三大类: 遗留的线程安全集合如 Hashtable.Vector.线程安全的实现无非直接加synchronized 使用 Coll ...

  2. 第三章 线程局部存储 windows程序设计 王艳平版

    /// // 02UseTLS.cpp.cpp文件 /* 动态调TLS的典型步聚: 1,主线程调用TlsAlloc函数为线程局部存储分配索引 DWORD TlsAlloc(void) 返回一个TLS索 ...

  3. Windows核心编程 第九章 线程与内核对象的同步(上)

    第9章 线程与内核对象的同步 上一章介绍了如何使用允许线程保留在用户方式中的机制来实现线程同步的方法.用户方式同步的优点是它的同步速度非常快.如果强调线程的运行速度,那么首先应该确定用户方式的线程同步 ...

  4. 【windows核心编程】线程局部存储TLS

    线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来 ...

  5. Windows核心编程 第九章 线程与内核对象的同步(下)

    9.4 等待定时器内核对象 等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象.它们通常用来在某个时间执行某个操作. 若要创建等待定时器,只需要调用C r e a t e Wa i ...

  6. 第九章 线程与内核对象的同步(6)

    六.其他的线程同步函数 1.异步设备I/O 异步设备I/O使得线程能够启动一个读操作或写操作,但是不必等待读操作或写操作完成.设备对象是可以同步的内核对象,可以调用WaitForSingleObjec ...

  7. java2第九章的总结_java并发的艺术-读书笔记-第九章线程池

    使用线程池的好处: 1.降低资源消耗:减少了线程创建和销毁的资源消耗 2.提高响应速度,当任务到达时,线程可以不尽兴创建直接处理 3.提高线程的可管理性.使用线程池可以对线程进行统一的管理,监控,使用 ...

  8. 第九章 线程与内核对象的同步(4)

    四.信标内核对象 信标内核对象用于资源进行计数.包含:引用计数.最大资源数量(用于标识信标能够控制的资源的最大数量).当期资源数量(用于标识当前可以使用的资源的数量). 信标的使用规则:当前资源数量大 ...

  9. PE学习(九)第九章:TLS 动态TLS与静态TLS

    第九章:线程局部存储 PEB,在NT中,该结构可以从进程空间的FS:[0x30]处找到,PEB描述的信息主要包括:进程状态.进程堆.PE映像信息等,其中Ldr记录了进程加载进内存的所有模块的基地址. ...

最新文章

  1. 程序员致富的若干方法探讨
  2. O(n)线性构造后缀树详解(一)
  3. Meta AI发布图音文大一统模型Data2vec,4天在GitHub揽1.5万星
  4. Spring Boot 2.x基础教程:使用Flyway管理数据库版本
  5. mysql 5.0 to mysql 5.1的BTREE索引问题
  6. 今日头条 文章采集_我在今日头条的成长之路—文章的排版与结构
  7. 量子计算: 1秒完成传统计算机100年的任务量
  8. SAP Kyma和Marketing Cloud的连接 - Marketing Cloud里的配置
  9. python怎么编写流氓软件_PBot很多程序员都知道吧?深度分析一款基于python的恶意软件!...
  10. 移动前端的一些坑和解决方法(外观表现)
  11. 汉字为什么能流传至今_为什么中国的文字流传至今?
  12. android ts流解码,DVB开发之TS流的接收,解码与播放
  13. 关于springcloud中eureka报错com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException:
  14. 委托 和 事件 总括:
  15. opencv warp(扭曲)球面投影的原理
  16. 线段树 从入门到进阶(超清晰,简单易懂)
  17. window expects a time attribute for grouping in a stream environment.
  18. 活体检测技术哪家强?实测N种场景告诉你答案
  19. python中idx+=1_在Python中为apos;循环访问索引 Dovov编程网
  20. fedora dnf_如何使用DNF升级Fedora Linux系统

热门文章

  1. linux下OpenSSL的RSA密钥生成
  2. Android 3.0 r1中文API文档(104) —— ViewTreeObserver
  3. lnmp、lamp、lnmpa一键安装包
  4. intellij idea 15,webstorm 最新注册破解
  5. CSS3无前缀脚本prefixfree.js与Animatable使用
  6. JAVA不使用POI给Word文档添加水印
  7. 【C#食谱】【杭帮菜】菜单2:写一个TCP客户端
  8. 关于js封装函数的一些东西
  9. jquery书写一个简易的二级联动
  10. hadoop nn 运维一例