XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog

DisableThreadLibraryCalls与DLLMain死锁

 

1、首先写个简单的DLL,用来验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            printf("DLL_PROCESS_ATTACH\n");
        }
        break;
 
    case DLL_PROCESS_DETACH:
        {
            printf("DLL_PROCESS_DETACH\n");
        }
        break;
 
    case DLL_THREAD_ATTACH:
        {
            printf("DLL_THREAD_ATTACH\n");
        }
        break;
    case DLL_THREAD_DETACH:
        {
            printf("DLL_THREAD_DETACH\n");
        }
        break;
    }
    return TRUE;
}

 2、再写一个测试用的EXE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
unsigned int __stdcall ThreadFun(PVOID pM);
 
//int WinMain(HINSTANCE hInstance,
//          HINSTANCE hPrevInstance,
//          LPSTR lpCmdLine,
//          int nCmdShow)
void main()
{
    HMODULE hMod = LoadLibrary("TestDLL.dll");
 
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
    if(!hThread)
    {
        return;
    }
 
    WaitForSingleObject(hThread, INFINITE);
 
    FreeLibrary(hMod);
    getchar();
    exit(0);
//  return 0;
}
 
unsigned int __stdcall ThreadFun(PVOID pM)
{
    printf("线程ID号为%4d的子线程说:Hello World\n", GetCurrentThreadId());
    return 0;
}

3、你把这个EXE跑一遍就会发现,在线程被创建的时候,也会执行一次已经被加载的DLL的DLLMain,加载原因是DLL_THREAD_ATTACH,离开时也同理,最终显示结果如下:

 

 

4、如果你在原先的DLL代码中加入DisableThreadLibraryCalls调用,那么就不会在线程启动时去执行这个DLL的DLLMain了

 

 

 

 当然这里关于线程是否执行DLLMain的DLL_THREAD_ATTACH和DLL_THREAD_DETACH的,还有一些比较细节的逻辑,可以参考:

https://blog.csdn.net/breaksoftware/article/details/8142339

5、在DLL中会产生死锁的情况及原因

5.1、DLLMain中直接或者间接调用LoadLibrary:

起初我以为,假如我们在A.dll中的DllMain收到DLL_PROCESS_ATTACH时,加载了B.dll;而B.dll中的DllMain在收到DLL_PROCESS_ATTACH时又去加载A.dll,则产生了循环依赖,然而经试验检验,并不会,因为如果A.dll已经加载进EXE的内存了,B.dll就不会再去加载A.dll了,实验如下:

 

 

 结果如下:

 

 并没有发生死锁。第一这里没有发生调用循环;第二这里也没有发生死锁。

首先,为什么没有发生我们预计的循环调用呢?我们来一步步分析两个DLL相互加载的过程。

 看调用堆栈发现,TestDLL2又调用了LdrLoadDll,看这架势是要递归加载的:

 

 然而当我跟进去调试的时候,有某一个地方触发了返回,并没有调用LdrpRunInitializeRoutines,这个地方就是LdrpFindOrMapDll。但凡你要加载一个DLL,LdrpFindOrMapDll就会去查找是否已经加载过这个DLL了。

 

 如果没有加载过某个DLL那么LdrpFindOrMapDll的最后一个参数会被写入1,而如果加载过了这个参数就会被写入0:

 DLL的查找方式就是计算Hash并保存在一张全局的Hash表中:

其次,这里为什么没有发生死锁?原因很简单,我调试这里时:

 

 发现TestDLL在再次调用LoadLibrary前后,LdrpLoaderLock的变化如下(命令是dt _RTL_CRITICAL_SECTION 770520c0):

 

 

 

 也就是说这是同一个线程对LdrpLoaderLock上了锁,所以并没有死锁。那么再来验证下如果是另一个线程里,调用LoadLibrary是否会发生死锁呢?

我们把实验代码稍微改动下,让TestDLL在DLLMain中起一个线程://后发现,这里论证可能有误,因为即便是单独起一个线程,也会导致死锁,所以就证明不了这是LoadLibrary造成的了

 

 exe中为了保证TestDLL不被Free掉,我就一直Sleep了:

 

 如愿,运行起来就在某处卡死掉了。我们查看所有的临界区:

