While Ofek Shilon is right that there is no ingredient in the code, his answer contains only part of the whole ingredient. A complete working solution can be found here , which in turn is taken from here .
For an explanation of how this works, you can refer to this blog (suppose we are working with a VC ++ compiler).
For convenience, the code is located below (note that the x86 and x64 platforms are supported):
#include <windows.h> // Explained in p. 2 below void NTAPI tls_callback(PVOID DllHandle, DWORD dwReason, PVOID) { if (dwReason == DLL_THREAD_ATTACH) { MessageBox(0, L"DLL_THREAD_ATTACH", L"DLL_THREAD_ATTACH", 0); } if (dwReason == DLL_PROCESS_ATTACH) { MessageBox(0, L"DLL_PROCESS_ATTACH", L"DLL_PROCESS_ATTACH", 0); } } #ifdef _WIN64 #pragma comment (linker, "/INCLUDE:_tls_used") // See p. 1 below #pragma comment (linker, "/INCLUDE:tls_callback_func") // See p. 3 below #else #pragma comment (linker, "/INCLUDE:__tls_used") // See p. 1 below #pragma comment (linker, "/INCLUDE:_tls_callback_func") // See p. 3 below #endif // Explained in p. 3 below #ifdef _WIN64 #pragma const_seg(".CRT$XLF") EXTERN_C const #else #pragma data_seg(".CRT$XLF") EXTERN_C #endif PIMAGE_TLS_CALLBACK tls_callback_func = tls_callback; #ifdef _WIN64 #pragma const_seg() #else #pragma data_seg() #endif //_WIN64 DWORD WINAPI ThreadProc(CONST LPVOID lpParam) { ExitThread(0); } int main(void) { MessageBox(0, L"hello from main", L"main", 0); CreateThread(NULL, 0, &ThreadProc, 0, 0, NULL); return 0; }
EDIT:
Some explanation is definitely required, so let's see what happens in the code.
If we want to use TLS callbacks, we must tell the compiler about this explicitly. This is done by including the variable _tls_used , which has a pointer to a callback array (with zero completion). For the type of this variable, you can refer to tlssup.c in the CRT source code along with Visual Studio:
- For VS 12.0, by default it is here:
c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\ - For VS 14.0, by default it is here:
c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\
It is defined as follows:
#ifdef _WIN64 _CRTALLOC(".rdata$T") const IMAGE_TLS_DIRECTORY64 _tls_used = { (ULONGLONG) &_tls_start,
This code initializes the values ββfor the IMAGE_TLS_DIRECTORY(64) structure pointed to by the entry in the TLS directory. A pointer to the return array is one of its fields. This array is moved by the OS loader, and the called functions are called until a null pointer is executed.
For information about directories in a PE file, refer to this link from MSDN and look for the description IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] .
x86 note: as you can see, the same name _tls_used is found in tlssup.c for the x86 and x64 platforms, but an additional _ is added when this name is included for the x86 assembly. This is not a typo, but a linker function, so __tls_used is efficiently assigned.
- Now we are going to create our callback. Its type can be obtained from the definition
IMAGE_TLS_DIRECTORY(64) , which can be found in winnt.h , there is a field
for x64:
ULONGLONG AddressOfCallBacks;
and for x86:
DWORD AddressOfCallBacks;
The type of callbacks is defined as follows (also from winnt.h ):
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) (PVOID DllHandle, DWORD Reason, PVOID Reserved);
This is the same as for DllMain , and it can handle the same set of events: process \ thread attach \ detach.
- It's time to register your callback. First of all, take a look at the code from
tlssup.c :
The sections divided in it:
_CRTALLOC(".CRT$XLA") PIMAGE_TLS_CALLBACK __xl_a = 0; _CRTALLOC(".CRT$XLZ") PIMAGE_TLS_CALLBACK __xl_z = 0;
It is very important to know what is special about $ when assigning a PE section, so a quote from an article titled "Compiler and Linker Support for Implicit TLS" :
Untitled data in a PE image is placed in one or more sections, which are memory areas with a common set of attributes (such as page protection). The __declspec(allocate("section-name")) (CL-specific) keyword tells the compiler that a particular variable should be placed in a specific section in the final executable. The compiler additionally supports the concatenation of so-called partitions into one large section. This support is activated by prefixing the section name with the $ symbol, followed by any other text. the compiler combines the resulting section with the section of the same name, truncated by the $ symbol (inclusive).
The compiler alphabetically arranges the individual sections when they are concatenated (due to the use of the $ character in the name section). This means that in memory (in the final executable image) the variable in the ".CRT$XLB" section will be after the variable in ".CRT$XLA" , but before the variable in the ".CRT$XLZ" . Since runtime uses this compiler quirk to create a null-value array of completed function pointers to TLS callbacks (with the pointer stored in the ".CRT$XLZ" , which is the null terminator). Thus, in order to ensure that the declared function pointer is within bounds of the TLS callback array referenced by _tls_used , it needs a place in the ".CRT$XLx" form section.
Perhaps, in fact, there are 2+ callbacks (we will use only one), and we can call them in order, now we know how to do it. Just place these callbacks in sections named alphabetically.
EXTERN_C added to prohibit the use of the C ++ style name and use the C-style.
const and const_seg are used for the x64 version of the code, because otherwise it does not work, I donβt know the exact reason for this, it is possible that the CRT section permissions are different for x86 and x64.
And finally, we must include the name of the callback function for the linker to know that it should be added to the TLS callback array. Please note that additional _ for x64 build see at the end of paragraph 1 above.