深入解析DLL劫持漏洞

wyzsk發表於2020-08-19
作者: xlab · 2016/03/01 12:31

Author:Wins0n

0x00 DLL劫持漏洞介紹


0.1 漏洞簡介

如果在程式嘗試載入一個DLL時沒有指定DLL的絕對路徑,那麼Windows會嘗試去指定的目錄下查詢這個DLL;如果攻擊者能夠控制其中的某一個目錄,並且放一個惡意的DLL檔案到這個目錄下,這個惡意的DLL便會被程式所載入,從而造成程式碼執行。這就是所謂的DLL劫持。

在Windows XP SP2之前,Windows查詢DLL的目錄以及對應的順序如下:

  1. 程式對應的應用程式所在目錄
  2. 當前目錄(Current Directory)
  3. 系統目錄(透過 GetSystemDirectory獲取)
  4. 16位系統目錄
  5. Windows目錄(透過GetWindowsDirectory獲取)
  6. PATH環境變數中的各個目錄

在Windows下,幾乎每一種檔案型別都會關聯一個對應的處理程式,當我們在資源管理器中開啟某種特定型別的檔案時,與之相關聯的處理程式便會被執行,也就是會新建一個程式,程式預設的Current Directory(當前目錄) 就是被開啟檔案所在的目錄。 在Windows搜尋DLL的這些目錄中,攻擊者最容易控制的當然是Current Directory。攻擊者可以把惡意的DLL檔案和目標檔案(如WORD文件) 打包在一起,如果受害者進行解壓操作,惡意DLL和目標檔案就會位於同一個目錄,攻擊者可以十分方便的實施DLL劫持。

由於早期Windows查詢DLL檔案的順序並不合理,可以想象DLL劫持漏洞伴隨著Windows存在了相當長的時間。然而,在相當長的一段時間裡DLL劫持漏洞並沒有受到大家的關注,直到2010年8月,微軟釋出安全通報2269637,同時網上公佈了大量受影響軟體的名字, DLL劫持漏洞才開始進入大家的視野。

0.2 漏洞歸類

DLL劫持漏洞翻譯成英文叫做 DLL Hijacking Vulnerability, CWE將其歸類為 UntrustedSearch Path Vulnerability。 如果想要去CVE資料庫中搜尋DLL劫持漏洞案例, 搜尋這兩個關鍵詞即可。

0.3 緩解措施

從Windows XP SP2開始,SafeDllSearchMode預設會被開啟。 SafeDllSearchMode的開啟與否主要影響Current Directory( 當前目錄) 在搜尋順序中的位置。 開啟SafeDllSearchMode後的DLL搜尋順序如下:

  1. 程式對應的應用程式所在目錄
  2. 系統目錄( 透過GetSystemDirectory獲取)
  3. 16位系統目錄
  4. Windows目錄(透過GetWindowsDirectory獲取)
  5. 當前目錄
  6. PATH環境變數中的各個目錄

啟用 SafeDllSearchMode 之後可以防範大部分 DLL 劫持,如系統 DLL 劫持。不過,如果程式嘗試載入的 DLL 並不存在,那麼程式仍然會嘗試去當前目錄載入這個 DLL ,這是 SafeDllSearchMode所無法防範的。不過微軟引入了SetDllDirectory 這個 API ,給這個 API 傳遞一個空字串就可以將當前目錄從 DLL 搜尋順序中排除掉。

BOOL WINAPI SetDllDirectory(
In_opt LPCTSTR lpPathName
);
If the lpPathName parameter is an empty string (“”), the call removes the current directory
from the default DLL search order.

0.4 漏洞檢查

使用 Sysinternals 工具包中的Process Monitor( ProcMon )可以十分方便的檢測 DLL 劫持漏洞,只需要設定幾個過濾引數即可。

  • ProcessName 目標程式的名字
  • Path 檔案路徑,可以設定為 begins with 當前目錄所在路徑
  • Result 結果,設定為 NAME NOT FOUND

0x01 DLL 劫持漏洞利用場景


1.1 針對應用程式安裝目錄的 DLL 劫持

不管 SafeDllSearchMode 是否開啟,在查詢 DLL 時應用程式本身所在的目錄都是最先被搜尋的。因此如果能夠放一個惡意的 DLL 檔案到程式的安裝目錄,就可以利用 DLL 劫持漏洞來執行程式碼。

