Windows 的 DLL 名稱解析機制 - 以實務角度整理搜尋順序、Known DLLs、API set、SxS
· 小村 豪 · Windows, DLL, Loader, 資安, Windows 開發
在 Windows 上處理原生 DLL 時,常會出現下列混亂。
- 寫
LoadLibrary("foo.dll")時,實際會去哪裡找? - 明明和執行檔放在同一個資料夾,為什麼載入的是別的 DLL?
- 是
System32優先,還是應用資料夾優先? - manifest、API set、Known DLLs 各自是在哪個階段生效?
- 使用
SetDllDirectory或AddDllDirectory會改變什麼? - 什麼情況下容易被 DLL 植入攻擊或 DLL hijacking?
這個議題光是「背一行搜尋順序」並不足以應付實務。
實際上,Windows 的 loader 在一路掃描檔案系統之前,會先評估幾條特別規則。
本文把 Windows 的 DLL 名稱解析,連同 unpackaged app 與 packaged app 的差異、Known DLLs、loaded-module list、API set、side-by-side manifest、LoadLibraryEx 系列 API 的影響 一起,以實務角度整理。
內容以 2026 年 3 月時點 的 Microsoft Learn 公開資訊為前提。123456789
1. 先下結論
先把實務向的結論列出來。
- Windows 的 DLL 名稱解析 不是「先掃檔案系統」。DLL redirection、API set、SxS manifest、loaded-module list、Known DLLs 都在檔案系統搜尋之前。1
- unpackaged app 在 safe DLL search mode 生效的標準狀態下,應用資料夾雖然位置靠前,但前面還有上述特別規則會先評估。1
- 即便用完整路徑載入 DLL,該 DLL 的相依 DLL 並不會自動也被固定在同一個完整路徑。相依 DLL 仍然是以模組名稱搜尋,可能從別處解析。1
Known DLLs是 OS 把特定已知 DLL 綁到系統側副本的機制,不是一般應用能用放置覆蓋的東西。1- API set 並不是「實際 DLL 的檔名」,而是 把實作 DLL 隱藏在後的虛擬別名。看到
api-ms-win-...這種名字若用一般 DLL 搜尋的觀念來想,很容易搞錯。3 SetDllDirectory除了改變搜尋順序,還會 實質上停用 safe DLL search mode,隨便使用反而可能在資安上適得其反。1- 實務上,把 完整路徑、
SetDefaultDllDirectories、AddDllDirectory、LoadLibraryEx的LOAD_LIBRARY_SEARCH_*旗標 組合起來,明確縮小搜尋空間才是安全的做法。4562
總之,Windows 的 DLL 名稱解析不是「哪個資料夾排第幾」,而是「有沒有前置規則在解析名字」與「透過 API 怎麼改變搜尋空間」共同決定,這種觀點比較貼近實務。
2. DLL 名稱解析在「資料夾掃描」之前,還有前置規則
Microsoft Learn 的 DLL search order 說明中,在載入 DLL 時會先把下列要素當成搜尋順序的一部分。1
- DLL redirection
- API sets
- SxS manifest redirection
- loaded-module list
- Known DLLs
之後才進到 app folder、System32、Windows 資料夾、PATH 等檔案系統層級的搜尋。1
沒掌握這一點,會覺得「怎麼會有東西在應用資料夾之前就決定」——但以 Windows loader 的描述來說,這其實才是主軸。
flowchart TD
A["想解析某個 DLL 名稱"] --> B["DLL redirection"]
B --> C["API set"]
C --> D["SxS manifest redirection"]
D --> E["loaded-module list"]
E --> F["Known DLLs"]
F --> G["檔案系統層級的搜尋順序"]
G --> H["決定實際要載入哪個 DLL"]
3. unpackaged app 的標準搜尋順序
對於一般桌面應用,若載入 DLL 時不使用完整路徑,Microsoft Learn 有說明 unpackaged app 的標準搜尋順序。在 safe DLL search mode 生效的預設情況下,主要流程如下。1
- DLL redirection
- API sets
- SxS manifest redirection
- loaded-module list
- Known DLLs
- Windows 11 21H2 以後加入 package dependency graph
- 應用程式被載入的資料夾
System32- 16-bit system folder
- Windows folder
- current folder
PATH
實務上特別重要的是:
- current folder 在預設下相當靠後。safe DLL search mode 讓 current folder 很難被擠到前面。1
- 但 靠後並不代表安全。只要被攻擊者掌控的目錄還留在搜尋範圍內,DLL preloading 的風險就存在。2
- 在 Windows 11 21H2 以後,就連 unpackaged app 的搜尋說明裡也加入了 package dependency graph。若只記得舊說明,很容易忽略這一點。1
4. packaged app 與 unpackaged app 並不一樣
Microsoft Learn 對 packaged app 定義了另一套搜尋順序。packaged app 會讓 package dependency graph 在更前段生效,搜尋的思維本身也有些不同。1
忽略這個差別的話,MSIX 化或導入 Windows App SDK 後會遇到下列混亂。
- 開發時的 unpackaged 執行找得到的 DLL,在正式 package 時找不到
- package manifest 指定的相依關係與舊式
PATH相依混在一起,重現條件也跟著改變 - 用單一張「Windows 的 DLL 搜尋順序就是這樣」的表說明,結果漏了 packaged app 的差異
在文章或設計審查中,先區分「這是在講 packaged app 還是 unpackaged app」 會比較安全。1
5. Known DLLs 與 loaded-module list 在做什麼
DLL 名稱解析中最違反直覺的,是 loaded-module list 與 Known DLLs。
5.1 loaded-module list
Microsoft Learn 寫到,系統會確認 是否已經有同名模組載入在記憶體。1
也就是說,在檔案系統搜尋之前,會先判斷:
- 這個 DLL 名稱是否已經載入過?
- 既然如此,還需不需要真的去找?
因此在調查時,若漏掉「這個行程其實早就載入了另一個資料夾的同名 DLL」這件事,會誤判重現條件。
5.2 Known DLLs
Known DLLs 是 Windows 在該版本中視為「已知」的 DLL 清單,可以在 HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs 下確認。若是其中的 DLL,系統會直接使用已知的那份副本。1
重點是,Known DLLs 不是「在應用資料夾放同名 DLL 就能搶先」那類情境。
如果用「和 System32 比誰先」的思路去理解,會誤判行為。
6. API set 不是「真正的 DLL 名稱」,而是契約名稱
看到 api-ms-win-core-... 這類名字,直覺會想「這個 DLL 在哪」。但 Microsoft Learn 說明,API set 是 對實體 DLL 的虛擬別名,把實作與契約分離。3
換句話說,把
- API set 名稱 = 實體 DLL 檔名
- API set 解析 = 一般 DLL 檔案搜尋
這樣想並不正確。
若掌握 API set 的觀念,就能說明:
- 不同 Windows 版本或裝置類型上實作 DLL 的名字可能不同,但整體仍然一致
- 呼叫端不需要固定知道「是哪個 host DLL 在實作」
這些情境。3
7. manifest 與 side-by-side(SxS)是解決 DLL versioning 問題的另一種方案
DLL redirection 與 SxS manifest 不只是搜尋順序的小技巧,是為了避免 DLL versioning 衝突而設計的機制。789
Microsoft Learn 的整理是:
- manifest 是描述 side-by-side assembly 或 isolated application 的 XML
- side-by-side assembly 是命名、binding、versioning、deployment 的單位
- loader 根據 manifest 上的相依關係,判斷該 bind 到哪個版本
因此在實務上,需要把下列情境分開思考。
- 只是把 private DLL 放進 app folder
- 用
.local等 DLL redirection - 用 manifest 做 side-by-side binding
這些雖然「都會影響 DLL 解析」,但設計意圖並不相同。
8. LoadLibraryEx、SetDllDirectory、AddDllDirectory 會改變什麼
8.1 SetDllDirectory
SetDllDirectory 會改變搜尋順序,但 Microsoft Learn 明言它會 實質停用 safe DLL search mode。1
也就是說,即使你只是想「增加一個應用專屬資料夾」,結果也會影響到包含 current folder 在內的整個搜尋空間。
此外,若父行程呼叫 SetDllDirectory,其影響可能波及子行程的標準搜尋順序。1
因此實務上,與其隨便常用 SetDllDirectory,不如偏向以下組合:
SetDefaultDllDirectoriesAddDllDirectoryLoadLibraryEx的LOAD_LIBRARY_SEARCH_*
8.2 AddDllDirectory
AddDllDirectory 加入的路徑,要搭配 LOAD_LIBRARY_SEARCH_USER_DIRS 使用。
Microsoft Learn 指出,當加入多個路徑時,搜尋順序 未定義。15
所以,
- 加入了多個目錄
- 還指望它們之間有固定的搜尋順序
這種設計要避免。
8.3 SetDefaultDllDirectories
SetDefaultDllDirectories 是用來從標準 DLL search path 中移除易受攻擊的目錄、縮小搜尋範圍的 API。4
特別重要的是:
- 以行程為單位生效
- 呼叫後在行程生命週期內持續
- 已設定過的標準搜尋路徑,無法原封不動地還原回一開始的標準形態
若考量資安,「在啟動後立即把搜尋空間縮到較安全的範圍」這類設計用這個 API 會比較好下手。4
8.4 LoadLibraryEx
LoadLibraryEx 可以透過 LOAD_WITH_ALTERED_SEARCH_PATH 或 LOAD_LIBRARY_SEARCH_* 旗標改變搜尋行為。61
實務上,它適合用來表達下列需求。
- 含相依 DLL,想把「載入端 DLL 的資料夾」也納入搜尋對象
- 想把搜尋範圍縮到應用資料夾、
System32、以及明確加入的使用者目錄
9. 就算用完整路徑,相依 DLL 也不會被固定
這是這個主題實務上非常重要、卻又容易被忽略的一點。
Microsoft Learn 明言,即使最初的 DLL 以完整路徑載入,它的相依 DLL 仍會以模組名稱進行搜尋。14
也就是說,
- 明確載入了
C:\\MyApp\\plugins\\foo.dll - 但
foo.dll相依的bar.dll不一定會從同一個資料夾被取用
這可能會造成:
- 開發環境能跑
- 部署到使用端卻解析到別的
bar.dll - 相依 DLL 衝突變成依使用環境才重現
這類有點棘手的問題。
10. 如何避免 DLL preloading / hijacking
Microsoft Learn 的 DLL security 指出,沒有完整路徑的動態載入,加上能被攻擊者掌控的搜尋目錄,會導向 DLL preloading attack 或 binary planting attack。2
實務上以下列為基本原則會比較好整理。
- 減少類似
LoadLibrary("foo.dll")這種只給裸名的載入 - 需要時使用完整路徑
- 用
SetDefaultDllDirectories縮小行程的預設搜尋路徑 - 只允許的目錄用
AddDllDirectory顯式加入 - 搭配
LOAD_LIBRARY_SEARCH_SYSTEM32、LOAD_LIBRARY_SEARCH_APPLICATION_DIR、LOAD_LIBRARY_SEARCH_USER_DIRS等旗標明確表達搜尋範圍 - 避免 current folder 或隨便倚賴
PATH
尤其是 以管理員權限執行的行程帶著模糊的搜尋路徑 的情境相當危險。Microsoft Learn 也寫明,惡意 DLL 一旦被載入,將以該行程的權限執行。2
11. 實務上的判斷 checklist
審查 Windows 上的 DLL 載入設計時,至少確認以下項目可以減少事故。
- 該應用是 packaged app 還是 unpackaged app
- 哪些 DLL 來自靜態連結,哪些是動態載入
- 是完整路徑還是只有模組名稱
- 有沒有用
SetDllDirectory - 是否能改用
SetDefaultDllDirectories+LOAD_LIBRARY_SEARCH_* - 是否多次使用
AddDllDirectory且暗中期待其順序 - 相依關係是以 manifest / SxS / private DLL / redirection 中哪一個管理
- current folder 或
PATH有沒有在資安上過於寬鬆的假設 - 相依 DLL 會不會在其他環境被從不同地方解析
把這 9 點拆開確認,「找不到 DLL」「載入錯的 DLL」「只有正式環境啟動不了」「資安審查卡關」這類問題大多可以提早化解。
12. 總結
Windows 的 DLL 名稱解析不是單純的「資料夾搜尋順序」。
實際上,DLL redirection、API set、SxS manifest、loaded-module list、Known DLLs,以及 API 呼叫改變的搜尋空間 疊加後才決定最終結果。134
實務上最重要的幾點是:
- 不要把搜尋順序當成一張死背的表
- 區分 packaged / unpackaged
- 理解即便指定完整路徑,相依 DLL 仍可能被另外處理
- 別草率使用
SetDllDirectory - 想往安全靠,就用
SetDefaultDllDirectories與LoadLibraryEx的搜尋旗標
DLL 名稱解析是啟動故障、環境差異、資安問題同時浮上檯面的地方。
正因如此,不只要了解「Windows 用什麼順序去找」,更值得了解「Windows 一開始把什麼當作名稱解析的前提」。
Related articles
References
- Microsoft Learn: Dynamic-link library search order
- Microsoft Learn: Dynamic-Link Library Security
- Microsoft Learn: Windows API sets
- Microsoft Learn: SetDefaultDllDirectories function
- Microsoft Learn: AddDllDirectory function
- Microsoft Learn: LoadLibraryEx function
- Microsoft Learn: Dynamic-link library redirection
- Microsoft Learn: Manifests
- Microsoft Learn: About Side-by-Side Assemblies
-
Microsoft Learn, “Dynamic-link library search order”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21
-
Microsoft Learn, “Dynamic-Link Library Security”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-security ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, “Windows API sets”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, “SetDefaultDllDirectories function”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, “AddDllDirectory function”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-adddlldirectory ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, “LoadLibraryEx function”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, “Dynamic-link library redirection”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection ↩ ↩2
-
Microsoft Learn, “Manifests”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/sbscs/manifests ↩ ↩2 ↩3
-
Microsoft Learn, “About Side-by-Side Assemblies”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/sbscs/about-side-by-side-assemblies- ↩ ↩2 ↩3
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
Windows 什麼時候需要系統管理員權限 - UAC、保護區、設計上的分辨方式
從邊界與儲存位置的角度,整理 Windows 何時真正需要系統管理員權限:UAC、保護區、HKLM、服務、驅動、防火牆。同時說明 per-user 與 per-machine 的差異,以及把管理員處理切成獨立 EXE、服務或工作的設計取捨,幫讀者判斷該不該提權。
ClickOnce 是什麼 - 以實務視角整理機制、更新、適合場面・不適合場面
本文以實務視角整理 ClickOnce 是什麼,從 manifest、快取、更新、簽章的構造,到適合公司內部 .NET 桌面業務應用程式的案件與不適合 machine-wide 或 service、driver 等深度 OS 整合的案件,幫助讀者判斷是否採用並掌握 depl...
用 Windows 沙箱加速 Windows 應用程式開發的驗證 - 以實務向整理管理員權限問題、乾淨環境、權限不足・資源不足的重現
整理在 Windows 應用程式開發中如何運用 Windows Sandbox 加速驗證的實務做法。透過按情境分檔的 .wsb、唯讀輸入與專用 Outbox 寫入分離、在容器內另建標準使用者重現權限不足、以及降低記憶體和關閉 vGPU 製造資源不足偏向,把每次的乾淨環境準備...
Windows 應用程式中把「僅需要系統管理員權限的處理」分離出來的具體寫法
本文以 .NET 8 桌面應用程式為例,具體展示如何讓 UI 保持 asInvoker,把僅需系統管理員權限的處理切到 helper EXE。涵蓋 manifest、runas 啟動、具名管道 ACL、PID 驗證、固定 operation 與請求驗證,以及 Explore...
Windows 應用程式不要把機密資訊以明文存進設定檔的最佳實踐
本文整理 Windows 桌面應用程式儲存連線憑證或 API Token 時的實務做法,說明為何 DPAPI 與 ProtectedData 比明文或自做加密更能切斷檔案外流即等同機密外流的鏈條,並比較 CurrentUser 與 LocalMachine 的適用情境、優先...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。