无法安装的Windows更新列表

您是否想知道为什么会生成已安装的Windows更新列表? 并通过哪个API获取呢? 我将在我的小型研究中尝试回答这些问题以及其他新出现的问题。



背景或一切的开始。


每年我们公司都会召开一次年轻专家会议,每个参与者都可以解决一个部门的问题(预先提出主题列表)。 SPAS的部门(软件和硬件的维护)具有以下任务,这使我感兴趣,此外,它使再次返回编程成为可能(不幸的是,此刻,我在这家公司工作时只是一个NPPS的简单运营商)。

以前,在WSUS的帮助下,对于每个“ TO”,所有发布的更新都被提取并分发到所有计算机。 TSB(技术服务公告)也定期出现,这表明需要以隔离软件包的形式安装必要的更新。 结果,我们累积了无法在WSUS中跟踪的更新,而这些更新只能通过“已安装的更新”部分中的控制面板看到。



视觉更新方案

在某些情况下,工作站或服务器“崩溃”,您必须从前一段时间创建的映像中还原它。 从映像恢复时,我们可能会丢失在计算机崩溃之前安装的所需更新(以隔离软件包的形式出现)。 尽可能详细地解释,因为澄清已经是商业秘密。

这就是为什么这个想法出现的原因是创建一个程序,该程序可以提取此更新列表(最好是通过本地网络远程 ),写入文件/数据库,将当前列表与特定模板进行比较并通过SNMP,OPC等协议之一将消息发送到SCADA系统。

正如您可能从文章标题中猜到的那样,选择列表检索方法已经是一项艰巨的任务。 像往常一样,我决定在搜索引擎中搜索正确的一个,在专用资源上提出问题( 一个两个 ,出于某种原因,英语的stackoverflow不喜欢我的问题,因此必须删除),但是所有答案都没有得到理想的结果。 因此,我必须自己弄清楚,稍后将进行讨论。

控制台命令


让我们从一个简单的例子开始,充分利用Windows提供的功能,而无需使用第三方工具。 可以使用以下命令完成此操作:

  • WMIC QFE清单
  • 系统信息
  • Dism /在线/获取软件包
  • 通过PowerShell:

    • 获取修补程序
    • Get-SilWindowsUpdate (仅在服务器版本中可用)
    • Get-WmiObject-类win32_quickfixengineering-通过访问WMI类win32_quickfixengineering(稍后再介绍WMI)



您可以通过控制面板“添加/删除程序”的标准项通过图形界面获得列表,但我们无法从此处复制任何内容。 每个控制面板工具由Windows \ System文件夹中的.cpl文件表示。 控制面板启动时,Windows系统文件夹中的.Cpl文件将自动下载。 Appwiz.cpl文件负责“程序”项。 他的分析没有得出任何结果。

可以将console命令的输出重定向到一个文件,然后可以对其进行解析,但这是错误的,再加上一个程序调用(根据安全理事会的规定将不起作用),而且毫无疑问可以远程接收该列表。 因此,我建议您只需调用命令,通过控制面板将每个列表中的更新数量与列表进行比较,然后继续我们的调查。

正式地,获取更新列表的所有方法可以分为两组:本地和网络。

本地和网络获取信息的方法

所有方法都在带有集成更新的干净系统映像(Windows 7、8,Server 2012 R2)上进行了测试,在从官方Microsoft服务器通过更新中心进行的每次更新之后,都进行了附加检查。 让我们更详细地介绍它们。

用水户协会