這種利用場景的要求相對較高,因為大部分程式預設安裝到%ProgramFiles%或者是%ProgramFiles(x86)%。這兩個目錄都需要管理員許可權才可以進行寫入操作,也就是說在進行DLL 劫持之前,要求已經具有程式碼執行許可權。基於這一原因,軟體廠商一般不予處理此類問題。這種場景多被一些惡意程式碼所使用,對常用軟體進行 DLL 劫持可以在一定程度替代自啟動功能,同時,利用 白加黑 方式還能逃避安全軟體的檢測。此外,一些外掛或者破解程式也會採用這種方式進行 DLL 劫持,例如 QQ 的一些顯 IP 外掛就是透過劫持msimg32.dll來實現功能的。

1.2 針對檔案關聯的 DLL 劫持

在 Windows 下,我們平時使用的各種檔案(如 MP3 音樂、 DOC 文件、 PDF 文件、 MKV 影片等)都有一個與之關聯的預設處理軟體。當在資源管理器中開啟某種特定型別的檔案時,作業系統會自動建立一個程式來處理這個檔案,程式對應的程式就是該檔案型別關聯的預設處理程式,程式的當前目錄就是被開啟檔案所在的目錄。

例如,如果 Adobe Acrobat DC 關聯了 .PDF 檔案型別,那麼開啟 PDF 檔案時就會自動建立一個Acrobat.exe 程式,程式的當前目錄(Current Directory)就是 PDF 檔案所在的目錄。如果程式嘗試載入一個不存在的 DLL ,根據預設的 DLL 搜尋順序,程式最終會搜尋到 PDF 檔案所在目錄(即當前目錄),如果該目錄下恰好就存在有一個同名的 DLL ,那麼這個 DLL 就會被程式所載入。這就是所謂的檔案關聯型 DLL 劫持

相對於針對應用程式安裝目錄的 DLL劫持,針對檔案關聯的 DLL 劫持的利用條件十分簡單,只要放一個惡意的 DLL 就行了。由於實施這種 DLL 劫持不需要其他先決條件,許多廠商關注並承認該利用場景下的 DLL 劫持漏洞

許多流行軟體可能仍然存在有這種 DLL 劫持漏洞:筆者在 2015 年給 12 月給 Adobe 報告了 AdobeAcrobat DC 15.009.20077 中存在的一個 DLL 劫持漏洞( CVE-2016-0947 ),該漏洞由 Acrobat.exe程式載入不存在的 updaternotifications.dll 所引起。此外,去 CVE 漏洞庫搜尋DLL Hijacking或者Untrusted Search Path也能找到很多案例。

1.3 針對安裝程式的 DLL 劫持

許多應用程式的安裝包程式也存在有 DLL 劫持漏洞,這種場景與 針對應用程式安裝目錄的DLL劫持 比較類似,本來也沒有什麼特殊之處,不過結合後文提到的瀏覽器自動下載漏洞,其利用條件又變得相對簡單了。

這裡以 Notepad++ 最新的安裝包 npp.6.9.Installer.exe 為例來進行講解。啟動 ProcMon 並設定好過濾器,可以看到 npp.6.9.Installer.exe 執行後嘗試載入了許多 DLL ,這些都是第一次載入時沒有載入成功的。

p1

