Windows10的DllMain中CreateThread并WaitforSingleObject造成死锁

本文分两种情况讨论,即在CreateThread之后是否调用WaitForSingleObject.
先看:
static DWORD WINAPI ThreadCreateInDllMain(LPVOID) {
    printf("ThreadCreateInDllMain start");
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    printf("DllMain start.\n");
    HMODULE hMod = NULL;
    HANDLE hThread = NULL;
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        printf("DLL_PROCESS_ATTACH\n");
        hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);
        WaitForSingleObject(hThread, INFINITE);
   
        break;
    case DLL_THREAD_ATTACH:
        printf("DLL_THREAD_ATTACH\n");
        break;
    case DLL_THREAD_DETACH:
        printf("DLL_THREAD_DETACH\n");
        break;
    case DLL_PROCESS_DETACH:
        printf("DLL_PROCESS_DETACH\n");

        break;
    }
    return TRUE;
}
我们看看这个NtWaitforSingleObject在等待什么(其实我们自己写的代码我们肯定知道是在等待一个线程对象):

 

 

查看句柄对应的详细信息:

 

 

等待3号线程:

 

 

我们看3号线程在干啥,3号线程也在等待(3号线程的等待是关键):

 

 

它在等待一个事件:

 

 

IDA看它等的是什么,它在等待LdrpWorkCompleteEvent事件:

 

 

设置这个Event的线程不是在等待,就是已经退出了。所以我们要找哪里能SetEvent(LdrpWorkCompleteEvent),找到是在LdrpProcessWork中:

 

 

我没有具体去追踪调用ZwSetEvent(LdrpWorkCompleteEvent)的调用关系,但是,总之,我们知道DllMain中的线程会负责ZwSetEvent(LdrpWorkCompleteEvent),但是它在等待线程完成,而线程又在等待DllMain的ZwSetEvent(LdrpWorkCompleteEvent),所以会死锁。
 
 
如果代码是这样,就不会死锁:

static DWORD WINAPI ThreadCreateInDllMain(LPVOID) {
    printf("ThreadCreateInDllMain start");
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    printf("DllMain start.\n");
    HMODULE hMod = NULL;
    HANDLE hThread = NULL;
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        printf("DLL_PROCESS_ATTACH\n");
        hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);
   
        break;
    case DLL_THREAD_ATTACH:
        printf("DLL_THREAD_ATTACH\n");
        break;
    case DLL_THREAD_DETACH:
        printf("DLL_THREAD_DETACH\n");
        break;
    case DLL_PROCESS_DETACH:
        printf("DLL_PROCESS_DETACH\n");

        break;
    }
    return TRUE;
}
我单步跟踪了LdrpDrainWorkQueue,即线程的NtWaitForsingleobject的调用者,如果DllMain中没有WaitForSingleObject的流程,LdrpDrainWorkQueue从这里就break掉了:

 

 

如果DllMain中有WaitForsingleobject,那么这里直接跳转:

 

 

跳转到这里等待:

 

 

其实LdrpDrainWorkQueue中会走到NtWaitforSingleObject进行等待的根本原因就在于上边判断了是否有WorkInProcess:

 

 

所以我们可以推断LdrpWorkInProgress表示是否还有DllMain在处理。创建线程的时候,如果还有DllMain没有执行完,那么会等待这个DllMain执行完才创建线程。而此时Thread在等待DllMain完,而DllMain又在等待Thread则必然死锁。

 

 

posted @ 2022-02-04 23:21  _No.47  阅读(743)  评论(1编辑  收藏  举报