LockCount=1表示有一个线程正在等待这个临界区。然后我们查看这个临界区的详细信息:

 

 0号线程正在等待1号线程拥有的临界区:

 

或者我们直接用!locks命令查看:

我们验证下0号线程:

 

再验证下1号线程:

 

 

这里总结下,DLLMain中并不是因为LoadLibrary中的循环调用造成的死锁,而是由于其它原因。什么原因?一个国外网站上给出了解释:

Your DllMain function runs inside the loader lock,one of the few times the OS lets you run code while one of its internal locks is held. This means that you must be extra careful not to violate a lock hierarchy in your DllMain; otherwise, you are asking for a deadlock.

The loader lock is taken by any function that needs to access the list of DLLs loaded into the process.loader lock This includes functions like GetModuleHandle and GetModuleFileName. If your DllMain enters a critical section or waits on a synchronization object, and that critical section or synchronization object is owned by some code that is in turn waiting for the loader lock, you just created a deadlock.

 

所以我们的结论是:

无法通过使用DisableThreadLibraryCalls解决死锁问题。因为,DisableThreadLibraryCalls只是用来防止EXE中起线程时重新执行DLLMain,但是你在主线程第一次Load TestDLL.dll时就已经出现死锁了。


 

 

DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响

 

(转载于breaksoftware的csdn博客)

《windows核心编程》作者在讨论DllMain执行序列化的时候,曾说过一个他的故事:他试图通过调用DisableThreadLibraryCalls以使得新线程不在调用DllMain从而解决死锁问题。但是该方案最后失败了。思考作者的思路,他可能一开始认为:因为线程要调用DllMain而加锁,于是windows在发现DllMain不用调用时就不用加锁了。本文将探讨DisableThreadLibraryCalls对DllMain死锁的影响。首先我们需要定位是什么函数调用了DllMain。为了方便分析,我设计了以下代码

  1. // 主程序  
  2. while ( cin>>n ) {  
  3.         string strDllName;  
  4.         DWORD dwSleepTime = 0;  
  5.         switch(n) {  
  6.         case 0:{  
  7.             strDllName = "DllWithDisableThreadLibraryCalls_A";  
  8.             dwSleepTime = 100000;  
  9.                }break;  
  10. ……  
  11.         case 4:{  
  12.             strDllName = "DllWithoutDisableThreadLibraryCalls_A";  
  13.             dwSleepTime = 3000;  
  14.                }break;  
  15. ……  
  16.         default:  
  17.             break;  
  18.         }  
  19.         HMODULE h = LoadLibraryA(strDllName.c_str());  
  20.   
  21.         Sleep(dwSleepTime);  
  22.   
  23.         if ( NULL != h ) {  
  24.             FreeLibrary(h);  
  25.         }  
  26.     }  
        该过程将根据输入值n决定加载哪个DLL。当输入0时,主线程将加载DllWithDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,我们将调用DisableThreadLibraryCalls以让其不再收到DLL_THREAD_ATTACH和DLL_THREAD_DETACH。
  1. case DLL_PROCESS_ATTACH:{  
  2.             printf("DLL DllWithDisableThreadLibraryCalls_A:\tProcess attach (tid = %d)\n", tid);  
  3.             DisableThreadLibraryCalls(hModule);  
  4.             HANDLE hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);  
  5.             CloseHandle(hThread);  
  6.         }break;  
        在该例程中,我们要创建一个新的线程。这是为了检测新线程是否会对该DLL有所操作,线程函数很简单。
  1. static DWORD WINAPI ThreadCreateInDllMain(LPVOID) {  
  2.     return 0;  
  3. }  
        当输入4时,主线程将加载DllWithoutDisableThreadLibraryCalls_A.dll。它的DllMain收到DLL_PROCESS_ATTACH时,将直接启动一个线程(线程函数同上),而不会调用DisableThreadLibraryCalls。这步是为了让我们找出线程创建时是通过什么流程调用到DllMain函数的。

        我们先让我们进程加载DllWithoutDisableThreadLibraryCalls_A.dll(输入4)以找到DllMain加载的关键路径。为了达到这个目的,我将设置几个断点:

        Exe中

       DLL中



        在LoadLibrary之后Sleep了一下,是为了让工作线程有机会执行起来。

        在执行到Sleep之后,程序中断在DLL中。我们看调用堆栈


        我将关注下从ntdll进入DllWithoutDisableThreadLibraryCalls_A.dll的逻辑调用。双击_LdrpCallInitRoutine这行查看其汇编