仔細觀察程式嘗試載入這些 DLL 時產生的呼叫棧,會發現有的呼叫棧中存在有LoadLibrary(Ex),而有的呼叫棧中卻沒有。這裡選取 Version.dll 和 SHFOLDER.dll 來進行對比說明。

  • npp.6.9.Installer.exe 在嘗試載入 Version.dll 時產生的呼叫棧中並沒有LoadLibrary(Ex),這是因為 DLL 並不是被程式動態載入的,而是因為應用程式的匯入表直接或者間接匯入了這個DLL 。在這種利用場景下,偽造 DLL 的匯出表最好與被偽造 DLL 的匯出表完全一致,否則DLL 可能無法被程式成功載入(會彈出錯誤提示訊息框)。有一個叫做 AheadLib 的工具可以十分方便的生成此類 DLL 的原始檔。

    #!bash
    0 fltmgr.sys FltAcquirePushLockShared + 0x907
    1 fltmgr.sys FltIsCallbackDataDirty + 0x1f3d
    2 fltmgr.sys FltDeletePushLock + 0x64f
    3 ntoskrnl.exe MmCreateSection + 0x25b1
    4 ntoskrnl.exe SeQueryInformationToken + 0xe3e
    5 ntoskrnl.exe ObOpenObjectByName + 0x306
    6 ntoskrnl.exe NtOpenProcessTokenEx + 0x326
    7 ntoskrnl.exe KeSynchronizeExecution + 0x3a23
    8 ntdll.dll ZwQueryAttributesFile + 0xa
    9 wow64.dll Wow64EmulateAtlThunk + 0xd2b1
    10 wow64.dll Wow64SystemServiceEx + 0xd7
    11 wow64cpu.dll TurboDispatchJumpAddressEnd + 0x2d
    12 wow64.dll Wow64SystemServiceEx + 0x1ce
    13 wow64.dll Wow64LdrpInitialize + 0x42a
    14 ntdll.dll RtlUniform + 0x6e6
    15 ntdll.dll EtwEventSetInformation + 0x186f8
    16 ntdll.dll LdrInitializeThunk + 0xe
    17 ntdll.dll ZwQueryAttributesFile + 0x12
    18 ntdll.dll aullrem + 0x1f1
    19 ntdll.dll aullrem + 0x6cb
    20 ntdll.dll aullrem + 0x565
    21 ntdll.dll RtlEncodeSystemPointer + 0x404
    22 ntdll.dll RtlSetBits + 0xf0
    23 ntdll.dll RtlSetBits + 0x16b
    24 ntdll.dll RtlSetBits + 0x60
    25 ntdll.dll RtlSetThreadPoolStartFunc + 0x3a1
    26 ntdll.dll RtlSetUnhandledExceptionFilter + 0x50
    27 ntdll.dll LdrInitializeThunk + 0x10
    
  • npp.6.9.Installer.exe 在嘗試載入 SHFOLDER.dll 時產生的呼叫棧中存在有LoadLibrary(Ex),說明這個 DLL 是被程式所動態載入的。在這種利用場景下,偽造的 DLL檔案不需要存在任何匯出函式即可被成功載入,即使載入後程式內部出錯,也是在 DLL 被成功載入之後的事情。

    #!bash
    0 fltmgr.sys FltAcquirePushLockShared + 0x907
    1 fltmgr.sys FltIsCallbackDataDirty + 0x1f3d
    2 fltmgr.sys FltDeletePushLock + 0x64f
    3 ntoskrnl.exe MmCreateSection + 0x25b1
    4 ntoskrnl.exe SeQueryInformationToken + 0xe3e
    5 ntoskrnl.exe ObOpenObjectByName + 0x306
    6 ntoskrnl.exe NtOpenProcessTokenEx + 0x326
    7 ntoskrnl.exe KeSynchronizeExecution + 0x3a23
    8 ntdll.dll ZwQueryAttributesFile + 0xa
    9 wow64.dll Wow64EmulateAtlThunk + 0xd2b1
    10 wow64.dll Wow64SystemServiceEx + 0xd7
    11 wow64cpu.dll TurboDispatchJumpAddressEnd + 0x2d
    12 wow64.dll Wow64SystemServiceEx + 0x1ce
    13 wow64.dll Wow64LdrpInitialize + 0x42a
    14 ntdll.dll RtlUniform + 0x6e6
    15 ntdll.dll EtwEventSetInformation + 0x186f8
    16 ntdll.dll LdrInitializeThunk + 0xe
    17 ntdll.dll ZwQueryAttributesFile + 0x12
    18 ntdll.dll aullrem + 0x1f1
    19 ntdll.dll aullrem + 0x6cb
    20 ntdll.dll aullrem + 0x565
    21 ntdll.dll RtlLookupAtomInAtomTable + 0x35a
    22 ntdll.dll RtlUlonglongByteSwap + 0x671
    23 KernelBase.dll LoadLibraryExW + 0x243
    24 KernelBase.dll LoadLibraryExA + 0x26
    25 kernel32.dll LoadLibraryA + 0x31
    

1.4 Microsoft Edge 與 Google Chrome 的自動下載漏洞

