您的位置:首页 > 大数据 > 人工智能

[转载]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中

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来使上例解决死锁的原因。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