日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TLS回调函数(1)

發布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TLS回调函数(1) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡述

代碼逆向分析領域中,TLS(Thread Local Storage,線程局部存儲)回調函數(Callback Function)常用反調試。TLS回調函數的調用運行要先于EP代碼的執行,該特征使它可以作為一種反調試技術的使用。

TLS

TLS是各線程的獨立的數據存儲空間,使用TLS技術可在線程內部獨立使用或修改進程的全局數據或靜態數據,就像對待自身的局部變量一樣。

IMAGE_DATA_DIRECTORY[9]

若在編程中啟用了TLS功能,PE頭文件中就會設置TLS表(TLS Table)項目,如下(IMAGE_NT_HEADERS-IMAGE_OPTIONAL_HEADER-IMAGE_DATA_DIRECTORY[9])


typedef struct _IMAGE_TLS_DIRECTORY64 {ULONGLONG StartAddressOfRawData;ULONGLONG EndAddressOfRawData;ULONGLONG AddressOfIndex; // PDWORDULONGLONG AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *;DWORD SizeOfZeroFill;union {DWORD Characteristics;struct {DWORD Reserved0 : 20;DWORD Alignment : 4;DWORD Reserved1 : 8;} DUMMYSTRUCTNAME;} DUMMYUNIONNAME;} IMAGE_TLS_DIRECTORY64;typedef struct _IMAGE_TLS_DIRECTORY32 {DWORD StartAddressOfRawData;DWORD EndAddressOfRawData;DWORD AddressOfIndex; // PDWORDDWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *DWORD SizeOfZeroFill;union {DWORD Characteristics;struct {DWORD Reserved0 : 20;DWORD Alignment : 4;DWORD Reserved1 : 8;} DUMMYSTRUCTNAME;} DUMMYUNIONNAME;} IMAGE_TLS_DIRECTORY32; typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;

_IMAGE_TLS_DIRECTORY32結構體有2種版本,分別為32位版本和64位版本。



代碼逆向分析中涉及的比較重要的成員AddressOfCallbacks,該值指向含有TLS回調函數地方(VA)的數組。這意味著可以向同一程序注冊多個TLS回調函數

回調函數地址數組


該數組中實際存儲的就是TLS回調函數的地址。進程啟動運行時,(執行EP代碼前)系統會逐一調用存儲在該數組中的函數。
可以看出此數組有兩個回調函數(地址為4113B1和41129E),我們可以通過修改程序注冊多個TLS函數

TLS回調函數

接下來就是從技術層面簡單整理之前介紹的TLS回調函數相關內容。

所謂TLS回調函數是指,每當創建/終止進程的線程時會自動調用執行的函數。有意思的是,創建進程的主線程時,也會自動調用回調函數,且其調用執行先于EP代碼。反調試技術利用的就是TLS回調函數的這一特征。

請注意,創建或終止某線程時,TLS回調函數都會自動調用執行,前后共2次(原意即為此)。
執行進程的主線程(運行線程的EP代碼)前,TLS回調函數會被先調用執行,許多逆向分析人員將該特征應用于程序的反調試技術。

IMAGE_TLS_CALLBACK

typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) (PVOID DllHandle,DWORD Reason,PVOID Reserved);

仔細觀察TLS回調函數的定義可以發現,它與DllMain的定義類似。

BOOL WINAPI DllMain(__in HINSTANCE hModule,__in DWORD fdwReason,__in LPVOID lpReserved);

觀察以上2個函數可以發現,它們的參數順序與含義都是一樣的。其實,參數DllMain為模塊句柄(即加載地址),參數fdwReason表示調用TLS回調函數的原因,具體原因有四種,如下所示

#define DLL_PROCESS_ATTACH 1 #define DLL_THREAD_ATTACH 2 #define DLL_THREAD_DETACH 3 #define DLL_PROCESS_ATTACH 0

接下來用例子講解這四種原因

#include<Windows.h>#pragma comment(linker,"/INCLUDE:__tls_used")void print_console(const char* szMsg) {HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);WriteConsole(hStdout, szMsg, strlen(szMsg), NULL, NULL);}void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved) {char szMsg[80] = { 0, };wsprintfA(szMsg, "TLS_CALLBACK1():DllHandle=%X,Reason=%d\n", DllHandle, Reason);print_console(szMsg); }void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved) {char szMsg[80] = { 0, };wsprintfA(szMsg, "TLS_CALLBACK2():DllHandle=%X,Reason=%d\n", DllHandle, Reason);print_console(szMsg); }#pragma data_seg(".CRT$XLX") PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1,TLS_CALLBACK2,0 };#pragma data_seg()DWORD WINAPI ThreadProc(LPVOID lPram) {print_console("ThreadProc() start\n");print_console("ThreadProc() end\n");return 0; }int main(void) {HANDLE hThread = NULL;print_console("main()start\n");hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);WaitForSingleObject(hThread, 60 * 1000);CloseHandle(hThread);print_console("main() end\n");system("pause");return 0; }