透過 iframe 可以觸發 Microsoft Edge 和 Google Chrome 的自動下載功能,這一特性被@HaifeiLi認為是一個安全漏洞,其在 Twitter 上發表了很多關於該漏洞的推文,甚至抱怨 Chrome 和 Edge 團隊忽視這個漏洞的存在。在 HaifefiLi 的長期呼籲下, Chrome 最終在 48.0.2564.82 版本中修復了這個漏洞,而截至筆者撰文時 Edge 似乎仍然沒有修復該漏洞。

Edge 瀏覽器的預設下載目錄為C:\Users\<Username>\Downloads,透過 Edge 下載的檔案預設都會儲存在這個目錄下。可以利用 Edge 的自動下載漏洞下載一個惡意的 DLL 檔案(如Version.dll )到這個目錄下,然後利用頁面超時自動跳轉功能讓 Edge 跳轉到正常頁面來誘導使用者下載一個正常的安裝檔案,當使用者執行安裝程式時惡意的 DLL 檔案便會被程式載入。

測試瀏覽器自動下載漏洞的 HTML 測試程式碼如下所示:

#!html
<html>
    <head>
        <title>Windows Update</title>
    </head>
    <body>
        <iframe src="evil.dll"></iframe>
        <script>
            setTimeout( function () {
                    window.location = "https://get.adobe.com/reader"
                }, 5000);
        </script>
    </body>
</html>

在 Windows 10 下使用 Edge 開啟這個 HTML 頁面,可以看到 DLL 檔案被自動下載到了本地的下載目錄中。不過由於 DLL 沒有有效的數字簽名,所以 Edge 會提示這個檔案可能存在風險。

p2

如果 DLL 檔案具有有效的數字簽名,那麼 Edge 就不會這樣提示了。在最新版本的 GoogleChrome ( 48.0.2564.116 m )上測試發現,不管 DLL 是否具有有效的數字簽名, DLL 檔案下載之後都需要使用者手工確認才會儲存,否則會被刪除。 Chrome 和 Edge 的測試結果彙總如下:

p3

瀏覽器的自動下載漏洞還是十分危險的,攻擊者甚至只需要誘導使用者下載一個惡意的 DLL ,以後使用者在下載目錄中執行各種程式時都有可能載入這個 DLL 。此外,安裝程式一般都會請求管理員許可權,對於惡意的 DLL 來說管理員許可權似乎是與生俱來的。

0x02 非典型漏洞 CVE-2016-0041 分析


微軟安全公告 MS16-014 中的描述表明其修復了一個 CVE-ID 為 CVE-2016-0041 的 DLL 劫持漏洞。漏洞詳情為: Windows 10 下的 URLMON.dll 檔案存在載入 phoneinfo.dll 的程式碼,而 Windows 10本身並不攜帶這個 phoneinfo.dll 檔案,並且在查詢 DLL 時使用的是標準的目錄搜尋順序,所以這裡會導致 DLL 劫持漏洞。這個漏洞的獨特之處在於其存在於作業系統本身,所以在 Windows 10下,只要是呼叫了 URLMON.dll 中能夠觸發漏洞程式碼的 API 的軟體都會受到這個漏洞的影響。筆者在 2015 年底也發現了也發現了這個漏洞,同時確認 Foxit Reader 7.2.8.1124 受到該漏洞的影響,並將其報告給了 Foxit Software 。

2.1 漏洞分析

在發現這個漏洞時,筆者發現網上很少有關於 phoneinfo.dll 檔案的介紹,只有@tombkeeperSexrets of LoadLibrary中提到了這個檔案。 TK 指出 IE11 running on Windows 10 TP 9926 會嘗試載入 phoneinfo.dll ,而 IE 的當前目錄就是桌面,所以如果放一個 phoneinfo.dll 到桌面上的話,在啟動 IE 時這個 DLL 便會被載入。