WUApi (Windows Update代理API)-使用Windows Update代理API。 最明显的选择,其名称说明一切。 我们将为此使用Wuapi.dll库。
注意:以下,为方便起见,我将所有结果嵌入到列表中。 这可能不是合理的,但是对我而言,这似乎是个好主意。
实施实例
using WUApiLib; public static List<string> listUpdateHistory() { //WUApi List<string> result = new List<string>(200); try { UpdateSession uSession = new UpdateSession(); IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher(); uSearcher.Online = false; ISearchResult sResult = uSearcher.Search("IsInstalled=1 And IsHidden=0"); string sw = "   WUApi: " + sResult.Updates.Count; result.Add(sw); foreach (WUApiLib.IUpdate update in sResult.Updates) { result.Add(update.Title); } } catch (Exception ex) { result.Add("-   : " + ex.Message); } return result; } 


此方法的第二种变化是: 更新会话 -通过连接到Windows Update代理更新会话来接收信息(在这种情况下,我们不直接与该库一起使用)。

实施实例
 public static List<string> Sessionlist(string pc) { List<string> result = new List<string>(50); //    object sess = null; object search = null; object coll = null; try { sess = Activator.CreateInstance(Type.GetTypeFromProgID("Microsoft.Update.Session", pc)); search = (sess as dynamic).CreateUpdateSearcher(); int n = (search as dynamic).GetTotalHistoryCount(); int kol = 0; //coll = (search as dynamic).QueryHistory(1, n); coll = (search as dynamic).QueryHistory(0, n); result.Add("  Update.Session: " + n); foreach (dynamic item in coll as dynamic) { if (item.Operation == 1) result.Add(item.Title); kol++; //Console.WriteLine(": " + kol); } result.Add("  : " + kol); } catch (Exception ex) { result.Add("-   : " + ex.Message); } finally { if (sess != null) Marshal.ReleaseComObject(sess); if (search != null) Marshal.ReleaseComObject(search); if (coll != null) Marshal.ReleaseComObject(coll); } return result; } 


微软建议远程使用该API

这两种方法的主要缺点是它们不允许查找未通过Windows Update分发的KB修补程序。 您只能看到更新代理本身经历了什么,也就是说,此选项不适合我们。

DISM


部署映像服务和管理是一个命令行工具,可用于服务Windows映像或准备Windows预安装环境(Windows PE)的映像。 它替代了程序包管理器(Pkgmgr.exe),PEimg和Intlcfg。

该实用程序用于将更新,Service Pack集成到系统映像中。 Windows更新是单独的模块,可以通过几种方式呈现:

  • .cab文件(内阁)-存档。 设计用于以自动模式使用Windows Update模块进行分发和安装;
  • .msu文件(Microsoft Update独立软件包)-可执行文件。 专为用户自己通过Microsoft更新目录以手动模式分发和安装而设计。 实际上,它们是由.cab-、. xml和.txt文件组成的打包集。

前面提到的dism / online / get-packages命令显示有关wim映像/当前系统中所有软件包的基本信息。 Microsoft已经照顾了我们,并提供了NuGet软件包以方便使用API​​。

实施实例
 using Microsoft.Dism; public static List<string> DISMlist() { List<string> result = new List<string>(220); try { DismApi.Initialize(DismLogLevel.LogErrors); var dismsession = DismApi.OpenOnlineSession(); var listupdate = DismApi.GetPackages(dismsession); int ab = listupdate.Count; //Console.WriteLine("   DISM: " + ab); string sw = "   DISM: " + ab; result.Add(sw); foreach (DismPackage feature in listupdate) { result.Add(feature.PackageName); //result.Add($"[ ] {feature.PackageName}"); //result.Add($"[ ] {feature.InstallTime}"); //result.Add($"[ ] {feature.ReleaseType}"); } } catch (Exception ex) { result.Add("-   : " + ex.Message); } return result; } 


更新数量与“控制面板”列表中的数量一致,直到通过控制中心进行的第一次更新-之后,更新数量变少了(它是214,变成了209),尽管从逻辑上讲应该增加。 输出示例更新前,更新

这是什么原因,我只能推测-也许某些更新替代了以前的更新,因此数量减少了。

过了一会儿,我遇到了中文DISM ++的实用程序,该实用程序不基于DISM API或DISM Core API,但是它的库没有我需要打开的方法,因此我放弃了这个想法并继续进行进一步的搜索。

华硕


Windows Server Update Services( WSUS )是用于更新操作系统和Mi​​crosoft产品的服务器。 更新服务器与Microsoft网站同步,下载可以在公司LAN内分发的更新。 同样,一个专门用于更新的专用工具。

仅在Windows的服务器版本上分发,因此部署了以下支架:

  • 主系统是Windows Server 2016;
  • 通过Hyper-V虚拟化系统,部署了两个客户端操作系统:
    • Windows 8.1
    • Windows 7


所有系统都连接到单个虚拟局域网,但不能访问Internet

一些技巧
为了不为新系统分配硬盘分区,我使用WinNTSetup并将系统安装在VHD磁盘中-从Windows 7(专业版/旗舰版)开始的引导加载程序在从磁盘映像引导方面做得很好。 这样获得的磁盘可以在Hyper-V中安全使用-您一次用一块石头杀死了两只鸟。 请记住,只需通过bcdedit / export e:\ bcd_backup.bcd命令预先复制BCD存储库。

我不想配置用于分发更新的AD,因此我只是在组策略中注册了WSUS服务器的路径:

设定值

由于输入错误(8350而不是8530),请务必注意端口,尽管一切都已正确完成,但我无法在客户端计算机上收到更新。 此外,Windows 7和Windows 8上的组策略中项目的名称也不同。

要使用WSUS接收报告,您必须另外安装软件包-系统将通知您。

现在有一点代码
 //      using Microsoft.UpdateServices.Administration; public static List<string> GetWSUSlist(params string[] list) { List<string> result = new List<string>(200); //    string namehost = list[0]; // ,     string = "example1"; string servername = list[1]; //  string = "WIN-E1U41FA6E55"; string Username = list[2]; string Password = list[3]; try { ComputerTargetScope scope = new ComputerTargetScope(); IUpdateServer server = AdminProxy.GetUpdateServer(servername, false, 8530); ComputerTargetCollection targets = server.GetComputerTargets(scope); // Search targets = server.SearchComputerTargets(namehost); // To get only on server FindTarget method IComputerTarget target = FindTarget(targets, namehost); result.Add(" : " + target.FullDomainName); IUpdateSummary summary = target.GetUpdateInstallationSummary(); UpdateScope _updateScope = new UpdateScope(); // See in UpdateInstallationStates all other properties criteria //_updateScope.IncludedInstallationStates = UpdateInstallationStates.Downloaded; UpdateInstallationInfoCollection updatesInfo = target.GetUpdateInstallationInfoPerUpdate(_updateScope); int updateCount = updatesInfo.Count; result.Add(" -   - " + updateCount); foreach (IUpdateInstallationInfo updateInfo in updatesInfo) { result.Add(updateInfo.GetUpdate().Title); } } catch (Exception ex) { result.Add("-   : " + ex.Message); } return result; } public static IComputerTarget FindTarget(ComputerTargetCollection coll, string computername) { foreach (IComputerTarget target in coll) { if (target.FullDomainName.Contains(computername.ToLower())) return target; } return null; } 


由于没有互联网,因此出现更新情况,如以下屏幕截图所示:



行为与WUApi类似-如果更新未通过更新,则他们不知道。 因此,此方法不再起作用。

Wmi


直译的Windows Management Instrumentation( WMI )是Windows管理工具箱。

WMI是Microsoft实施的标准,用于通过Internet管理企业以集中管理和监视运行Windows平台的计算机基础结构的各个部分。 WMI是访问接口的开放统一系统,可以访问其中运行的操作系统,设备和应用程序的任何参数。

此方法使您既可以从本地计算机接收数据,也可以从本地网络远程接收数据。 要访问WMI对象,使用特定的WMI查询语言(WQL),它是SQL的一种。 我们将通过WMI类win32_quickfixengineering接收列表。

实施实例
 using System.Management; public static List<string> GetWMIlist(params string[] list) { List<string> result = new List<string>(200); //    ManagementScope Scope; string ComputerName = list[0]; string Username = list[1]; string Password = list[2]; int kol = 0; if (!ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase)) { //    ,      //  . ConnectionOptions Conn = new ConnectionOptions(); Conn.Username = Username; Conn.Password = Password; //      «NTLMDOMAIN:»  NTLM  ,       NTLM. Conn.Authority = "ntlmdomain:DOMAIN"; Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), Conn); } else Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null); try { Scope.Connect(); ObjectQuery Query = new ObjectQuery("SELECT * FROM Win32_QuickFixEngineering"); ManagementObjectSearcher Searcher = new ManagementObjectSearcher(Scope, Query); foreach (ManagementObject WmiObject in Searcher.Get()) { result.Add(WmiObject["HotFixID"].ToString()); //Console.WriteLine("{0,-35} {1,-40}", "HotFixID", WmiObject["HotFixID"]);// String //result.Add(); /*result.Add("{0,-17} {1}", " : ", WmiObject["Description"]); result.Add("{0,-17} {1}", ": ", WmiObject["Caption"]); result.Add("{0,-17} {1}", " : ", WmiObject["InstalledOn"]);*/ kol++; } result.Add("  " + kol); } catch (Exception ex) { result.Add("-   : " + ex.Message); } return result; } 


从数量上讲,所有内容都是重合的(即使在更新后也是如此),因此决定使用此方法。 对于以编程方式创建WMI请求,我建议您使用以下实用程序-WMI Delphi Code Creator 。 多亏了她,我对我的代码有所不同,并决定使用此程序中的空白。

XML格式


通过WMI方法获得的数据并没有阻止我,因此我决定进行“表面逆向工程”。 我们将使用Sysinternals Suite软件集中的Process Monitor实用程序来识别在调用上面列出的控制台命令并通过“控制面板”访问“已安装的更新”项时使用的文件和注册表分支。

我的注意力吸引到位于C:\ Windows \ serviceic \ Packages \文件夹中的wuindex.xml文件。 为了对其进行分析,编写了以下程序:

控制台应用程序示例
 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Text.RegularExpressions; using System.IO; namespace XMLviewer { class Program { static void Main(string[] args) { string writePath = AppDomain.CurrentDomain.BaseDirectory + "XML  " + Environment.MachineName + ".txt"; if (!File.Exists(writePath)) { Console.WriteLine("  txt "); } else { Console.WriteLine(" XML .txt ,   "); File.Delete(writePath); } //      KB Regex regex = new Regex(@"KB[0-9]{6,7}"); //Regex(@"(\w{2}\d{6,7}) ?"); //SortedSet    ,     ""     SortedSet<string> spisok = new SortedSet<string>(); XmlDocument xDoc = new XmlDocument(); string path = "C:\\Windows\\servicing\\Packages\\wuindex.xml"; //   xml xDoc.Load(path); int kol = 0; //-  int total = 0; //-    xml int total2 = 0; //-   XmlNodeList name = xDoc.GetElementsByTagName("Mappings"); foreach (XmlNode xnode in name) { //Console.WriteLine(xnode.Name); kol++; XmlNode attr = xnode.Attributes.GetNamedItem("UpdateId"); //Console.WriteLine(attr.Value); foreach (XmlNode childnode in xnode.ChildNodes) { XmlNode childattr = childnode.Attributes.GetNamedItem("Package"); total++; //Console.WriteLine(childattr.Value); MatchCollection matches = regex.Matches(childattr.Value); if (matches.Count > 0) { foreach (Match match in matches) //Console.WriteLine(match.Value); spisok.Add(match.Value); } else { //Console.WriteLine("  "); } } } try { StreamWriter sw = new StreamWriter(writePath); foreach (string element in spisok) { //Console.WriteLine(element); sw.WriteLine(element); total2++; } sw.Close(); } catch (Exception ex) { Console.WriteLine(": " + ex.Message); } //Console.WriteLine("\n"); Console.WriteLine(" : " +kol); Console.WriteLine("    xml: " + total); Console.WriteLine(" KB : " + total2); Console.WriteLine("    ."); Console.Read(); } } } 


不幸的是,该文件并非在所有系统上都可以找到,其生成和更新的原理仍然是我的一个谜。 因此,这种方法也不适合我们。

立方英尺


在这里,我们来探讨所有这些方法的关联。 继续分析Process Monitor日志,我确定了以下文件夹和文件。

位于C:\ Windows \ SoftwareDistribution \ DataStore文件夹中的DataStore.edb文件 。 这是一个数据库,其中包含Windows安装版本的所有更新的历史记录,包括仅排队的更新。

ESEDatabaseView程序用于分析DataStore.edb文件。 数据库中有一个tbUpdates表,其内容难以解释。

ESEDatabaseView中的TbUpdates表

引起我注意TiWorker.exe进程后,每次我在“控制面板”中打开一个项目时都会调用该进程。 他“浏览”了许多文件夹,其中之一使我走上了正轨。

C:\ Windows \ SoftwareDistribution是Windows Update用于将更新下载到计算机并安装它们的文件夹,它还存储有关所有以前安装的更新的信息。

位于C:\ Windows \ winsxs的 WinSxS文件夹。 这是Windows操作系统的服务文件夹,用于存储以前安装的系统组件版本。 由于存在更新,因此如有必要,可以回滚到更新的旧版本。

C:\ Windows \ serviceic-整个系统的主要组件,名称为基于组件的服务(CBS)

CBS是Windows的一部分,是基于组件的服务,并且与Windows Update服务集成在一起。 与基于文件的服务(FBS)服务 (适用于Windows Vista之前的操作系统)不同,在文件服务中直接在系统目录中更新文件,而CBS则引入了整个目录层次结构以及模块/服务库的整个系列(堆栈)。

CbsApi.dll是主要的CBS技术支持库。 它没有开放的方法,所以我不能直接使用它。 Microsoft使用TrustedInstaller.exe和TiWorker.exe访问该库的方法,并通过这些过程显示我们需要的数据。 记录保存在C:\ Windows \ Logs \ CBS \ CBS.log中

在创建该程序的原型时(您可以在屏幕截图中看到2019年5月),没有关于CBS的俄语信息,但是在8月底,有一个非常不错的博客文章-http://datadump.ru/component-based-servicing 。 一篇非常有趣的文章证实了我的经验并收集了必要的信息。 有关该主题的更多信息: http : //www.outsidethebox.ms/17988/

结论


Microsoft太复杂了获取更新列表的琐碎任务,并使此过程不十分明显。 所有这些都是为了安全起见,而不是为了易于使用。 我同意本文的作者-在接收更新时开始缺乏可预测性和透明度。

作为研究的结果,编写了以下程序 ,可以在此视频中看到其演示:


计划添加:

  1. 将必要更新列表与接收到的更新进行比较;
  2. 通过SNMP / OPC发送结果(如果有经验,请在评论中分享);
  3. 从指定的文件夹组织丢失的“脱机”更新的安装。

如果您知道更多不仅可以获取更新列表,而且还可以获取其他组件(Adobe Flash,Acrobat Reader等)列表的方法,或者您还有其他有趣的建议,请在评论或私人消息中写上它-我将很高兴收到任何反馈。 并参与本文的调查-因此,我将知道我对Habrahabr受众的经历是否会很有趣。

Source: https://habr.com/ru/post/zh-CN467445/


All Articles