[plain] view plain copy
  1. _LdrpCallInitRoutine@16:  
  2. 7C921176 55               push        ebp    
  3. 7C921177 8B EC            mov         ebp,esp   
  4. 7C921179 56               push        esi    
  5. 7C92117A 57               push        edi    
  6. 7C92117B 53               push        ebx    
  7. 7C92117C 8B F4            mov         esi,esp   
  8. 7C92117E FF 75 14         push        dword ptr [ebp+14h]   
  9. 7C921181 FF 75 10         push        dword ptr [ebp+10h]   
  10. 7C921184 FF 75 0C         push        dword ptr [ebp+0Ch]   
  11. 7C921187 FF 55 08         call        dword ptr [ebp+8]   
  12. 7C92118A 8B E6            mov         esp,esi   
  13. 7C92118C 5B               pop         ebx    
  14. 7C92118D 5F               pop         edi    
  15. 7C92118E 5E               pop         esi    
  16. 7C92118F 5D               pop         ebp    
  17. 7C921190 C2 10 00         ret         10h    
        在7C921187这行,我们看到它调用了一个以参数形式传入的函数地址。_LdrpCallInitRoutine的逻辑很简单,只是简单的调用,对我们帮助不大。我们查看_LdrpInitializeThread这个函数。
[plain] view plain copy
  1. _LdrpInitializeThread@4:  
  2. 7C9399A2 6A 40            push        40h    
  3. 7C9399A4 68 D8 99 93 7C   push        7C9399D8h   
  4. 7C9399A9 E8 1D 4F FF FF   call        __SEH_prolog (7C92E8CBh)   
  5. 7C9399AE 64 A1 18 00 00 00 mov         eax,dword ptr fs:[00000018h]   
  6. 7C9399B4 8B 58 30         mov         ebx,dword ptr [eax+30h]   
  7. 7C9399B7 89 5D DC         mov         dword ptr [ebp-24h],ebx   
  8. 7C9399BA 80 3D C4 E0 99 7C 00 cmp         byte ptr [_LdrpShutdownInProgress (7C99E0C4h)],0   
  9. 7C9399C1 0F 85 C3 00 00 00 jne         _LdrpInitializeThread@4+136h (7C939A8Ah)   
  10. 7C9399C7 E8 59 FF FF FF   call        _LdrpAllocateTls@0 (7C939925h)   
  11. 7C9399CC 8B 43 0C         mov         eax,dword ptr [ebx+0Ch]   
  12. 7C9399CF 8B 70 14         mov         esi,dword ptr [eax+14h]   
  13. 7C9399D2 EB 1C            jmp         _LdrpInitializeThread@4+30h (7C9399F0h)   
  14. 7C9399D4 90               nop                
  15. 7C9399D5 90               nop                
  16. 7C9399D6 90               nop                
  17. 7C9399D7 90               nop                
  18. 7C9399D8 ??               db          ffh    
  19. 7C9399D9 ??               db          ffh    
  20. 7C9399DA ??               db          ffh    
  21. 7C9399DB FF 00            inc         dword ptr [eax]   
  22. 7C9399DD 00 00            add         byte ptr [eax],al   
  23. 7C9399DF 00 B5 F3 95 7C FF add         byte ptr [ebp-836A0Dh],dh   
  24. 7C9399E5 ??               db          ffh    
  25. 7C9399E6 ??               db          ffh    
  26. 7C9399E7 FF 00            inc         dword ptr [eax]   
  27. 7C9399E9 00 00            add         byte ptr [eax],al   
  28. 7C9399EB 00 62 5C         add         byte ptr [edx+5Ch],ah   
  29. 7C9399EE 95               xchg        eax,ebp   
  30. 7C9399EF 7C 89            jl          __LdrpInitialize@12+259h (7C93997Ah)   
  31. 7C9399F1 75 E4            jne         7C9399D7   
  32. 7C9399F3 8B 4B 0C         mov         ecx,dword ptr [ebx+0Ch]   
  33. 7C9399F6 6A 14            push        14h    
  34. 7C9399F8 58               pop         eax    
  35. 7C9399F9 03 C8            add         ecx,eax   
  36. 7C9399FB 3B F1            cmp         esi,ecx   
  37. 7C9399FD 74 7E            je          _LdrpInitializeThread@4+0E0h (7C939A7Dh)   
  38. 7C9399FF 8B 4B 08         mov         ecx,dword ptr [ebx+8]   
  39. 7C939A02 3B 4E 10         cmp         ecx,dword ptr [esi+10h]   
  40. 7C939A05 74 6F            je          _LdrpInitializeThread@4+0C9h (7C939A76h)   
  41. 7C939A07 8B 56 2C         mov         edx,dword ptr [esi+2Ch]   
  42. 7C939A0A F7 C2 00 00 04 00 test        edx,40000h   
  43. 7C939A10 75 64            jne         _LdrpInitializeThread@4+0C9h (7C939A76h)   
  44. 7C939A12 8B 4E 14         mov         ecx,dword ptr [esi+14h]   
  45. 7C939A15 89 4D E0         mov         dword ptr [ebp-20h],ecx   
  46. 7C939A18 85 C9            test        ecx,ecx   
  47. 7C939A1A 74 5A            je          _LdrpInitializeThread@4+0C9h (7C939A76h)   
  48. 7C939A1C F7 C2 00 00 08 00 test        edx,80000h   
  49. 7C939A22 74 52            je          _LdrpInitializeThread@4+0C9h (7C939A76h)   
  50. 7C939A24 F6 C2 04         test        dl,4   
  51. 7C939A27 74 4D            je          _LdrpInitializeThread@4+0C9h (7C939A76h)   
  52. 7C939A29 89 45 B0         mov         dword ptr [ebp-50h],eax   
  53. 7C939A2C C7 45 B4 01 00 00 00 mov         dword ptr [ebp-4Ch],1   
  54. 7C939A33 33 C0            xor         eax,eax   
  55. 7C939A35 8D 7D B8         lea         edi,[ebp-48h]   
  56. 7C939A38 AB               stos        dword ptr es:[edi]   
  57. 7C939A39 AB               stos        dword ptr es:[edi]   
  58. 7C939A3A AB               stos        dword ptr es:[edi]   
  59. 7C939A3B FF 76 40         push        dword ptr [esi+40h]   
  60. 7C939A3E 8D 45 B0         lea         eax,[ebp-50h]   
  61. 7C939A41 50               push        eax    
  62. 7C939A42 E8 51 77 FE FF   call        _RtlActivateActivationContextUnsafeFast@8 (7C921198h)   
  63. 7C939A47 33 FF            xor         edi,edi   
  64. 7C939A49 89 7D FC         mov         dword ptr [ebp-4],edi   
  65. 7C939A4C 66 39 7E 32      cmp         word ptr [esi+32h],di   
  66. 7C939A50 0F 85 93 0C 01 00 jne         _LdrpInitializeThread@4+96h (7C94A6E9h)   
  67. 7C939A56 80 3D C4 E0 99 7C 00 cmp         byte ptr [_LdrpShutdownInProgress (7C99E0C4h)],0   
  68. 7C939A5D 75 0E            jne         _LdrpInitializeThread@4+0C0h (7C939A6Dh)   
  69. 7C939A5F 57               push        edi    
  70. 7C939A60 6A 02            push        2      
  71. 7C939A62 FF 76 10         push        dword ptr [esi+10h]   
  72. 7C939A65 FF 75 E0         push        dword ptr [ebp-20h]   
  73. 7C939A68 E8 09 77 FE FF   call        _LdrpCallInitRoutine@16 (7C921176h)   
  74. 7C939A6D 83 4D FC FF      or          dword ptr [ebp-4],0FFFFFFFFh   
  75. 7C939A71 E8 1D FF FF FF   call        _LdrpInitializeThread@4+0D6h (7C939993h)   
  76. 7C939A76 8B 36            mov         esi,dword ptr [esi]   
  77. 7C939A78 E9 73 FF FF FF   jmp         _LdrpInitializeThread@4+30h (7C9399F0h)   
  78. 7C939A7D 80 3D DC E0 99 7C 00 cmp         byte ptr [_LdrpImageHasTls (7C99E0DCh)],0   
  79. 7C939A84 0F 85 7D C1 01 00 jne         _LdrpInitializeThread@4+0E9h (7C955C07h)   
  80. 7C939A8A E8 77 4E FF FF   call        __SEH_epilog (7C92E906h)   
  81. 7C939A8F C2 04 00         ret         4      
        7C939A68处是我们调用LdrpCallInitRoutine的地方。从_LdrpInitializeThread这个函数名看,它应该是执行一些线程初始化操作,由《 DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析》中我们得知,线程在初始化期间将调用加载的DLL的DllMain。于是我们重点将关注于该函数。我们在所有条件跳转地方设断点。然后让我们程序恢复运行,发现程序分别在7C939A78,7C9399FD,7C939A84中断一次后便进入线程函数中,先不管它。现在我们输入0,我们让我们程序加载DllWithDisableThreadLibraryCalls_A.dll。我们查看调用流程

        由以上流程可以发现,标红色的7C939A10前一句和7C9399FD前一句是重要的跳转条件判断。它们分别是

