這是許多初學者困惑的地方。答案取決於連結方式:
.lib,includelib + invoke Func。Windows 載入器會在 EXE 進入點執行前,先載入所有靜態相依的 DLL 並呼叫其 DllEntry (DLL_PROCESS_ATTACH)。LoadLibrary / GetProcAddress。EXE 先在進入點開始執行,直到執行到 LoadLibrary 那一行,DLL 才會被載入。修改前一篇的 MyDll.asm 與測試程式,在 DllEntry 與 OutEdxEax 中加入 MessageBox 觀察執行順序。
.DATA
szText0 db "哈哈哈...MyDLL加載",0
szText1 db "MyDLL第一次插入進程的地址空間",0
szText2 db "MyDLL從進程的地址空間卸出",0
szText3 db "同一進程的新線程生成",0
szText4 db "同一進程的線程銷毀",0
szText5 db "MyDLL的函數OutEdxEax調用成功",0
.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason == DLL_PROCESS_ATTACH
invoke MessageBox, NULL, addr szText1, addr szText0, MB_OK
mov eax, TRUE
ret
.elseif reason == DLL_PROCESS_DETACH
invoke MessageBox, NULL, addr szText2, addr szText0, MB_OK
mov eax, FALSE
ret
.elseif reason == DLL_THREAD_ATTACH
invoke MessageBox, NULL, addr szText3, addr szText0, MB_OK
mov reason, TRUE
ret
.elseif reason == DLL_THREAD_DETACH
invoke MessageBox, NULL, addr szText4, addr szText0, MB_OK
mov eax, FALSE
ret
.endif
ret
DllEntry Endp
OutEdxEax proc uses ebx esi edi, lpString
; ... 轉換邏輯同上 ...
invoke MessageBox, NULL, addr szText5, addr szText0, MB_OK ; 驗證函式被呼叫
ret
OutEdxEax endp
include mydll.inc
includelib mydll.lib
.DATA
szText db "哈哈哈...10.exe運行",0
.code
start:
invoke MessageBox, NULL, addr szText, addr szText, MB_OK ; EXE 入口第一個動作
mov edx, 12345678h
mov eax, 87654321h
invoke OutEdxEax, addr CharOut
invoke StdOut, addr CharOut
invoke ExitProcess, NULL
end start
觀察結果順序:
結論:隱式連結下,DLL 載入與初始化 早於 EXE 進入點代碼。
; 不 include mydll.inc, 不 includelib mydll.lib
.DATA
MyDllFileName db "e:\masm32\xlfancy\mydll.dll",0
CallFuncName db "OutEdxEax",0
.data?
MyDLLHandle dd ?
OutEdxEaxAddr dd ?
.code
start:
invoke MessageBox, NULL, addr szText, addr szText, MB_OK ; EXE 先跑
invoke LoadLibrary, offset MyDllFileName ; 這裡才載入 DLL
or eax, eax
jz ExitPro
mov MyDLLHandle, eax
invoke GetProcAddress, eax, offset CallFuncName
or eax, eax
jz FreeLib
mov OutEdxEaxAddr, eax
; 手動準備參數呼叫
lea eax, CharOut
push eax
mov edx, 12345678h
mov eax, 87654321h
call OutEdxEaxAddr
invoke StdOut, addr CharOut
FreeLib:
invoke FreeLibrary, MyDLLHandle
ExitPro:
invoke ExitProcess, NULL
end start
觀察結果順序:
結論:顯式連結下,EXE 進入點代碼 早於 DLL 載入。
| 特性 | 隱式連結 | 顯式連結 |
|---|---|---|
| 載入時機 | 進程建立初期,載入器解析 IAT | 執行期呼叫 LoadLibrary 時 |
| 相依性解析 | 靜態:EXE 匯入表列出所有 DLL | 動態:程式碼決定載入誰 |
| 錯誤處理 | DLL 缺失 -> 進程啟動失敗 (彈系統錯誤框) | LoadLibrary 返回 NULL,程式可自行處理 |
| 適用場景 | 核心功能、必須存在的庫 | 外掛、選用功能、延遲載入優化啟動速度 |
| GetProcAddress 需要? | 不需要,連結器處理 | 必要,取得函式位址 |
「以上都在本機編譯通過,可就是看不到結果。(折騰了半天,不只所以,JMP...) 不管了,沒犯路線錯誤就行...(馬馬乎乎,也敢貼出來...『砰』,怎麼有磚打到腦袋上?)」
這段話道出了底層開發的真實寫照:理論懂了,環境配對、路徑正確、權限足夠,才能跑出預期結果。實驗環境 (Win2000/XP 時代) 與現代 Windows (簽名驅動、強制驅動簽名、PPL 保護) 差異極大,請在虛擬機或測試機驗證。