Greg Linares 在 SRT-VR-24DEC2015 中指出 Windows10 的 URLMON.dll 中存在兩處載入phoneinfo.dll 的地方,可能是 DLL 檔案的版本不一樣,筆者找到的程式碼與之存在一些細微差異。筆者在分析 11.0.10240.16384 版本的 URLMON.dll 時找到的反彙編程式碼如下所示:

  • 下面的程式碼位於BuildUserAgentStringMobileHelper中:

    #!bash
    .text:1A4636A1 loc_1A4636A1:
    .text:1A4636A1 mov eax, pfnQueryPhoneInformation
    .text:1A4636A6 mov [ebp+pszValue], 0
    .text:1A4636AD mov [ebp+szSrc], eax
    .text:1A4636B3 test eax, eax ;  判斷 eax 暫存器的值是否為 0
    .text:1A4636B5 jnz loc_1A48FC97 ;  如果不為 0 則跳轉
    .text:1A4636BB push eax ; dwFlags = 0
    .text:1A4636BC push eax ; hFile = 0
    .text:1A4636BD push offset aPhoneinfo_dll ; "phoneinfo.dll"
    .text:1A4636C2 call ds:[email protected] ; LoadLibraryExW(x,x,x)
    .text:1A4636C8 test eax, eax
    .text:1A4636CA jnz loc_1A48FC78
    
  • 下面的程式碼位於_QueryPhoneInformationA中:

    #!bash
    .text:1A461B93 mov edi, pfnQueryPhoneInformation
    .text:1A461B99 mov byte ptr [ebx], 0
    .text:1A461B9C test edi, edi
    .text:1A461B9E jnz loc_1A48EE56
    .text:1A461BA4 push edi ; dwFlags
    .text:1A461BA5 push edi ; hFile
    .text:1A461BA6 push offset aPhoneinfo_dll ; "phoneinfo.dll"
    .text:1A461BAB call ds:[email protected] ; LoadLibraryExW(x,x,x)
    .text:1A461BB1 test eax, eax
    .text:1A461BB3 jnz loc_1A48EE34
    

這裡載入 phoneinfo.dll 的程式碼為 LoadLibraryExW("phoneinfo.dll", NULL, 0) 。因為這 裡 dwFlags 的值為 0 ,所以使用標準的 DLL 搜尋順序;由於 Windows 10 上並不存在 phoneinfo.dll 這個檔案,所以程式最終會嘗試載入當前目錄下的 DLL 。

這裡簡單分析一下受該漏洞影響的 Foxit Reader 。當在 Windows 10 下開啟一個 PDF 檔案時,程式 FoxitReader.exe 會載入當前目錄下的 phoneinfo.dll 檔案,對應的呼叫棧如下所示:

#!bash
......
16  KernelBase.dll LoadLibraryExW + 0x124
17  urlmon.dll Ordinal523 + 0x6f1
18  urlmon.dll Ordinal492 + 0x941
19  urlmon.dll Ordinal492 + 0x165
20  urlmon.dll Ordinal445 + 0x2e0
21  urlmon.dll RegisterFormatEnumerator + 0xe2
22  urlmon.dll UrlMkGetSessionOption + 0xcf
23  FoxitReader.exe CertFreeCertificateChainEngine + 0x72fbef
24  FoxitReader.exe CertFreeCertificateChainEngine + 0x70bbc2
25  FoxitReader.exe CertFreeCertificateChainEngine + 0x70f61e
26  FoxitReader.exe CertFreeCertificateChainEngine + 0x6f9f9c
27  user32.dll Ordinal2535 + 0x83
28  user32.dll GetScrollInfo + 0x1e8
29  user32.dll DispatchMessageW + 0x28d
30  user32.dll DispatchMessageW + 0x10
31  FoxitReader.exe FoxitReader.exe + 0x2d1f1b
32  FoxitReader.exe FoxitReader.exe + 0x2da62d
33  FoxitReader.exe FoxitReader.exe + 0x882a36
34  FoxitReader.exe FoxitReader.exe + 0x1866d1
35  FoxitReader.exe FoxitReader.exe + 0x173286
36  FoxitReader.exe FoxitReader.exe + 0x173563
37  FoxitReader.exe FoxitReader.exe + 0x21483f
38  FoxitReader.exe FoxitReader.exe + 0x217726
39  FoxitReader.exe FoxitReader.exe + 0x1e4922
40  FoxitReader.exe FoxitReader.exe + 0x1f4aba
41  FoxitReader.exe FoxitReader.exe + 0x1e8d6a
42  FoxitReader.exe FoxitReader.exe + 0x1eb562
43  FoxitReader.exe CertFreeCertificateChainEngine + 0x91e56c
44  FoxitReader.exe FoxitReader.exe + 0x46cd8e
45  kernel32.dll BaseThreadInitThunk + 0x24
46  ntdll.dll RtlInitializeCriticalSectionAndSpinCount + 0x29e
47  ntdll.dll RtlInitializeCriticalSectionAndSpinCount + 0x26d

