探索 dotnet core 为何在 Windows7 系统需要补丁的原因
在一些 Windows 7 系統(tǒng)上,根據(jù) dotnet 官方文檔,需要安裝上 KB2533623 補(bǔ)丁,才能運(yùn)行 dotnet core 或 .NET 5 等應(yīng)用。盡管非所有的設(shè)備都需要安裝此,但這也讓應(yīng)用的分發(fā)不便,安裝包上都需要帶上補(bǔ)丁給用戶安裝。此補(bǔ)丁同時(shí)也要求安裝完成之后重啟系統(tǒng),這對(duì)用戶端來說,也是較不方便。本文來聊聊為什么 dotnet core 一系的框架依賴于此補(bǔ)丁
特別感謝?lsj?給我講解 Win32 調(diào)用部分的知識(shí)和幫我調(diào)查具體的原因,我只是記錄的工具人
補(bǔ)丁
開始之前,先來理一下所需補(bǔ)丁的情況,不想看補(bǔ)丁細(xì)節(jié)還請(qǐng)?zhí)较挛闹黝}這章。準(zhǔn)確來說,在當(dāng)前 2021.12.25 官方推薦 Win7 系統(tǒng)打上的補(bǔ)丁是 KB3063858 補(bǔ)丁
KB3063858: MS15-063:Windows 內(nèi)核中的漏洞可能允許特權(quán)提升:2015 年 6 月 9 日 此安全更新可解決 Windows 中的一個(gè)漏洞。如果用戶訪問包含特殊設(shè)計(jì)的文件的網(wǎng)絡(luò)共享(或指向網(wǎng)絡(luò)共享的網(wǎng)站)時(shí),此漏洞可能允許特權(quán)提升。但是在所有情況下,攻擊者都無法強(qiáng)制用戶訪問此類網(wǎng)絡(luò)共享或網(wǎng)站。
也有伙伴推薦給我的是安裝 KB4457144 補(bǔ)丁。但是 KB4457144 補(bǔ)丁太大了,包含太多內(nèi)容,帶上這個(gè)補(bǔ)丁沒什么優(yōu)勢
KB4457144: 2018 年 9 月 11 日 - KB4457144(月度匯總)
在 GitHub 上的 Security update KB2533623 no longer available · Issue #20459 · dotnet/docs 討論上,有大佬表示 KB3063858 或 KB4457144 包含了 KB2533623 補(bǔ)丁內(nèi)容:
The thread claims that KB2533623 is superseded by KB3063858 or KB4457144. https://github.com/dotnet/docs/issues/20459#issuecomment-689513876
值得一說的是對(duì)需要安裝 KB3063858 補(bǔ)丁的系統(tǒng)來說,大多數(shù)都需要額外加上證書,參閱 https://www.microsoft.com/pkiops/Docs/Repository.htm 因此我認(rèn)為對(duì)于客戶端分發(fā)來說,打上 KB2533623 補(bǔ)丁似乎更好。但是 KB2533623 當(dāng)前被微軟下架了,請(qǐng)看 Security update KB2533623 no longer available · Issue #20459 · dotnet/docs
這是 KB2533623 的下載地址:?http://www.microsoft.com/download/details.aspx?familyid=c79c41b0-fbfb-4d61-b5d8-cadbe184b9fc
另外,在剛推送 dotnet core 3.0 的預(yù)覽版本時(shí),有伙伴在 WPF 官方倉庫反饋說需要加上 KB2999226 補(bǔ)丁。此 KB2999226 補(bǔ)丁是?Windows 中的 Universal C Runtime 更新?的內(nèi)容,參閱?https://github.com/dotnet/wpf/issues/2009#issuecomment-543872453
也許可以使用?runtime.win7-x64.Microsoft.NETCore.Windows.ApiSets NuGet 庫?代替 KB2999226 補(bǔ)丁內(nèi)容,只需要將?api-xxxxx.dll?這些文件拷貝到輸出路徑即可。或者是解包 VC++ 2015 的分發(fā)包里的文件,將?api-xxxxx.dll?和?ucrtbase.dll?拷貝到輸出路徑即可
因此,對(duì)于客戶端分發(fā)來說,似乎采用 KB2533623 最小補(bǔ)丁,然后在輸出路徑上拷貝好?api-xxxxx.dll?這些文件到輸出路徑是最佳方法
下載地址:
KB2533623 x86
MD5:EDF1D538C85F24EC0EF0991E6B27F0D7
SHA1:25BECC0815F3E47B0BA2AE84480E75438C119859
KB2533623 x64
MD5:0A894C59C245DAD32E6E48ECBDBC8921
SHA1:8A59EA3C7378895791E6CDCA38CC2AD9E83BEBFF
KB3063858 32-bit
KB3063858 64-bit
主題
清理好了各個(gè)補(bǔ)丁的關(guān)系之后,咱回到主題。為什么在 dotnet core 一系都有此要求?而且還不是對(duì)所有 Win7 系統(tǒng)都有此要求,這是為什么?回答這兩個(gè)問題,可以從 dotnet core 的 dotnet host core run 開始聊起
在 Windows 下,咱雙擊運(yùn)行的 dotnet core 的可執(zhí)行 exe 文件,其實(shí)是一個(gè) AppHost 文件。咱編寫的 Main 函數(shù),在非單文件模式下,是放在同名的 dll 里面。詳細(xì)關(guān)于 AppHost 請(qǐng)參閱 dotnet core 應(yīng)用是如何跑起來的 通過AppHost理解運(yùn)行過程
在 dotnet host core run 里,對(duì)應(yīng)代碼是?src\coreclr\hosts\corerun\corerun.hpp?文件,在這里需要拉起?hostpolicy.dll?組件。加載此組件的代碼如下,不過代碼內(nèi)容不重要哈
inline bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value){const char_t* hostpolicyName = W("hostpolicy.dll");pal::mod_t hMod = (pal::mod_t)::GetModuleHandleW(hostpolicyName);if (hMod != nullptr)return true;// Check if a hostpolicy exists and if it does, load it.if (pal::does_file_exist(mock_hostpolicy_value))hMod = (pal::mod_t)::LoadLibraryExW(mock_hostpolicy_value.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);if (hMod == nullptr)pal::fprintf(stderr, W("Failed to load mock hostpolicy at path '%s'. Error: 0x%08x\n"), mock_hostpolicy_value.c_str(), ::GetLastError());return hMod != nullptr;}以上最關(guān)鍵的只有這一行代碼?LoadLibraryExW(mock_hostpolicy_value.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
以上代碼通過 LoadLibraryExW 函數(shù)進(jìn)行加載庫。調(diào)用時(shí)傳入了?LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR?參數(shù)。根據(jù)官方文檔的描述,調(diào)用此函數(shù),如果加入了?LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR?參數(shù),將要求 KB2533623 補(bǔ)丁
If this value is used, the directory that contains the DLL is temporarily added to the beginning of the list of directories that are searched for the DLL’s dependencies. Directories in the standard search path are not searched. The lpFileName parameter must specify a fully qualified path. This value cannot be combined with LOAD_WITH_ALTERED_SEARCH_PATH. For example, if Lib2.dll is a dependency of C:\Dir1\Lib1.dll, loading Lib1.dll with this value causes the system to search for Lib2.dll only in C:\Dir1. To search for Lib2.dll in C:\Dir1 and all of the directories in the DLL search path, combine this value with LOAD_LIBRARY_DEFAULT_DIRS.
Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: This value requires KB2533623 to be installed. Windows Server 2003 and Windows XP: This value is not supported
除以上邏輯之外,在 dotnet 倉庫里還可以找到其他的各個(gè)部分的 LoadLibraryExW 函數(shù)上,也都帶上了?LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR?參數(shù),列表如下:
src\coreclr\dlls\dbgshim\dbgshim.cpp: CreateDebuggingInterfaceFromVersion2 函數(shù)
src\coreclr\hosts\corerun\corerun.hpp: try_load_hostpolicy 函數(shù)
src\coreclr\hosts\coreshim\CoreShim.cpp: TryLoadHostPolicy 函數(shù)
src\coreclr\vm\nativelibrary.cpp: DetermineLibNameVariations 函數(shù)
src\native\corehost\hostmisc\pal.windows.cpp: load_library 函數(shù)
看起來文件不多,就看哪個(gè)伙伴想不開就去改改挖坑吧
此 LoadLibraryExW 函數(shù)是放在 Kernel32.dll 的,相同的需求,在 dotnet core 里也間接依賴于 AddDllDirectory 和 SetDefaultDllDirectories 和 RemoveDllDirectory 等方法。如官方文檔描述,剛好這些方法也都相同依賴 KB2533623 補(bǔ)丁
Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: To use this function in an application, call GetProcAddress to retrieve the function’s address from Kernel32.dll. KB2533623 must be installed on the target platform.
通過如上描述,可以了解到,在 dotnet core 需要補(bǔ)丁的原因是調(diào)用了 Kernel32.dll 的新(大約10年前加的)函數(shù),對(duì)于一些 Win7 舊設(shè)備上,沒有更新 Kernel32.dll 加上函數(shù)
因此,判斷 dotnet core 所依賴環(huán)境可以有兩個(gè)方法,第一個(gè)方法是判斷 KB2533623 補(bǔ)丁是否有安裝,另一個(gè)方法是判斷 Kernel32.dll 里是否存在 AddDllDirectory 函數(shù)。第一個(gè)方法判斷是不靠譜的,因?yàn)椴灰欢ㄐ枰?KB2533623 補(bǔ)丁,如上文描述,換成其他幾個(gè)補(bǔ)丁也可以。判斷補(bǔ)丁安裝可以使用下面命令行代碼,更多請(qǐng)參閱 dotnet 通過 WMI 獲取系統(tǒng)補(bǔ)丁?和 PowerShell 通過 WMI 獲取補(bǔ)丁
BAT:wmic qfe GET hotfixid | findstr /C:"KB2533623"另外,判斷是否存在 KB2533623 補(bǔ)丁,不存在則安裝的 bat 腳本代碼如下
echo off cd /d %~dp0 wmic qfe GET hotfixid | findstr /C:"KB2533623" if %errorlevel% equ 0 (Echo Patch KB2533623 installed) else ( wusa Windows6.1-KB2533623-x64.msu /quiet /norestart wusa Windows6.1-KB2533623-x86.msu /quiet /norestart exit /b 1 ) exit /b 0第二個(gè)方法我比較推薦,判斷代碼如下:
using PInvoke; using System;namespace AddDllDirectoryDetectCs {class Program{static void Main(string[] args){using (var hModule = Kernel32.LoadLibrary("Kernel32.dll")){if (!hModule.IsInvalid){IntPtr hFarProc = Kernel32.GetProcAddress(hModule, "AddDllDirectory");if (hFarProc != IntPtr.Zero){Console.WriteLine("Either running on Win8+, or KB2533623 is installed");}else{Console.Write("Likely running on Win7 or older OS, and KB2533623 is not installed");}}}// 以下是判斷 Universal C Runtime 的邏輯,可以忽略u(píng)sing (var hModule = Kernel32.LoadLibraryEx("UCRTBASE.dll", IntPtr.Zero, Kernel32.LoadLibraryExFlags.LOAD_LIBRARY_SEARCH_SYSTEM32)){if (!hModule.IsInvalid){Console.WriteLine("UCRT is available - Either running on Win10+ or KB2999226 is installed");}else{Console.WriteLine("UCRT is not available - Likely running on OS older than Win10 and KB2999226 is not installed");}}}} }以上代碼由 WPF 框架官方開發(fā)?Vatsan Madhavan?大佬提供,請(qǐng)看?vatsan-madhavan/checknetcoreprereqs: Helper utility to check .NET Core Prerequisites on Windows systems
收到?Vatsan Madhavan?大佬贊
參考
LoadLibraryExW function (libloaderapi.h) - Win32 apps Microsoft Docs
LoadLibraryExA function (libloaderapi.h) - Win32 apps Microsoft Docs
LoadLibraryW function (libloaderapi.h) - Win32 apps Microsoft Docs
windows - SetDllDirectory does not cascade, so dependency DLLs cannot be loaded - Stack Overflow
SetDllDirectoryA function (winbase.h) - Win32 apps Microsoft Docs
AddDllDirectory function (libloaderapi.h) - Win32 apps Microsoft Docs
Windows installer should warn about missing required updated · Issue #2452 · dotnet/runtime
RemoveDllDirectory function (libloaderapi.h) - Win32 apps
Unable to load DLL ‘wpfgfx_cor3.dll’ or one of its dependencies · Issue #2009 · dotnet/wpf
SqlClient: Unable to load DLL ‘sni.dll’ · Issue #16905 · dotnet/runtime
Failed to bind to coreclr?dotnet run failure on Windows 7 and Windows 2008 R2 · Issue #5590 · dotnet/sdk
API Set Usage Question · Issue #5075 · dotnet/runtime
Cannot load CoreCLR.dll · Issue #5076 · dotnet/runtime
vatsan-madhavan/checknetcoreprereqs: Helper utility to check .NET Core Prerequisites on Windows systems
使用 C# 判斷指定的 Windows 更新是否已安裝-碼農(nóng)很忙
Windows 7 安裝 msu 系統(tǒng)更新時(shí)的常見報(bào)錯(cuò)及解決辦法-碼農(nóng)很忙
Windows 7 安裝 .NET 5 / .NET Core 3.1 環(huán)境的方法和依賴文件-碼農(nóng)很忙
Windows 7 SP 1 部署 .NET 6 Desktop Runtime 桌面運(yùn)行時(shí)會(huì)遇到的問題-碼農(nóng)很忙
總結(jié)
以上是生活随笔為你收集整理的探索 dotnet core 为何在 Windows7 系统需要补丁的原因的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 技术分享 | CodeReview主要R
- 下一篇: 如何在单个测试中同时执行多个断言