线程本地存储tls

1.动态tls介绍windows

windows为每一个进程分配一组线程本地存储的标记. 至少有64个标记,若是程序使用超过了64个将动态增加.函数

这些标记有2种状态: free和inuse. 全部标记的状态对该进程中全部线程都是可见的.测试

程序中每一个线程会对应每一个标记一个slot,这个slot是个lpvoid的值加密

程序经过调用DWORD TlsAlloc(void) 来获取一个free状态的索引,并遍历该进程全部线程对其slot赋值为0,而后每一个线程就能够经过spa

这个标记值(该标记的索引dword),来对本身的slot赋值,索引是每一个线程可见的,可是每一个线程本身的slot是只有本身可见的,这个slot就能够线程

理解为线程本地存储.指针

每一个线程能够经过下面的函数对本身的slot赋值:调试

BOOL
WINAPI
TlsSetValue(
_In_ DWORD dwTlsIndex,  //标记的索引值
_In_opt_ LPVOID lpTlsValue  //给slot赋的值
)code

或者获取slot的值:blog

LPVOID    //返回slot的值
WINAPI
TlsGetValue(
_In_ DWORD dwTlsIndex   //标记的索引值
)

因此通常用slot来存储指针

 

前面说过,标记对全部线程可见,若是有任何线程经过下面函数来释放标记,那么全部线程的对应于该标记的slot值将不可用

BOOL
WINAPI
TlsFree(
_In_ DWORD dwTlsIndex //标记的索引值
)

2.测试代码

DWORD threadProc(LPVOID param)
{
    DWORD index = *(DWORD*)param;
    printf("tls1 [%d] value is %s\n", index, (char*)TlsGetValue(index));
    TlsSetValue(index, "thread");
    printf("tls2 [%d] value is %s\n", index, (char*)TlsGetValue(index));
    return 0;
}
int main()
{
    DWORD index;
    index=TlsAlloc();
    TlsSetValue(index, "tls");
    printf("main1 tls value is %s\n", (char*)TlsGetValue(index));
    CreateThread(0, 0, (LPTHREAD_START_ROUTINE)threadProc, &index, 0, 0);
    Sleep(100);
    printf("main2 tls value is %s\n", (char*)TlsGetValue(index));
    DWORD ret = TlsFree(index);
    system("pause");
    return 0;
}

执行结果:

main1 tls value is tls
tls1 [1] value is (null)
tls2 [1] value is thread
main2 tls value is tls
请按任意键继续. . .

 

3.tls回调函数及静态TLS

 

tls回调函数会在线程建立和销毁时执行,所以能够用于反调试和软件加密解密

使用连接指令:#pragma comment(linker,"/include:__tls_used") 开启tls节

经过#pragma data_seg(".CRT$XLX")将回调函数填入tls节区中

#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK callbacks[] = { callback1 ,callback2 ,0 };
#pragma data_seg()

回调函数原型:

void NTAPI callback1(PVOID hmodule, DWORD reason, PVOID reserved);

相关文章
相关标签/搜索