結合 IDA 或者 Windbg 進行分析,可以知道這裡的呼叫路徑為:

UrlMkGetSessionOption
└--> GetUserAgentString
     └--> GetUserAgentStringForMode
          └--> InitUserAgentGlobals
               └--> BuildUserAgentStringMobileHelper
                    └-->LoadLibraryExW

即 Foxit Reader 因為呼叫了 URLMON.dll 中的 UrlMkGetSessionOption 函式,導致其受到 DLL 劫持漏 洞的影響。在 IDA 中使用交叉引用功能進行回溯,可以找到其他能夠觸發該漏洞的路徑,Greg Linares給出了另外兩個路徑:

  • 路徑1

    ┌─────────────────────────────────────────────────────┐
    │          CINetHttpEdge::SetOptionUserAgent          │
    │             CINetHttp::SetOptionUserAgent           │
    │ CIEBrowserModeFilter::collectCacheEntryInfoCallback │
    └─────────────────────────────────────────────────────┘
    └--> MapBrowserEmulationStateToUserAgent (Ordinal 445)
         └--> InitUserAgentGlobals (Ordinal 492)
              └--> BuildUserAgentStringMobileHelper
    
  • 路徑2

    ┌─────────────────────────────────────┐
    │ ObtainUserAgentString (Ordinal 211) │
    │         GetUserAgentString          │
    └─────────────────────────────────────┘
    └--> InitUserAgentGlobals (Ordinal 492)
        └--> BuildUserAgentStringMobileHelper
    

Greg Linares同時也指出了他們發現的其他受該漏洞影響的軟體:

  • Internet Explorer 沒有命令列引數的情況下(例如雙擊並開啟 IE )
  • Skype 啟動的時候
  • OneDrive 同步以及更新的時候(無需使用者互動)
  • Visual Studio 2015 微軟賬戶更新或者同步的時候

2.2 補丁分析

更新後的 URLMON.dll 檔案在呼叫 LoadLibraryEx 載入 phoneinfo.dll 時將 dwFlags 引數值指定為 0x800 ,即LOAD_LIBRARY_SEARCH_SYSTEM32,表示只搜尋 System32 目錄。對應的程式碼為LoadLibraryExW(L"phoneinfo.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32),反匯 編程式碼如下:

#!bash
.text:1A46386B push 800h ; dwFlags
.text:1A463870 push eax ; hFile
.text:1A463871 push offset aPhoneinfo_dll ; "phoneinfo.dll"
.text:1A463876 call ds:[email protected] ; LoadLibraryExW(x,x,x)

0x03 DLL 劫持漏洞緩解措施


DLL 劫持漏洞在未來可能仍然會影響著許多軟體或者作業系統元件,亦或是與其他漏洞相結合以 衍生出新的攻擊方法。儘管目前沒有一個完美的方法( No Silver Bullet )可以防止軟體受到 DLL 劫持漏洞的影響,但是開發人員仍然可以採取各種措施來緩解 DLL 劫持漏洞帶來的影響。

3.1 基本緩解措施

  • 在載入 DLL 時儘量使用 DLL 的絕對路徑
  • 呼叫 SetDllDirectory(L"")當前目錄 從 DLL 搜尋目錄中排除
  • 使用 LoadLibraryEx 載入 DLL 時,指定 LOAD_LIBRARY_SEARCH_ 系列標誌

此外,程式也可以嘗試去驗證 DLL 的合法性,例如是否具有自家的合法數字簽名、是否是合法的系統 DLL 檔案等。

3.2 Windows Edge 緩解措施

最新版本的 Edge 提供了一種對抗 DLL 劫持(注入)的緩解措施:只有擁有微軟簽名以及 WHQL ( Windows Hardware Quality Lab )簽名的 DLL 模組才會被 Edge 載入,而且這套機制是在 作業系統核心中實現的。

關於這一緩解措施的細節分析,可以閱讀 Paul Rascagneres 的文章 MICROSOFT EDGE BINARY INJECTION MITIGATION OVERVIEW

0x04 Acknowledges


感謝TK在行文思路上的建議;同時,在本文的寫作過程中參考了以下資料,在此亦表示感謝。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章