TLS回調函數


@author: dlive

TLS (Thread Local Storage 線程局部存儲 )回調函數常用於反調試。

TLS回調函數的調用運行要先於EP代碼執行,該特性使它可以作為一種反調試技術使用。

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

0x01 PE TLS Table

若在編程中啟用了TLS功能,PE頭文件中就會設置TLS表(IMAGE_NT_HEARDERS->IMAGE_OPTIONAL_HEADER->IMAGE_DATA_DIRECTORY[9])

可以看到TLS Table的RVA是00009310,找到對應位置如下

TLS Table中比較重要的成員為AddressOfCallbacks,該值指向含有TLS回調函數地址(VA)的數據(一個程序中可以注冊多個TLS回調函數)

0x02 TLS回調函數

TLS回調函數是指,每當創建/終止進程的線程時會自動調用執行的函數(前后共調用兩次)。創建進程的主線程時也會自動調用回調函數,且其調用執行先於EP代碼。

TLS回調函數的聲明:

void NTAPI TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved)

DllMain的聲明:

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)

可以看到兩者聲明非常相似,第一個參數為模塊句柄,即加載地址,第二個參數為調用原因

調用原因有四種

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

TlsTest.cpp

#include <windows.h>

//告知連接器使用TLS
#pragma comment(linker, "/INCLUDE:__tls_used")

void print_console(char* szMsg)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	//先於主線程調用執行的TLS回調函數中使用printf可能會發生Runtime Error,可直接調用WriteConsole API
    WriteConsoleA(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);
}
/*
	注冊TLS函數
	.CRT$XLX的作用
	CRT表示使用C Runtime 機制
	X表示表示名隨機
	L表示TLS Callback section
	X也可以換成B~Y任意一個字符
*/
#pragma data_seg(".CRT$XLX")
	//存儲回調函數地址
    PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
#pragma data_seg()

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    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");

    return 0;
}

主線程調用main前調用TLS回調函數,調用原因為DLL_PROCESS_ATTACH

子線程啟動前調用TLS,原因為DLL_THREAD_ATTACH

子線程結束后調用TLS,原因為DLL_THREAD_DETACH

主線程結束后調用TLS的原因為DLL_PROCESS_DETACH

0x03 調試TLS回調函數

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

調試TLS回調函數時,因為回調函數代碼在EP之前就已經執行了,所以調試選項需要設置暫停於系統斷點(system breakpoint), 設置后調試器會在ntdll.dll模塊內部的“system startup breakpoint‘處暫停

然后在PE中找到回調函數的地址,下斷點調試即可

OD2.0中直接提供暫停在TLS函數的選項


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM