应该说楼主言简意赅,一语说出了关键所在:
系统对PE文件的Loader在检测是否有TLS Callback存在并调用函数时,读取记录TLS的IMAGE_DATA_DIRECTORY结构,从其VirtualAddress成员中得到IMAGE_TLS_DIRECTORY结构的偏移(加上基址变成指针),但并未检测其Size成员。因此即使此处Size成员置0,依然可以读取并调用TLS Callback函数。
但IDA、DumpBin等工具在检测TLS Callback时,则检测了相应IMAGE_DATA_DIRECTORY结构的Size成员。
因此,当编写者有意将相应IMAGE_DATA_DIRECTORY结构的Size成员置0时,IDA和DumpBin等工具将认为这个TLS Callback无效,然而PE的Loader却不检测Size成员而依然认为其有效并正常执行TLS Callback,从而导致IDA等对此TLS Callback的误判。
接下来我验证一下楼主所提到的内容。
PE的Loader对TLS Callback的读取和调用在ntdll!LdrpCallTlsInitializers
可以看到,在调用RtlImageDirectoryEntryToData得到TLS对应的IMAGE_TLS_DIRECTORY结构的指针后,并没有检查相应IMAGE_DATA_DIRECTORY结构的Size成员(其实RtlImageDirectoryEntryToData除了返回结构指针外,IMAGE_DATA_DIRECTORY结构的Size成员同样写入最后一个参数提供的地址中返回给调用者了,但是这里没有检查这个值)就直接查找IMAGE_TLS_DIRECTORY结构中的函数指针并循环调用了。