什么情况下在 TLS 回调中不能使用任何包含 CRT 函数的调用?
首先我们需要知道 TLS 回调的执行在程序入口点之前,而在静态链接时,CRT 的初始化在入口函数执行时才会进行。所以,在静态链接选项(对于 MSVC 是 /MT 编译选项)启用时,TLS 中如果执行了 CRT 函数,将因为尚未初始化资源,而导致访问非法内存而导致崩溃。
例如,此时调用一个 printf 或者 memcpy 均会导致崩溃。
每个针对 C++ 运行时编译的库在加载时都会调用 _DllMainCRTStartup。_DllMainCRTStartup 调用 _CRT_INIT,后者初始化 C/C++ 运行时库并在静态非局部变量上调用 C++ 构造函数。
PE 格式包含一个可选标头,其中有一个名为“AddressOfEntryPoint”的插槽,该插槽调用一个函数,该函数将调用 _DllMainCRTStartup,从而触发初始化链。_DllMainCRTStartup 完成初始化阶段后,它将调用程序实现的 DllMain() 函数。
但是 PE 中的静态链接 CRT 在调用 PE 的入口点后初始化。但 TLS 回调在入口点之前调用 DLL_PROCESS_ATACH。在这种情况下,静态 CRT 尚未初始化,并且对此 CRT 代码的任何调用都可能崩溃。
解决方案 - 在单独的 DLL 中使用动态链接 CRT - 在这种情况下,它将在 TLS 回调之前初始化。
也就是说此时只能选择默认的 /MD 或者 /MDd 选项(貌似还有一种利用 vcrt 的第三方替代?具体不清楚)。
0:000> k
# Child-SP RetAddr Call Site
00 0000007c`e676eb58 00007ff6`e3b04829 AGTtest!__crtFlsGetValue+0x10 [f:\dd\vctools\crt\crtw32\misc\winapisupp.c @ 422]
01 0000007c`e676eb60 00007ff6`e3b047f3 AGTtest!_getptd_noexit+0x1d [f:\dd\vctools\crt\crtw32\startup\tidtable.c @ 277]
02 0000007c`e676eb90 00007ff6`e3b03737 AGTtest!_getptd+0xb [f:\dd\vctools\crt\crtw32\startup\tidtable.c @ 337]
03 0000007c`e676ebc0 00007ff6`e3b06030 AGTtest!_LocaleUpdate::_LocaleUpdate+0x1b [f:\dd\vctools\crt\crtw32\h\setlocal.h @ 248]
04 0000007c`e676ebf0 00007ff6`e3b02816 AGTtest!_output_s_l+0x6c [f:\dd\vctools\crt\crtw32\stdio\output.c @ 1028]
05 0000007c`e676ef10 00007ff6`e3b028a8 AGTtest!_vsnprintf_helper+0x92 [f:\dd\vctools\crt\crtw32\stdio\vsprintf.c @ 140]
06 0000007c`e676ef80 00007ff6`e3b025a3 AGTtest!_vsprintf_s_l+0x3c [f:\dd\vctools\crt\crtw32\stdio\vsprintf.c @ 237]
07 0000007c`e676efc0 00007ff6`e3b0112f AGTtest!sprintf_s+0x1f [f:\dd\vctools\crt\crtw32\stdio\sprintf.c @ 216]
08 0000007c`e676f000 00007ffb`bd6a52c8 AGTtest!tls_start_protect+0x1f [d:\repos\antidebug\agt\tls_callback.c @ 83]
09 0000007c`e676f040 00007ffb`bd6a1577 ntdll!LdrpCallInitRoutine+0x4c
0a 0000007c`e676f0a0 00007ffb`bd7201cd ntdll!LdrpCallTlsInitializers+0x93
0b 0000007c`e676f120 00007ffb`bd75166d ntdll!LdrpInitializeProcess+0x1c99
0c 0000007c`e676f510 00007ffb`bd706d5e ntdll!_LdrpInitialize+0x4a8b9
0d 0000007c`e676f590 00000000`00000000 ntdll!LdrInitializeThunk+0xe