Windows PE第九章 线程局部存储
线程局部存储(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第九章 线程局部存储的更多相关文章
- Windows核心编程 第九章 线程与内核对象的同步(上)
		
第9章 线程与内核对象的同步 上一章介绍了如何使用允许线程保留在用户方式中的机制来实现线程同步的方法.用户方式同步的优点是它的同步速度非常快.如果强调线程的运行速度,那么首先应该确定用户方式的线程同步 ...
 - Windows PE  第一章开发环境和基本工具使用
		
第一章 Windows PE 基本工具 1.1开发语言MASM32 1.1.1设置开发环境 这个不细说了,我在整理Intel汇编的时候详细的说了环境搭建以及细节.地址是:http://blog.csd ...
 - 【windows核心编程】线程局部存储TLS
		
线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来 ...
 - Windows核心编程 第九章 线程与内核对象的同步(下)
		
9.4 等待定时器内核对象 等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象.它们通常用来在某个时间执行某个操作. 若要创建等待定时器,只需要调用C r e a t e Wa i ...
 - Windows PE  第一章 熟悉OD(顺便破解一个小工具)
		
熟悉OD(顺便破解一个小工具) 上一节了解了OD的简单使用,这次就练习下,目标是破解一款小软件(入门练手用的,没有壳什么的). 首先我们来看一下这个小软件: 我们的目的是输入任何字符串都可以成功注册, ...
 - 线程局部存储(TLS)
		
线程局部存储(TLS) 2011-10-11 09:59:28| 分类: Win32---API | 标签:tls |举报 |字号 订阅 什么是线程局部存储 众所周知,线程是执行的单元,同 ...
 - windows核心编程---第五章 线程的基础
		
与前面介绍的进程一样,线程也有两部分组成.一个是线程内核对象.它是一个数据结构,操作系统用它来管理线程以及用它来存储线程的一些统计信息.另一个是线程栈,用于维护线程执行时所需的所有函数参数和局部变量. ...
 - Windows  Pe 第三章 PE头文件(上)
		
第三章 PE头文件 本章是全书重点,所以要好好理解,概念比较多,但是非常重要. PE头文件记录了PE文件中所有的数据的组织方式,它类似于一本书的目录,通过目录我们可以快速定位到某个具体的章节:通过P ...
 - 【windows核心编程】 第六章  线程基础
		
Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ① 一个是线程的内核 ...
 
随机推荐
- MMA CTF 2nd 2016-greeting
			
目录 MMA CTF 2nd 2016-greeting 总结 题目分析 checksec 函数分析 漏洞点 知识点 利用思路 EXP 完整Exp MMA CTF 2nd 2016-greeting ...
 - FreeBSD 12.2 阿里云镜像使用说明
			
目前直接从阿里云 12.1 升级 12.2 会导致错误.镜像非本人制作.FreeBSD 12.2 阿里云镜像使用说明镜像下载地址: http://t.cn/A6taB5jO修改内容:对 /usr/sr ...
 - Windows包管理器——Scoop 包管理器
			
Scoop 包管理器 目录 Scoop 包管理器 参考 官方 博客 声明 目录 scoop 安装&&卸载 安装( 使用 powershell) 卸载(软件的使用权归自己所有,一言不合即 ...
 - 安装JDK9,jemter无法正常启动,怎么退回到JDK8
			
安装JDK8,配置环境变量 java -version显示的是8.1 然后安装JDK9之后,java -version显示的是9+8.1 这个时候,无法正常启动jemter 在环境变量中把path的C ...
 - 2019HDU多校第一场 6582 Path 【最短路+最大流最小割】
			
一.题目 Path 二.分析 首先肯定要求最短路,然后如何确定所有的最短路其实有多种方法. 1 根据最短路,那么最短路上的边肯定是可以满足$dist[from] + e.cost = dist[to] ...
 - 什么是IPFS集群?IPFS集群有什么好处?
			
IPFS作为区块链不多的创新技术,其热度一直居高不下.IPFS挖矿效率最高的就是集群结构,那么今天我就带着大家了解IPFS的集群挖矿. 什么是集群挖矿? 集群(cluster)就是计算机集群,指在 ...
 - 阿里二面:什么是mmap?
			
平时在面试中你肯定会经常碰见的问题就是:RocketMQ为什么快?Kafka为什么快?什么是mmap? 这一类的问题都逃不过的一个点就是零拷贝,虽然还有一些其他的原因,但是今天我们的话题主要就是零拷贝 ...
 - pta 简单求和
			
6-1 简单求和 (10 分) 本题要求实现一个函数,求给定的N个整数的和. 函数接口定义: int Sum ( int List[], int N ); 其中给定整数存放在数组List[]中,正 ...
 - OpenCV 之 平面单应性
			
上篇 OpenCV 之 图象几何变换 介绍了等距.相似和仿射变换,本篇侧重投影变换的平面单应性.OpenCV相关函数.应用实例等. 1 投影变换 1.1 平面单应性 投影变换 (Projectiv ...
 - PAT (Basic Level) Practice (中文)1065 单身狗 (25 分)  凌宸1642
			
PAT (Basic Level) Practice (中文)1065 单身狗 (25 分) 凌宸1642 题目描述: "单身狗"是中文对于单身人士的一种爱称.本题请你从上万人的大 ...