[转载]DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响
2015-02-02 21:20
537 查看
(转载于breaksoftware的csdn博客)
《windows核心编程》作者在讨论DllMain执行序列化的时候,曾说过一个他的故事:他试图通过调用DisableThreadLibraryCalls以使得新线程不在调用DllMain从而解决死锁问题。但是该方案最后失败了。思考作者的思路,他可能一开始认为:因为线程要调用DllMain而加锁,于是windows在发现DllMain不用调用时就不用加锁了。本文将探讨DisableThreadLibraryCalls对DllMain死锁的影响。首先我们需要定位是什么函数调用了DllMain。为了方便分析,我设计了以下代码
// 主程序
while ( cin>>n ) {
string strDllName;
DWORD dwSleepTime = 0;
switch(n) {
case 0:{
strDllName = "DllWithDisableThreadLibraryCalls_A";
dwSleepTime = 100000;
}break;
……
case 4:{
strDllName = "DllWithoutDisableThreadLibraryCalls_A";
dwSleepTime = 3000;
}break;
……
default:
break;
}
HMODULE h = LoadLibraryA(strDllName.c_str());
Sleep(dwSleepTime);
if ( NULL != h ) {
FreeLibrary(h);
}
}
该过程将根据输入值n决定加载哪个DLL。当输入0时,主线程将加载DllWithDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,我们将调用DisableThreadLibraryCalls以让其不再收到DLL_THREAD_ATTACH和DLL_THREAD_DETACH。
case DLL_PROCESS_ATTACH:{
printf("DLL DllWithDisableThreadLibraryCalls_A:\tProcess attach (tid = %d)\n", tid);
DisableThreadLibraryCalls(hModule);
HANDLE hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);
CloseHandle(hThread);
}break;
在该例程中,我们要创建一个新的线程。这是为了检测新线程是否会对该DLL有所操作,线程函数很简单。
static DWORD WINAPI ThreadCreateInDllMain(LPVOID) {
return 0;
}
当输入4时,主线程将加载DllWithoutDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,将直接启动一个线程(线程函数同上),而不会调用DisableThreadLibraryCalls。这步是为了让我们找出线程创建时是通过什么流程调用到DllMain函数的。
我们先让我们进程加载DllWithoutDisableThreadLibraryCalls_A.dll(输入4)以找到DllMain加载的关键路径。为了达到这个目的,我将设置几个断点:
Exe中
View Code
我们看到在11或13或25行进入临界区后,在29行调用了LdrpInitializeThread,而在31行退出临界区。这就是说整个LdrpInitializeThread的逻辑都在临界区中执行的,也就是说DisableThreadLibraryCalls将无权干涉是否会进入临界区。这就解释了为什么不能使用DisableThreadLibraryCalls来使上例解决死锁的原因。
《windows核心编程》作者在讨论DllMain执行序列化的时候,曾说过一个他的故事:他试图通过调用DisableThreadLibraryCalls以使得新线程不在调用DllMain从而解决死锁问题。但是该方案最后失败了。思考作者的思路,他可能一开始认为:因为线程要调用DllMain而加锁,于是windows在发现DllMain不用调用时就不用加锁了。本文将探讨DisableThreadLibraryCalls对DllMain死锁的影响。首先我们需要定位是什么函数调用了DllMain。为了方便分析,我设计了以下代码
// 主程序
while ( cin>>n ) {
string strDllName;
DWORD dwSleepTime = 0;
switch(n) {
case 0:{
strDllName = "DllWithDisableThreadLibraryCalls_A";
dwSleepTime = 100000;
}break;
……
case 4:{
strDllName = "DllWithoutDisableThreadLibraryCalls_A";
dwSleepTime = 3000;
}break;
……
default:
break;
}
HMODULE h = LoadLibraryA(strDllName.c_str());
Sleep(dwSleepTime);
if ( NULL != h ) {
FreeLibrary(h);
}
}
该过程将根据输入值n决定加载哪个DLL。当输入0时,主线程将加载DllWithDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,我们将调用DisableThreadLibraryCalls以让其不再收到DLL_THREAD_ATTACH和DLL_THREAD_DETACH。
case DLL_PROCESS_ATTACH:{
printf("DLL DllWithDisableThreadLibraryCalls_A:\tProcess attach (tid = %d)\n", tid);
DisableThreadLibraryCalls(hModule);
HANDLE hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);
CloseHandle(hThread);
}break;
在该例程中,我们要创建一个新的线程。这是为了检测新线程是否会对该DLL有所操作,线程函数很简单。
static DWORD WINAPI ThreadCreateInDllMain(LPVOID) {
return 0;
}
当输入4时,主线程将加载DllWithoutDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,将直接启动一个线程(线程函数同上),而不会调用DisableThreadLibraryCalls。这步是为了让我们找出线程创建时是通过什么流程调用到DllMain函数的。
我们先让我们进程加载DllWithoutDisableThreadLibraryCalls_A.dll(输入4)以找到DllMain加载的关键路径。为了达到这个目的,我将设置几个断点:
Exe中
VOID 2.LdrpInitialize ( 3. IN PCONTEXT Context, 4. IN PVOID SystemArgument1, 5. IN PVOID SystemArgument2 6. ) 7.{ 8. …… 9. Peb->LoaderLock = (PVOID)&LoaderLock; 10. 11. if ( !RtlTryEnterCriticalSection(&LoaderLock) ) { 12. if ( LoaderLockInitialized ) { 13. RtlEnterCriticalSection(&LoaderLock); 14. } 15. else { 16. 17. // 18. // drop into a 30ms delay loop 19. // 20. 21. DelayValue.QuadPart = Int32x32To64( 30, -10000 ); 22. while ( !LoaderLockInitialized ) { 23. NtDelayExecution(FALSE,&DelayValue); 24. } 25. RtlEnterCriticalSection(&LoaderLock); 26. } 27. } 28. …… 29. LdrpInitializeThread(Context); 30. …… 31. RtlLeaveCriticalSection(&LoaderLock); 32. …
View Code
我们看到在11或13或25行进入临界区后,在29行调用了LdrpInitializeThread,而在31行退出临界区。这就是说整个LdrpInitializeThread的逻辑都在临界区中执行的,也就是说DisableThreadLibraryCalls将无权干涉是否会进入临界区。这就解释了为什么不能使用DisableThreadLibraryCalls来使上例解决死锁的原因。
相关文章推荐
- DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响
- DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响
- [转载]DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁
- [转载]DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)
- [转载]DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析
- [转载] DllMain中不当操作导致死锁问题的分析--加载卸载DLL与DllMain死锁的关系
- [转载]DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁
- [转载]DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子
- [转载]DllMain中不当操作导致死锁问题的分析--死锁介绍
- [转载]DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2
- DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁
- [转]DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)
- DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁
- DllMain中不当操作导致死锁问题的分析--死锁介绍
- DllMain中不当操作导致死锁问题的分析--线程退出时产生了死锁
- DllMain中不当操作导致死锁问题的分析--死锁介绍
- DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析
- DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁
- DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2
- DllMain中不当操作导致死锁问题的分析--死锁介绍