TLS是Thread-local storage的缩写,它是线程自己私有的数据存储区。TLS回调函数则是一种的特殊函数,它在进程创建、进程退出、线程创建、线程退出时被操作系统调用,听起来有点像DllMain函数。TLS回调函数在进程创建时被调用,会早于进程的Main入口函数,很多软件会利用这一特性进行反调试检查。
实现TLS回调函数代码也很简单,不过有几个值得注意的小细节,见如下代码:
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:_p_thread_callback_base")
void NTAPI OnTLSCallback(PVOID module, DWORD reason, PVOID reserved) {
switch (reason)
{
case DLL_PROCESS_ATTACH:
std::cout << "DLL_PROCESS_ATTACH\n";
break;
case DLL_THREAD_ATTACH:
std::cout << "DLL_THREAD_ATTACH\n";
break;
case DLL_THREAD_DETACH:
std::cout << "DLL_THREAD_DETACH\n";
break;
case DLL_PROCESS_DETACH:
std::cout << "DLL_PROCESS_DETACH\n";
break;
}
}
extern "C" {
#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK p_thread_callback_base = OnTLSCallback;
// Reset the default section.
#pragma data_seg()
}
#pragma comment(linker, "/INCLUDE:__tls_used")
这句的作用时在PE文件里创建一个.tls的目录,如果不存在的话。
#pragma comment(linker, "/INCLUDE:_p_thread_callback_base")
的作用则是强制编译器生成对_p_thread_callback_base的引用,以防止在release模式下编译器whole program optimization优化导致找不到TLS回调函数。这点值得注意,有些人实现了一些TLS回调函数,发现在debug模式下可以正常工作,到了release模式就失效了。
#pragma data_seg(".CRT$XLB")
这部分代码创建一个tls段。".CRT$XLB"的含义是:
- CRT表明是使用C RunTime机制
- $后面的XLB中X表示随机的标识
- L表示是TLS callback section
- B可以被换成B到Y的任意一个字母,但是不能使用
".CRT$XLA"
和".CRT$XLZ"
,因为".CRT$XLA"
和".CRT$XLZ"
是用于tlssup.obj的
如果要定义多个TLS_CallBack函数,可以把下面这句写成:PIMAGE_TLS_CALLBACK p_thread_callback_base [] = {tls_callback_A, tls_callback_B, tls_callback_C,0};
。
我们用到了extern "C"
,这是想在c++代码里也可以方便的使用。
参考: