[轉]DllMain中不當操作導致死結問題的分析——DllMain中要謹慎寫代碼(完結篇)

來源:互聯網
上載者:User

標籤:

在CSDN中發現這篇文章,講解的比較詳細,所以在這裡備份一個。原文連結:http://blog.csdn.net/breaksoftware/article/details/8167641

 

DllMain的相關特性

首先列出《DllMain中不當操作導致死結問題的分析--進程對DllMain函數的調用規律的研究和分析》中論證的11個特性:
 

  1. Dll的載入不會導致之前建立的線程調用其DllMain函數。
  2. 線程建立後會調用已經載入了的DLL的DllMain,且調用原因是DLL_THREAD_ATTACH。(DisableThreadLibraryCalls會導致該過程不被調用)
  3. TerminateThread方式終止線程是不會讓該線程去調用該進程中載入的Dll的DllMain。
  4. 線程正常退出時,會調用進程中還沒卸載的DLL的DllMain,且調用原因是DLL_THREAD_DETACH。
  5. 進程正常退出時,會調用(不一定是主線程)該進程中還沒卸載的DLL的DllMain,且調用原因是DLL_PROCESS_DETACH。
  6. 載入DLL進入進程空間時(和哪個線程LoadLibrary無關),載入它的線程會調用DllMain,且調用原因是DLL_PROCESS_ATTACH。
  7. DLL從進程空間中卸載出去前,會被卸載其的線程調用其DllMain,且調用原因是DLL_PROCESS_DETACH。
  8. TerminateProcess 將導致線程和進程在退出時不對未卸載的DLL進行DllMain調用。
  9. ExitProcess將導致主線程意外退出,子線程對未卸載的DLL進行了DllMain調用,且調用原因是DLL_PROCESS_DETACH。
  10. ExitThread是最和平的退出方式,它會讓線程退出前對未卸載的DLL調用DllMain。
  11. 線程的建立和退出不會對調用了DisableThreadLibraryCalls的DLL調用DllMain。

 

不要在DllMain中做的事情 一、直接或者間接調用LoadLibrary(Ex)

假如我們在A.dll中的DllMain收到DLL_PROCESS_ATTACH時,載入了B.dll;而B.dll中的DllMain在收到DLL_PROCESS_ATTACH時又去載入A.dll。則產生了循環相依性。但是注意不要想當然認為這個過程是A.dll的DllMain調用了B.dll的DllMain,B.dll的DllMain再調用了A.dll的DllMain這樣的死迴圈。即使不出現循環相依性,如果出現《DllMain中不當操作導致死結問題的分析——線程中調用GetModuleFileName、GetModuleHandle等導致死結》中第三個例子的情況,也會死結的。

 

二、使用CoInitializeEx

在CoInitializeEx底層會調用LoadLibraryEx,原因同A。

 

三、使用CreateProcess

CreateProcess在底層執行了載入DLL的操作。我用IDA查看Kernel32中的CreateProcess可以發現其底層調用的CreateProcessInternalW中有

 

  四、使用User32或Gdi32中的函數

User32和Gdi32中部分函數在調用的底層會載入其他DLL。

  五、使用Managed 程式碼

運行Managed 程式碼需要載入其他DLL。

  六、與其他線程同步執行

由《DllMain中不當操作導致死結問題的分析--載入卸載DLL與DllMain死結的關係》、《DllMain中不當操作導致死結問題的分析--導致DllMain中死結的關鍵隱藏因子》和《DllMain中不當操作導致死結問題的分析--線程退出時產生了死結》可知,進程建立和銷毀以及DLL的載入都要進入PEB的LoadLock臨界區。如果佔用了LoaderLock臨界區的線程在等待一個需要經過臨界區才能結束的線程時,就發生了死結。以上3篇博文中均有案例。

  七、同步對象

如果該同步對象的釋放需要獲得PEB中的LoaderLock,而佔用該臨界區的線程又要去等待這個同步對象,則會死結。其實F中的線程也算是個同步對象。案例詳見《DllMain中不當操作導致死結問題的分析——線程中調用GetModuleFileName、GetModuleHandle等導致死結》中例子。

  八、使用CreateThread

理由同六。

  九、使用ExitThread

理由同六。

  十、寄希望於DisableThreadLibraryCalls解決死結問題

由《DllMain中不當操作導致死結問題的分析--DisableThreadLibraryCalls對DllMain中死結的影響》可知。DisableThreadLibraryCalls的實現邏輯是:找到PEB結構中用於儲存載入器資訊的結構體對象Ldr。

 

Ldr對象的InMemoryOrderModuleList使用者儲存已經載入的DLL的鏈表。

 

它遍曆這個鏈表,找到調用DisableThreadLibraryCalls的DLL的資訊,將該資訊中的Flags欄位設定或上0x40000。

 

而建立線程在底層將調用LdrpInitializeThread(詳見《DllMain中不當操作導致死結問題的分析--DisableThreadLibraryCalls對DllMain中死結的影響》)。該函數一開始便進入了PEB中LoaderLock臨界區,在該臨界區中根據PEB中LDR的InMemoryOrderModuleList遍曆載入的DLL,然後判斷該DLL資訊的Flags欄位是否或上了0x40000。如果或上了,就不調用DllMain。如果沒或上,就調用DllMain。這說明DisableThreadLibraryCalls對建立線程時是否進入臨界區無關。

在退出線程時底層將調用LdrShutdownThread(詳見《DllMain中不當操作導致死結問題的分析--線程退出時產生了死結》)。該函數邏輯和LdrpInitializeThread相似,只是在調用DllMain時傳的是DLL_THREAD_DETACH。所以DisableThreadLibraryCalls對LdrShutdownThread是否進入臨界區也是沒有影響的。

[轉]DllMain中不當操作導致死結問題的分析——DllMain中要謹慎寫代碼(完結篇)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.