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 PE 第八章 延迟加载导
- 下一篇: Windows PE 第十章 加载配置