[plain] view plain copy
  1. 7C939A0A F7 C2 00 00 04 00 test        edx,40000h   
[plain] view plain copy
  1. 7C9399F6 6A 14            push        14h    
  2. 7C9399F8 58               pop         eax    
  3. 7C9399F9 03 C8            add         ecx,eax   
  4. 7C9399FB 3B F1            cmp         esi,ecx   
        而且可以分析出7C939A0A可能是因为没有调用DllMain的主因。即可能是DisableThreadLibraryCalls设置了某结构体的某字段Or 40000h了。以下为了简洁,我不再引入汇编,而使用网上盛传的Win2K中的相关C代码加以说明。

        Kernel32中的DisableThreadLibraryCalls底层调用了ntdll中的LdrDisableThreadCalloutsForDll函数。我们看下LdrDisableThreadCalloutsForDll代码

 

  1. NTSTATUS  
  2. LdrDisableThreadCalloutsForDll (  
  3.     IN PVOID DllHandle  
  4.     )  
  5. /*++ 
  6. Routine Description: 
  7.     This function disables thread attach and detach notification 
  8.     for the specified DLL. 
  9. Arguments: 
  10.     DllHandle - Supplies a handle to the DLL to disable. 
  11. Return Value: 
  12.     TBD 
  13. --*/  
  14.   
  15. {  
  16.     NTSTATUS st;  
  17.     PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;  
  18.   
  19.     st = STATUS_SUCCESS;  
  20.   
  21.     try {  
  22.   
  23.         if ( LdrpInLdrInit == FALSE ) {  
  24.             RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);  
  25.             }  
  26.     if ( LdrpShutdownInProgress ) {  
  27.         return STATUS_SUCCESS;  
  28.         }  
  29.   
  30.         if (LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)) {  
  31.             if ( LdrDataTableEntry->TlsIndex ) {  
  32.                 st = STATUS_DLL_NOT_FOUND;  
  33.                 }  
  34.             else {  
  35.                 LdrDataTableEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;  
  36.                 }  
  37.             }  
  38.         }  
  39.     finally {  
  40.         if ( LdrpInLdrInit == FALSE ) {  
  41.             RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);  
  42.             }  
  43.         }  
  44.     return st;  
  45. }  
        我们看第35行,会发现 LdrDataTableEntry->Flags or 了 LDRP_DONT_CALL_FOR_THREADS(0x400000)。这个也就验证了刚才分析的_LdrpInitializeThread逻辑中的没有调用DllMain的原因。

        我们再看下LdrpInitializeThread的代码

 

  1. VOID LdrpInitializeThread( IN PCONTEXT Context )  
  2. {  
  3.     PPEB Peb;  
  4.     PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;  
  5.     PDLL_INIT_ROUTINE InitRoutine;  
  6.     PLIST_ENTRY Next;  
  7.   
  8.     Peb = NtCurrentPeb();  
  9.   
  10.     if ( LdrpShutdownInProgress ) {  
  11.         return;  
  12.     }  
  13.   
  14.     LdrpAllocateTls();  
  15.   
  16.     Next = Peb->Ldr->InMemoryOrderModuleList.Flink;  
  17.     while (Next != &Peb->Ldr->InMemoryOrderModuleList) {  
  18.         LdrDataTableEntry  
  19.             = (PLDR_DATA_TABLE_ENTRY)  
  20.             (CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));  
  21.   
  22.         //  
  23.         // Walk through the entire list looking for  
  24.         // entries. For each entry, that has an init  
  25.         // routine, call it.  
  26.         //  
  27.         if (Peb->ImageBaseAddress != LdrDataTableEntry->DllBase) {  
  28.             if ( !(LdrDataTableEntry->Flags & LDRP_DONT_CALL_FOR_THREADS)) {  
  29.                 InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;  
  30.                 if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {  
  31.                     if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {  
  32.                         if ( LdrDataTableEntry->TlsIndex ) {  
  33.                             if ( !LdrpShutdownInProgress ) {  
  34.                                 LdrpCallTlsInitializers(LdrDataTableEntry->DllBase,DLL_THREAD_ATTACH);  
  35.                             }  
  36.                         }  
  37.   
  38. #if defined (WX86)  
  39.                         if (!Wx86ProcessInit ||  
  40.                             LdrpRunWx86DllEntryPoint(InitRoutine,  
  41.                             NULL,  
  42.                             LdrDataTableEntry->DllBase,  
  43.                             DLL_THREAD_ATTACH,  
  44.                             NULL  
  45.                             ) ==  STATUS_IMAGE_MACHINE_TYPE_MISMATCH)  
  46. #endif  
  47.                         {  
  48.                             if ( !LdrpShutdownInProgress ) {  
  49.                                 LdrpCallInitRoutine(InitRoutine,  
  50.                                     LdrDataTableEntry->DllBase,  
  51.                                     DLL_THREAD_ATTACH,  
  52.                                     NULL);  
  53.                             }  
  54.                         }  
  55.                     }  
  56.                 }  
  57.             }  
  58.         }  
  59.         Next = Next->Flink;  
  60.     }  
  61.   
  62.     //  
  63.     // If the image has tls than call its initializers  
  64.     //  
  65.   
  66.     if ( LdrpImageHasTls && !LdrpShutdownInProgress ) {  
  67.         LdrpCallTlsInitializers(NtCurrentPeb()->ImageBaseAddress,DLL_THREAD_ATTACH);  
  68.     }  
  69.   
  70. }  
        这段逻辑的意思是:拿到PEB之后,从PEB的LDR字段中的InMemoryOrderModuleList中获取已经加载进入内存中的DLL信息。枚举这些DLL信息,如果该DLL信息的Flags字段或上LDRP_DONT_CALL_FOR_THREADS(0x40000),则不对其调用LdrpCallInitRoutine,进而不对调用DllMain。这就是为什么DisableThreadLibraryCalls会禁止DllMain的调用的原因。但是目前为止,我们还没发现哪个逻辑进入临界区而没出来,这样我们就要查看调用LdrpInitializeThread的LdrpInitialize的代码

 

  1. 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.     …  
        我们看到在11或13或25行进入临界区后,在29行调用了 LdrpInitializeThread,而在31行退出临界区。这就是说整个LdrpInitializeThread的逻辑都在临界区中执行的,也就是说DisableThreadLibraryCalls将无权干涉是否会进入临界区。这就解释了为什么不能使用DisableThreadLibraryCalls来使上例解决死锁的原因。
 
 
posted on 2023-06-18 22:51  不及格的程序员-八神  阅读(365)  评论(0编辑  收藏  举报