TLS回调函数

来源:互联网 发布:12海里知乎 编辑:程序博客网 时间:2024/06/13 20:19

本文转载自http://www.cnblogs.com/dliv3/p/6489629.html

作者: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函数的选项


原创粉丝点击