1. 程式人生 > >dll 載入與解除安裝的順序研究

dll 載入與解除安裝的順序研究

之前寫過一篇DLL 載入解除安裝的文章,不太好,重寫一下,更深入一點。

兩個組或者兩個公司獨立的開發可能用來組成同一個產品的元件,但是他們必須獨立的構建、測試和提交他們的工作。元件粒度很難是正確的且與怎麼對元件是最好的這樣的問題無關。取而代之的是,一個元件是怎樣才能對公司最好(團隊不喜歡使用多個dll,且他們想自主的寫一個單獨的DLL,以便他們的測試人員可以對測試元件所意為的工作量感到滿意)以及怎樣才對一個高效能的團隊最好的集合(一個功能一個DLL將使系統的效能下降不少)。

當一個載入器發現這種現象的時候,它會怎麼做?

誰更深,誰被更早的新增到列表中??

裝了火絨就是上面的效果,因為火絨會注入dtrampo.dll 到程序中。

不裝火絨的時候就是這種狀態。

為什麼DLL 以錯誤的順序解除安裝?

當一個程式開始執行,或者一個DLL 被載入,載入器為DLL 或者程式建立一個依賴樹。之後載入器找到一個正確的載入順序,使得,直到一個DLL 的所有的依賴的DLL 都被載入了之後該DLL才會被初始化。如果你有一個迴圈依賴,下面將思考這個問題??如果你在DLL_PROCESS_ATTACH 通知中呼叫LoadLibrary,將使得這個情形更加混亂,而且,會導致死鎖。

首先:迴圈載入:
DemoProgram:

#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello(); int main() { printf("DemoProgram world\r\n"); hello(); getchar(); return 0; }

helloworld.dll
// dllmain.cpp : 定義 DLL 應用程式的入口點。

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
extern "C" __declspec(dllexport) void
hello() { printf("Hello World\r\n"); return; } #pragma comment(lib,"ref_hello.lib") extern "C" __declspec(dllimport) void ref_hello(); BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: printf("HelloWorld Module\r\n"); // hello(); ref_hello(); break; case DLL_PROCESS_DETACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; }

ref_hello.dll:
// dllmain.cpp : 定義 DLL 應用程式的入口點。

#include "stdafx.h"
#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
       {
              printf("ref_hello Module\r\n");
              hello();
              break;
       }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
extern "C" __declspec(dllexport) void ref_hello()
{
       printf("ref_hello \r\n");
}

輸出為:
ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
Hello World

我們改變DemoProgram ,修改對helloworld 模組的引用為:對ref_hello 模組的引用:

#include <stdio.h>
// #pragma comment(lib,"helloworld.lib")
// extern "C" __declspec(dllimport) void hello();
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       getchar();
       return 0;
}

輸出如下:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
ref_hello

我們再次改變DemoProgram,使其同時引用兩個模組:

#include <stdio.h>
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       hello();
       getchar();
       return 0;
}

輸出為:

ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
ref_hello
Hello World

修改上面對於兩個模組的引用的順序:

#include <stdio.h>
#pragma comment(lib,"ref_hello.lib")
extern "C" __declspec(dllimport) void ref_hello();
#pragma comment(lib,"helloworld.lib")
extern "C" __declspec(dllimport) void hello();
int main()
{
       printf("DemoProgram world\r\n");
       ref_hello();
       hello();
       getchar();
       return 0;
}

輸出如下:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
Hello World
ref_hello

似乎發現了什麼。。。。。。。。。。。。。。。。。。
其PE 檔案中的DLL 的載入順序同上,

接下來在專案屬性中修改兩個模組的順序如下:

這裡寫圖片描述

ref_hello Module
Hello World
HelloWorld Module
ref_hello
DemoProgram world
Hello World
ref_hello

再次修改順序如下:
這裡寫圖片描述
輸出結果為:
HelloWorld Module
ref_hello
ref_hello Module
Hello World
DemoProgram world
Hello World
ref_hello

我們似乎又發現了什麼。。。。。。。。。。。。。。。。。。。。。。。。。。
使用CFF Explorer_CN.exe 檢視兩種EXE 其匯入表如下:
這裡寫圖片描述
這裡寫圖片描述

再對比EXE 的輸出我們可以發現匯入順序的玄機。

這個沒有考慮到一個問題是:如果A,B 迴圈依賴,且在DLL_PROCESS_DETACH 訊息中依然引用對方的程式碼,將可能導致程式崩潰,因為引用了已經解除安裝的DLL 的程式碼。
下一篇:

1. 如何在不修改PE 檔案的情況下修改其匯入的模組的載入順序??????

2. dllmain 中呼叫LoadLibrary