代碼講解:

#pragma comment(linker,"/INCLUDE:__tls_used")

告知鏈接器使用TLS

#pragma data_seg(".CRT$XLX") PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1,TLS_CALLBACK2,0 }; #pragma data_seg()

注冊TLS函數,.CRT$XLX的作用:CRT表示使用C Runtime 機制,X表示表示名隨機,L表示TLS Callback section,X也可以換成B~Y任意一個字符

PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1,TLS_CALLBACK2,0 };存儲回調函數地址

DLL_PROCESS_ATTACH

進程的主線程調用main函數前,已經注冊的TLS回調函數(TLS_CALLBACK1,TLS_CALLBACK2)會先被調用執行,此時Reason的值為1(DLL_PROCESS_ATTACH)

DLL_THREAD_ATTACH

所有的TLS回調函數執行完后,main()函數開始調用執行,創建用戶線程(ThreadProc)前,TLS回調函數會被再次調用執行,此時Reason的值為2(DLL_THREAD_ATTACH)

DLL_THREAD_DETACH

TLS回調函數全部 執行完畢后,ThreadProc()線程函數開始調用執行。其執行完畢后Reason=3(DLL_THREAD_DETACH),TLS回調函數被調用執行

DLL_PROCESS_DETACH

ThreadProc()線程函數執行完畢后,一直在等待線程終止的main函數(主線程)也會終止。此時Reason=0(DLL_PROCESS_ATTACH),TLS回調函數最后一次被調用執行。

補充:

源文件中并未使用printf()函數,因為開啟特定編譯選項(/MT)編譯源程序,先于主程序調用執行的TLS回調函數中可能發生Run-time Error(運行時錯誤)。此時可以直接調用WriteConsole() API來以防萬一。

調試TLS回調函數

若直接使用調試器打開帶有TLS回調函數的程序,則無法調試TLS回調函數,因為TLS在EP代碼之前就被調用執行了,如果TLS函數內部含有反調試代碼,這使程序直接無法繼續。需要如下操作選項----->調試設置---->事件------->點擊 系統斷點



調試器暫停的位置即是系統啟動斷點,在OD調試器的默認設置下,調試器會在EP處暫停,而WinDbg調試器默認在系統啟動斷點暫停

補充

DLL程序入口點函數:DllMain,注意:大小寫是區別的(僅導出資源的DLL可以沒有DllMain函數)

函數原型:

BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) {return TRUE; }

hModule參數:指向DLL本身的實例句柄;

ul_reason_for_call參數:指明了DLL被調用的原因,可以有以下4個取值:
1.DLL_PROCESS_ATTACH:
當DLL被進程 <<第一次>> 調用時,導致DllMain函數被調用,

同時ul_reason_for_call的值為DLL_PROCESS_ATTACH,

如果同一個進程后來再次調用此DLL時,操作系統只會增加DLL的使用次數,

不會再用DLL_PROCESS_ATTACH調用DLL的DllMain函數。

2.DLL_PROCESS_DETACH:
當DLL被從進程的地址空間解除映射時,系統調用了它的DllMain,傳遞的ul_reason_for_call值是DLL_PROCESS_DETACH。
★如果進程的終結是因為調用了TerminateProcess,系統就不會用DLL_PROCESS_DETACH來調用DLL的DllMain函數。這就意味著DLL在進程結束前沒有機會執行任何清理工作。

3.DLL_THREAD_ATTACH:
當進程創建一線程時,系統查看當前映射到進程地址空間中的所有DLL文件映像,

并用值DLL_THREAD_ATTACH調用DLL的DllMain函數。

新創建的線程負責執行這次的DLL的DllMain函數,

只有當所有的DLL都處理完這一通知后,系統才允許線程開始執行它的線程函數。

4.DLL_THREAD_DETACH:
如果線程調用了ExitThread來結束線程(線程函數返回時,系統也會自動調用ExitThread),

系統查看當前映射到進程空間中的所有DLL文件映像,

并用DLL_THREAD_DETACH來調用DllMain函數,

通知所有的DLL去執行線程級的清理工作。
★注意:如果線程的結束是因為系統中的一個線程調用了TerminateThread,

系統就不會用值DLL_THREAD_DETACH來調用所有DLL的DllMain函數。

③lpReserved參數:保留,目前沒什么意義。

TLS系列

TLS回調函數(1)

TLS回調函數(2)

總結

以上是生活随笔為你收集整理的TLS回调函数(1)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。