轉載自:
http://bbs.pediy.com/showthread.php?p=442958
TLS簡介
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++;
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.
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的內部數據結構
1.jpg
圖1 用于管理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目錄
7.JPGTLS Callback Functions這是線程建立和退出時的回調函數, 包括主線程和其他線程.AddressOfCallBacks 是指向函數指針數組的指針, 以 0 結束.

typedef struct _TEB
{
NT_TIB Tib;
PVOID EnvironmentPointer;
CLIENT_ID Cid;
PVOID ActiveRpcInfo;
PVOID ThreadLocalStoragePointer; ; 2ch
PPEB Peb; ; 30h
ULONG LastErrorValue; ; 34h
…}

TLS目錄 #define IMAGE_DIRECTORY_ENTRY_TLS 9 (第十個目錄)
Tls隱藏的入口點利用
就是利用Address of Callbacks字段,寫入要執行的代碼的地址就可以了. 測試對象:test.exe
程序類型:delphi編寫
PeEditor檢測Tls信息(文件偏移59400H處)如下:
2.jpg 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
;----------------------------------------------------------------

編譯生成可執行文件,這只是一個簡單的彈出對話框的程序。
3.jpg 打開16進制工具,載入msgbox.exe,復制代碼:
4.jpg 注意00402000H處存放的是MessageBox的入口地址,我把它固化下來,動態跟蹤發現是8A05D577H。(當然在編程時可以動態搜尋我們需要的API,為簡單起見這里Hard-code一下。)
打開16進制工具,載入藝術字體2.exe,定位到00059410。
5.jpg 我們把代碼復制在偏移00059410處,對應的VA=0045E030H,則把0045E030存入偏移00059410。
還要注意最后的 MessageBox的入口地址我們使用硬編碼的方式,我們把這個地址寫入在00059480處了。保存修改,然后運行程序。程序首先彈出兩個對話框(一個是TLS模板,一個是主線程創建),結束程序運行還會彈出一個對話框,不過這個對話框太丑了:
6.jpg 說明:我在看一本介紹病毒知識的書籍時看到作者提及到這么一句話,大概意思是說他們公司某人發現了這個秘密,一直沒有公布,不過后來由于rgb利用此技術制造了病毒,也就無所謂秘密可言了。由于Tls入口要比OEP先執行,所以在加殼與脫殼中都有利用的價值。