diff --git a/ReadMe.md b/ReadMe.md index a7b0d2f..da1666c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -57,7 +57,8 @@ YY-Thunks(鸭船),存在的目的就是抹平不同系统的差异,编 2. 【链接器】-【输入】-【附加依赖项】,添加 `objs\$(PlatformShortName)\YY_Thunks_for_WinXP.obj`。 3. 【链接器】-【系统】-【所需的最低版本】,根据实际情况填写 `5.01`(WinXP 32位) 或者 `5.02`(WinXP x64、2003)。 -4. 重新编译代码。 +4. 如果是编译DLL,那么【链接器】-【高级】-【自定义入口点】更改为`_DllMainCRTStartupForYY_Thunks`(如果不更改那么XP下使用TLS时可能崩溃!) +5. 重新编译代码。 > 温馨提示:如果需要兼容 Vista,【所需的最低版本】无需修改,但是【附加依赖项】请选择 `objs\$(PlatformShortName)\YY_Thunks_for_Vista.obj`。 diff --git a/src/Thunks/DllMainCRTStartup.hpp b/src/Thunks/DllMainCRTStartup.hpp new file mode 100644 index 0000000..0c93a5d --- /dev/null +++ b/src/Thunks/DllMainCRTStartup.hpp @@ -0,0 +1,292 @@ +#if defined(YY_Thunks_Implemented) && YY_Thunks_Support_Version < NTDDI_WIN6 + +namespace YY::Thunks::internal +{ + extern "C" BOOL WINAPI _DllMainCRTStartup( + HINSTANCE const instance, + DWORD const reason, + LPVOID const reserved + ); + + extern "C" ULONG _tls_index; + static ULONG _tls_index_old; +#ifdef _WIN64 + extern "C" const IMAGE_TLS_DIRECTORY64 _tls_used; +#else + extern "C" const IMAGE_TLS_DIRECTORY _tls_used; +#endif + + struct TlsRawItem + { + TlsRawItem* pNext; + TlsRawItem* pPrev; + BYTE* pBase; + }; + static thread_local TlsRawItem s_CurrentNode; + + static volatile LONG uStatus = 0; + static TlsRawItem* volatile pRoot = nullptr; + + static ULONG __fastcall GetTlsIndexBufferCount(TEB* _pTeb) + { + auto _ppThreadLocalStoragePointer = (void**)_pTeb->ThreadLocalStoragePointer; + if (!_ppThreadLocalStoragePointer) + return 0; + + return HeapSize(_pTeb->ProcessEnvironmentBlock->ProcessHeap, 0, _ppThreadLocalStoragePointer) / sizeof(void*); + } + static PVOID NTAPI RtlImageDirectoryEntryToData( + __in PVOID pBaseAddress, + __in ULONG dwDirectory, + __out PULONG pSize + ) + { + auto _pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress; + auto _pNtHerder = reinterpret_cast(PBYTE(pBaseAddress) + _pDosHeader->e_lfanew); + auto& _DataDirectory = _pNtHerder->OptionalHeader.DataDirectory[dwDirectory]; + + *pSize = _DataDirectory.Size; + if (_DataDirectory.Size == 0 || _DataDirectory.VirtualAddress == 0) + return nullptr; + + return PBYTE(pBaseAddress) + _DataDirectory.VirtualAddress; + } + + static ULONG __fastcall GetMaxTlsIndex() noexcept + { + ULONG uMaxTlsIndex = 0; + + auto _pLdr = (_PEB_LDR_DATA_XP*)(TEB*)NtCurrentTeb()->ProcessEnvironmentBlock->Ldr; + auto _pHerder = &_pLdr->InLoadOrderModuleList; + for (auto _pItem = _pHerder->Flink; _pItem != _pHerder; ) + { + auto _pEntry = CONTAINING_RECORD(_pItem, _LDR_DATA_TABLE_ENTRY_XP, InLoadOrderModuleList); + _pItem = _pItem->Flink; + + /*if (!_pEntry->TlsIndex) + continue;*/ + + ULONG TlsSize; + auto pTlsImage = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData( + _pEntry->BaseAddress, + IMAGE_DIRECTORY_ENTRY_TLS, + &TlsSize); + + if (pTlsImage == nullptr || pTlsImage->AddressOfIndex == 0) + { + continue; + } + + + __try + { + + const auto _TlsIndex = *(ULONG*)pTlsImage->AddressOfIndex; + if (uMaxTlsIndex < _TlsIndex) + uMaxTlsIndex = _TlsIndex; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + } + + return uMaxTlsIndex; + + } + + static bool __fastcall AllocTlsData() noexcept + { + if (_tls_index == 0) + return false; + + auto _pTeb= (TEB*)NtCurrentTeb(); + auto _pTlsIndex = (void**)_pTeb->ThreadLocalStoragePointer; + auto _cTlsIndexLength = GetTlsIndexBufferCount(_pTeb); + if (_cTlsIndexLength <= _tls_index) + { + // Index不足,扩充…… + auto _cNewTlsIndexLength = _tls_index + 128; + auto _pNewTlsIndex = (void**)Alloc(_cNewTlsIndexLength * sizeof(void*), HEAP_ZERO_MEMORY); + if (!_pNewTlsIndex) + return false; + + memcpy(_pNewTlsIndex, _pTlsIndex, _cTlsIndexLength * sizeof(void*)); + if ((void*)InterlockedCompareExchange((uintptr_t*)&_pTeb->ThreadLocalStoragePointer, (uintptr_t)_pNewTlsIndex, (uintptr_t)_pTlsIndex) != _pTlsIndex) + { + Free(_pNewTlsIndex); + return false; + } + Free(_pTlsIndex); + _pTlsIndex = _pNewTlsIndex; + } + const size_t _cbTlsRaw = _tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData; + auto _pRawTlsData = (BYTE*)Alloc(_cbTlsRaw, HEAP_ZERO_MEMORY); + if (!_pRawTlsData) + return false; + + memcpy(_pRawTlsData, (void*)_tls_used.StartAddressOfRawData, _cbTlsRaw); + InterlockedExchange((uintptr_t*)&_pTlsIndex[_tls_index], (uintptr_t)_pRawTlsData); + + s_CurrentNode.pBase = _pRawTlsData; + for (;;) + { + if (!_interlockedbittestandset(&uStatus, 0)) + { + s_CurrentNode.pNext = pRoot; + pRoot = &s_CurrentNode; + _interlockedbittestandreset(&uStatus, 0); + break; + } + } + return true; + } + + static void __fastcall FreeTlsData() + { + if (_tls_index == 0) + return; + auto _pTeb = (TEB*)NtCurrentTeb(); + if (_tls_index >= GetTlsIndexBufferCount(_pTeb)) + return; + + auto _ppTlsIndex = (void**)_pTeb->ThreadLocalStoragePointer; + if (_ppTlsIndex[_tls_index] == nullptr) + return; + + if (s_CurrentNode.pBase != _pTeb->ThreadLocalStoragePointer) + return; + + s_CurrentNode.pBase = nullptr; + + for (;;) + { + if (!_interlockedbittestandset(&uStatus, 0)) + { + if (&s_CurrentNode == pRoot) + { + s_CurrentNode.pNext->pPrev = nullptr; + pRoot = s_CurrentNode.pNext; + } + else + { + auto pPrev = s_CurrentNode.pPrev; + auto pNext = s_CurrentNode.pNext; + + if (pPrev) + pPrev->pNext = pNext; + + if (pNext) + pNext->pPrev = pPrev; + } + _interlockedbittestandreset(&uStatus, 0); + + auto _pTlsRawData = (void*)InterlockedExchange((uintptr_t*)&_ppTlsIndex[_tls_index], 0); + Free(_pTlsRawData); + break; + } + } + } + + static ULONG __fastcall CreateTlsIndex() noexcept + { + return GetMaxTlsIndex() + 1; + } + + static void __fastcall FreeTlsIndex() noexcept + { + if (_tls_index == 0) + return; + + // 故意不加锁…… + for (auto _pItem = pRoot; _pItem;) + { + auto _pNext = _pItem->pNext; + Free(_pItem->pBase); + _pItem = _pNext; + } + } + + static void __fastcall CallTlsCallback( + HINSTANCE const _hInstance, + DWORD const _uReason) + { + if (_tls_index == 0) + return; + + auto _pTeb = (TEB*)NtCurrentTeb(); + auto _ppThreadLocalStoragePointer = (void**)_pTeb->ThreadLocalStoragePointer; + if (!_ppThreadLocalStoragePointer) + return; + + if (_tls_index >= GetTlsIndexBufferCount(_pTeb)) + return; + + if (!_ppThreadLocalStoragePointer[_tls_index]) + return; + + __try + { + auto _pfnTlsCallbacks = reinterpret_cast(_tls_used.AddressOfCallBacks); + if (_pfnTlsCallbacks) + { + for (; *_pfnTlsCallbacks; ++_pfnTlsCallbacks) + { + (*_pfnTlsCallbacks)(_hInstance, _uReason, nullptr); + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + + } + } + + extern "C" BOOL WINAPI DllMainCRTStartupForYY_Thunks( + HINSTANCE const _hInstance, + DWORD const _uReason, + LPVOID const _pReserved + ) + { + if (internal::GetSystemVersion() < internal::MakeVersion(6, 0)) + { + // Vista开始已经可以动态的处理TLS问题了,所以这里只针对老系统处理。 + switch (_uReason) + { + case DLL_PROCESS_ATTACH: + _tls_index_old = _tls_index; + if (_tls_index == 0) + { + _tls_index = CreateTlsIndex(); + AllocTlsData(); + } + break; + case DLL_THREAD_ATTACH: + if (_tls_index_old == 0) + { + AllocTlsData(); + CallTlsCallback(_hInstance, _uReason); + } + break; + case DLL_THREAD_DETACH: + if (_tls_index_old == 0) + { + CallTlsCallback(_hInstance, _uReason); + FreeTlsData(); + } + break; + case DLL_PROCESS_DETACH: +#if (YY_Thunks_Support_Version < NTDDI_WINXP) + __YY_Thunks_Process_Terminating = _pReserved != nullptr; +#endif + if (_pReserved == nullptr && _tls_index_old == 0) + { + FreeTlsIndex(); + } + break; + } + } + + return _DllMainCRTStartup(_hInstance, _uReason, _pReserved); + } +} +#endif diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 5f2fd8f..071ef63 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj @@ -222,6 +222,7 @@ + diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters index b34511e..3cbda50 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters @@ -308,6 +308,9 @@ Thunks + + Thunks +