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