Windows 的 DLL 名稱解析機制 - 以實務角度整理搜尋順序、Known DLLs、API set、SxS

· · Windows, DLL, Loader, 資安, Windows 開發

在 Windows 上處理原生 DLL 時,常會出現下列混亂。

  • LoadLibrary("foo.dll") 時,實際會去哪裡找?
  • 明明和執行檔放在同一個資料夾,為什麼載入的是別的 DLL?
  • System32 優先,還是應用資料夾優先?
  • manifest、API set、Known DLLs 各自是在哪個階段生效?
  • 使用 SetDllDirectoryAddDllDirectory 會改變什麼?
  • 什麼情況下容易被 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
  • 實務上,把 完整路徑、SetDefaultDllDirectoriesAddDllDirectoryLoadLibraryExLOAD_LIBRARY_SEARCH_* 旗標 組合起來,明確縮小搜尋空間才是安全的做法。4562

總之,Windows 的 DLL 名稱解析不是「哪個資料夾排第幾」,而是「有沒有前置規則在解析名字」與「透過 API 怎麼改變搜尋空間」共同決定,這種觀點比較貼近實務。

2. DLL 名稱解析在「資料夾掃描」之前,還有前置規則

Microsoft Learn 的 DLL search order 說明中,在載入 DLL 時會先把下列要素當成搜尋順序的一部分。1

  1. DLL redirection
  2. API sets
  3. SxS manifest redirection
  4. loaded-module list
  5. 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

  1. DLL redirection
  2. API sets
  3. SxS manifest redirection
  4. loaded-module list
  5. Known DLLs
  6. Windows 11 21H2 以後加入 package dependency graph
  7. 應用程式被載入的資料夾
  8. System32
  9. 16-bit system folder
  10. Windows folder
  11. current folder
  12. 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 listKnown 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 到哪個版本

89

因此在實務上,需要把下列情境分開思考。

  • 只是把 private DLL 放進 app folder
  • .local 等 DLL redirection
  • 用 manifest 做 side-by-side binding

這些雖然「都會影響 DLL 解析」,但設計意圖並不相同。

8. LoadLibraryExSetDllDirectoryAddDllDirectory 會改變什麼

8.1 SetDllDirectory

SetDllDirectory 會改變搜尋順序,但 Microsoft Learn 明言它會 實質停用 safe DLL search mode1

也就是說,即使你只是想「增加一個應用專屬資料夾」,結果也會影響到包含 current folder 在內的整個搜尋空間。

此外,若父行程呼叫 SetDllDirectory,其影響可能波及子行程的標準搜尋順序。1

因此實務上,與其隨便常用 SetDllDirectory,不如偏向以下組合:

  • SetDefaultDllDirectories
  • AddDllDirectory
  • LoadLibraryExLOAD_LIBRARY_SEARCH_*

這比較安全。456

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_PATHLOAD_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_SYSTEM32LOAD_LIBRARY_SEARCH_APPLICATION_DIRLOAD_LIBRARY_SEARCH_USER_DIRS 等旗標明確表達搜尋範圍
  • 避免 current folder 或隨便倚賴 PATH

尤其是 以管理員權限執行的行程帶著模糊的搜尋路徑 的情境相當危險。Microsoft Learn 也寫明,惡意 DLL 一旦被載入,將以該行程的權限執行。2

11. 實務上的判斷 checklist

審查 Windows 上的 DLL 載入設計時,至少確認以下項目可以減少事故。

  1. 該應用是 packaged app 還是 unpackaged app
  2. 哪些 DLL 來自靜態連結,哪些是動態載入
  3. 是完整路徑還是只有模組名稱
  4. 有沒有用 SetDllDirectory
  5. 是否能改用 SetDefaultDllDirectories + LOAD_LIBRARY_SEARCH_*
  6. 是否多次使用 AddDllDirectory 且暗中期待其順序
  7. 相依關係是以 manifest / SxS / private DLL / redirection 中哪一個管理
  8. current folder 或 PATH 有沒有在資安上過於寬鬆的假設
  9. 相依 DLL 會不會在其他環境被從不同地方解析

把這 9 點拆開確認,「找不到 DLL」「載入錯的 DLL」「只有正式環境啟動不了」「資安審查卡關」這類問題大多可以提早化解。

12. 總結

Windows 的 DLL 名稱解析不是單純的「資料夾搜尋順序」。
實際上,DLL redirection、API set、SxS manifest、loaded-module list、Known DLLs,以及 API 呼叫改變的搜尋空間 疊加後才決定最終結果。134

實務上最重要的幾點是:

  • 不要把搜尋順序當成一張死背的表
  • 區分 packaged / unpackaged
  • 理解即便指定完整路徑,相依 DLL 仍可能被另外處理
  • 別草率使用 SetDllDirectory
  • 想往安全靠,就用 SetDefaultDllDirectoriesLoadLibraryEx 的搜尋旗標

DLL 名稱解析是啟動故障、環境差異、資安問題同時浮上檯面的地方。
正因如此,不只要了解「Windows 用什麼順序去找」,更值得了解「Windows 一開始把什麼當作名稱解析的前提」

References

  1. Microsoft Learn: Dynamic-link library search order
  2. Microsoft Learn: Dynamic-Link Library Security
  3. Microsoft Learn: Windows API sets
  4. Microsoft Learn: SetDefaultDllDirectories function
  5. Microsoft Learn: AddDllDirectory function
  6. Microsoft Learn: LoadLibraryEx function
  7. Microsoft Learn: Dynamic-link library redirection
  8. Microsoft Learn: Manifests
  9. Microsoft Learn: About Side-by-Side Assemblies
  1. 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

  2. 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

  3. Microsoft Learn, “Windows API sets”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets  2 3 4 5

  4. 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

  5. Microsoft Learn, “AddDllDirectory function”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-adddlldirectory  2 3 4

  6. Microsoft Learn, “LoadLibraryEx function”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa  2 3 4

  7. Microsoft Learn, “Dynamic-link library redirection”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection  2

  8. Microsoft Learn, “Manifests”, accessed March 24, 2026, https://learn.microsoft.com/en-us/windows/win32/sbscs/manifests  2 3

  9. 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

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