【问题标题】:TLS callback in which Calling sprintf_s leads to crash调用 sprintf_s 导致崩溃的 TLS 回调
【发布时间】:2018-05-24 11:56:21
【问题描述】:

这是我的代码:

#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:p_tls_callback1")
#pragma const_seg(push)
#pragma const_seg(".CRT$XLAAA")
EXTERN_C const PIMAGE_TLS_CALLBACK p_tls_callback1 = tls_start_protect;
#pragma const_seg(pop)

下面这段代码直接被tls_start_protect调用。

    char buf[10];
    sprintf_s(buf, 10, "hello\n");

然后它崩溃了。

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

_vscprintf(format, args) 的类似家族也在__crtFlsGetValue 中崩溃。

我不知道现在调用printfs 的家人还为时过早,在此之前,尚未完成初始化。我所知道的是 TLS 回调(仅 DLL_PROCESS_ATTACH)在 'ntdll!Ldr*' 加载所有依赖模块之后和 'EOP' 之前执行。

问题

  • 关于_vscprintf的初始化的任何细节,是由某些CPP构造函数代码中的“CRT”完成的吗???

  • TLS回调的其他限制???

  • 如果我确实需要在 TLS 回调中调用 _vscprintf,该怎么做? (不知何故,我只想在main 之前打印)

【问题讨论】:

    标签: printf portable-executable crt win64


    【解决方案1】:

    您使用 静态 链接的 CRT - 这可以从您的堆栈跟踪中看到。 exe 入口点被调用后,在 exe 中初始化的静态链接 CRT。但是DLL_PROCESS_ATACH 上的 tls 回调称为 before exe 入口点。在这种情况下,您的 static CRT 尚未初始化,对此 CRT 代码的任何调用都可能崩溃。解决方案 - 在单独的 DLL 中使用动态链接 CRT - 在这种情况下,它已经在 TLS 回调之前被初始化

    【讨论】:

    • 所以,我的假设是正确的。我想知道的是printfs 这样的家庭在调用之前在 crt 中设置的步骤。他们只是格式化字符串。
    • @TanakaYasen 这从您的堆栈跟踪中可见。 printf 尝试获取当前区域设置信息 -LocaleUpdate -此数据是每根光纤的。但光纤数据的索引尚未初始化。坠毁在 getptd -> _crtFlsGetValue 。如果你从单独的 dll(比如 msvcrt.dll)调用这个 api - 它已经在 tls 调用时初始化,一切都会好的。但是您仅在调用 exe 入口点后才使用静态 crt 进行初始化(包括 fls 索引)-在 tls 进程附加回调之后
    • 这个答案非常有价值,我投票不够。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多