From 99a788b2da7d33e9ecc2b03eabf2fa6cd9f99a81 Mon Sep 17 00:00:00 2001 From: Thomas Hounsell Date: Sun, 13 Feb 2022 23:57:47 +0000 Subject: [PATCH] Misc bugfixes; Support for reading EditionMappings.xml; Support for PID in setupp.ini * Update ManagedWimLib to 2.4.0 * Update copyright date * Add support for reading parent editions of virtual SKUs from EditionMappings.xml * Fix base SKU list for filename generation * Switch to OrdinalIgnoreCase - this is the recommended comparison for path names, it should also be slightly quicker. * Fix some errors thrown on real-world Windows ISOs * Where a WIM index doesn't contain a Windows install * No Base SKU available breaks renaming * Missing case insensitive comparison for install.wim name * Add support for detecting OEM / VLK from PID in setupp.ini in I386-style installs --- Windows Build Identifier/Comparer.cs | 8 +- .../InstalledImage/DetectionHandler.cs | 123 ++++++++++++------ .../Identification/MediaHandler.cs | 37 +++--- .../Interfaces/TxtSetupBridge.cs | 52 ++++++-- Windows Build Identifier/Program.cs | 22 ++-- .../Windows Build Identifier.csproj | 2 +- 6 files changed, 160 insertions(+), 84 deletions(-) diff --git a/Windows Build Identifier/Comparer.cs b/Windows Build Identifier/Comparer.cs index ad7bd02..4696325 100644 --- a/Windows Build Identifier/Comparer.cs +++ b/Windows Build Identifier/Comparer.cs @@ -87,7 +87,7 @@ public static void CompareBuilds(string xml1, string xml2) private static string Sanitize(string path1) { - if (path1.Contains(@"installedrepository\", StringComparison.InvariantCultureIgnoreCase)) + if (path1.Contains(@"installedrepository\", StringComparison.OrdinalIgnoreCase)) { string fold1T = path1.ToLower().Split(@"installedrepository\")[0]; string fold1 = path1.ToLower().Split(@"installedrepository\")[1]; @@ -111,7 +111,7 @@ private static string Sanitize(string path1) return f1; } - if (path1.Contains(@"build\filerepository\", StringComparison.InvariantCultureIgnoreCase)) + if (path1.Contains(@"build\filerepository\", StringComparison.OrdinalIgnoreCase)) { string fold1T = path1.ToLower().Split(@"build\filerepository\")[0]; string fold1 = path1.ToLower().Split(@"build\filerepository\")[1]; @@ -135,7 +135,7 @@ private static string Sanitize(string path1) return f1; } - if (path1.Contains(@"Windows\winsxs\", StringComparison.InvariantCultureIgnoreCase)) + if (path1.Contains(@"Windows\winsxs\", StringComparison.OrdinalIgnoreCase)) { string fold1T = path1.ToLower().Split(@"windows\winsxs\")[0]; string fold1 = path1.ToLower().Split(@"windows\winsxs\")[1]; @@ -171,7 +171,7 @@ private static bool ExcludedFromChecks(string path) { string index = @"install.wim\3\"; - if (!path.Contains(index, StringComparison.InvariantCultureIgnoreCase)) + if (!path.Contains(index, StringComparison.OrdinalIgnoreCase)) { return true; } diff --git a/Windows Build Identifier/Identification/InstalledImage/DetectionHandler.cs b/Windows Build Identifier/Identification/InstalledImage/DetectionHandler.cs index eb3dd17..04bbf26 100644 --- a/Windows Build Identifier/Identification/InstalledImage/DetectionHandler.cs +++ b/Windows Build Identifier/Identification/InstalledImage/DetectionHandler.cs @@ -28,6 +28,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Xml.Linq; using WindowsBuildIdentifier.Interfaces; namespace WindowsBuildIdentifier.Identification.InstalledImage @@ -56,54 +57,64 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in string softwareHivePath = ""; string systemHivePath = ""; string userPath = ""; - - string kernelEntry = fileentries.FirstOrDefault(x => - (x.EndsWith(@"\ntkrnlmp.exe", StringComparison.InvariantCultureIgnoreCase) || - x.EndsWith(@"\ntoskrnl.exe", StringComparison.InvariantCultureIgnoreCase)) - && x.Contains("System32", StringComparison.InvariantCultureIgnoreCase)); + string virtualEditionsPath = ""; + + string kernelEntry = fileentries + .Where(x => + (x.EndsWith(@"\ntkrnlmp.exe", StringComparison.OrdinalIgnoreCase) || + x.EndsWith(@"\ntoskrnl.exe", StringComparison.OrdinalIgnoreCase))) + .OrderBy(x => x.Contains("System32", StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); if (kernelEntry != null) { kernelPath = installProvider.ExpandFile(kernelEntry); } string hvEntry = fileentries.FirstOrDefault(x => - (x.EndsWith(@"\hvax64.exe", StringComparison.InvariantCultureIgnoreCase) // AMD64 - || x.EndsWith(@"\hvix64.exe", StringComparison.InvariantCultureIgnoreCase) // Intel64 - || x.EndsWith(@"\hvaa64.exe", StringComparison.InvariantCultureIgnoreCase)) // ARM64 - && x.Contains("System32", StringComparison.InvariantCultureIgnoreCase)); + (x.EndsWith(@"\hvax64.exe", StringComparison.OrdinalIgnoreCase) // AMD64 + || x.EndsWith(@"\hvix64.exe", StringComparison.OrdinalIgnoreCase) // Intel64 + || x.EndsWith(@"\hvaa64.exe", StringComparison.OrdinalIgnoreCase)) // ARM64 + && x.Contains("System32", StringComparison.OrdinalIgnoreCase)); if (hvEntry != null) { hvPath = installProvider.ExpandFile(hvEntry); } string shell32Entry = fileentries.FirstOrDefault(x => - x.EndsWith(@"system32\shell32.dll", StringComparison.InvariantCultureIgnoreCase)); + x.EndsWith(@"system32\shell32.dll", StringComparison.OrdinalIgnoreCase)); if (shell32Entry != null) { shell32Path = installProvider.ExpandFile(shell32Entry); } string softwareHiveEntry = fileentries.FirstOrDefault(x => - x.EndsWith(@"system32\config\software", StringComparison.InvariantCultureIgnoreCase)); + x.EndsWith(@"system32\config\software", StringComparison.OrdinalIgnoreCase)); if (softwareHiveEntry != null) { softwareHivePath = installProvider.ExpandFile(softwareHiveEntry); } string systemHiveEntry = fileentries.FirstOrDefault(x => - x.EndsWith(@"system32\config\system", StringComparison.InvariantCultureIgnoreCase)); + x.EndsWith(@"system32\config\system", StringComparison.OrdinalIgnoreCase)); if (systemHiveEntry != null) { systemHivePath = installProvider.ExpandFile(systemHiveEntry); } string userEntry = fileentries.FirstOrDefault(x => - x.EndsWith(@"\system32\user.exe", StringComparison.InvariantCultureIgnoreCase)); + x.EndsWith(@"\system32\user.exe", StringComparison.OrdinalIgnoreCase)); if (userEntry != null) { userPath = installProvider.ExpandFile(userEntry); } + string virtualEditionsEntry = fileentries.FirstOrDefault(x => + x.EndsWith(@"\Editions\EditionMappings.xml", StringComparison.OrdinalIgnoreCase)); + if (virtualEditionsEntry != null) + { + virtualEditionsPath = installProvider.ExpandFile(virtualEditionsEntry); + } + #region Version Gathering VersionInfo1 info = new(); @@ -121,7 +132,7 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in if (report.Architecture == MachineType.x86) { string possibleNec98 = fileentries.FirstOrDefault(x => - x.Contains(@"system32\hal98", StringComparison.InvariantCultureIgnoreCase)); + x.Contains(@"system32\hal98", StringComparison.OrdinalIgnoreCase)); if (possibleNec98 != null) { report.Architecture = MachineType.nec98; @@ -145,7 +156,7 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in report.Licensing = info2.Licensing; report.LanguageCodes = info2.LanguageCodes; - if (report.LanguageCodes == null || report.LanguageCodes.Length == 0) + if (!string.IsNullOrEmpty(kernelPath) && (report.LanguageCodes == null || report.LanguageCodes.Length == 0)) { FileVersionInfo infover = FileVersionInfo.GetVersionInfo(kernelPath); @@ -188,6 +199,14 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in report.CompileDate = correctVersion.CompileDate; } + if (report.BuildNumber == 0) + { + Console.WriteLine( + "Couldn't find Windows version data within this image."); + // couldn't find a good install within this image + return null; + } + // we have to scan all binaries because early versions of NT // do not report the same versions in all binaries if (report.BuildNumber < 1130) @@ -197,9 +216,9 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in ulong buildwindow = report.BuildNumber + 50; foreach (string binary in installProvider.GetFileSystemEntries()) { - if (binary.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) || - binary.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) || - binary.EndsWith(".sys", StringComparison.InvariantCultureIgnoreCase)) + if (binary.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || + binary.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) || + binary.EndsWith(".sys", StringComparison.OrdinalIgnoreCase)) { string file = ""; try @@ -244,7 +263,7 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in #region Edition Gathering bool isUnstaged = - fileentries.Any(x => x.StartsWith(@"packages\", StringComparison.InvariantCultureIgnoreCase)); + fileentries.Any(x => x.StartsWith(@"packages\", StringComparison.OrdinalIgnoreCase)); if (isUnstaged) { @@ -283,7 +302,7 @@ public static WindowsImage IdentifyWindowsNT(IWindowsInstallProviderInterface in else if (!string.IsNullOrEmpty(systemHivePath)) { Console.WriteLine("Extracting additional edition information"); - (string baseSku, string sku) = ExtractEditionFromRegistry(systemHivePath, softwareHivePath); + (string baseSku, string sku) = ExtractEditionFromRegistry(systemHivePath, softwareHivePath, virtualEditionsPath); report.BaseSku = baseSku; report.Sku = sku; } @@ -311,19 +330,19 @@ public static WindowsImage FixSkuNames(WindowsImage report, bool isUnstaged) { if (report.BuildNumber > 2195) { - if (report.Sku.Equals("personal", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("personal", StringComparison.OrdinalIgnoreCase)) { report.Sku = "Home"; } - if (report.Sku.Equals("advancedserver", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("advancedserver", StringComparison.OrdinalIgnoreCase)) { report.Sku = "EnterpriseServer"; } } else { - if (report.Sku.Equals("EnterpriseServer", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("EnterpriseServer", StringComparison.OrdinalIgnoreCase)) { report.Sku = "AdvancedServer"; } @@ -331,7 +350,7 @@ public static WindowsImage FixSkuNames(WindowsImage report, bool isUnstaged) if (report.BuildNumber >= 1911) { - if (report.Sku.Equals("workstation", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("workstation", StringComparison.OrdinalIgnoreCase)) { report.Sku = "Professional"; } @@ -342,17 +361,17 @@ public static WindowsImage FixSkuNames(WindowsImage report, bool isUnstaged) { foreach (string skuunstaged in report.Editions) { - if (skuunstaged.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - skuunstaged.EndsWith("hyperv", StringComparison.InvariantCultureIgnoreCase) || - skuunstaged.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - skuunstaged.EndsWith("v", StringComparison.InvariantCultureIgnoreCase)) + if (skuunstaged.Contains("server", StringComparison.OrdinalIgnoreCase) && + skuunstaged.EndsWith("hyperv", StringComparison.OrdinalIgnoreCase) || + skuunstaged.Contains("server", StringComparison.OrdinalIgnoreCase) && + skuunstaged.EndsWith("v", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.ServerV)) { report.Types.Add(Type.ServerV); } } - else if (skuunstaged.Contains("server", StringComparison.InvariantCultureIgnoreCase)) + else if (skuunstaged.Contains("server", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.Server)) { @@ -370,27 +389,27 @@ public static WindowsImage FixSkuNames(WindowsImage report, bool isUnstaged) } else if (!string.IsNullOrEmpty(report.Sku)) { - if (report.Sku.Equals("ads", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("ads", StringComparison.OrdinalIgnoreCase)) { report.Sku = "AdvancedServer"; } - if (report.Sku.Equals("pro", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Equals("pro", StringComparison.OrdinalIgnoreCase)) { report.Sku = "Professional"; } - if (report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - report.Sku.EndsWith("hyperv", StringComparison.InvariantCultureIgnoreCase) || - report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - report.Sku.EndsWith("v", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase) && + report.Sku.EndsWith("hyperv", StringComparison.OrdinalIgnoreCase) || + report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase) && + report.Sku.EndsWith("v", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.ServerV)) { report.Types.Add(Type.ServerV); } } - else if (report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase)) + else if (report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.Server)) { @@ -442,9 +461,21 @@ private static VersionInfo1 ExtractVersionInfo(string kernelPath) } private static (string baseSku, string sku) ExtractEditionFromRegistry(string systemHivePath, - string softwareHivePath) + string softwareHivePath, string virtualEditionsPath) { (string baseSku, string sku) ret = ("", ""); + Dictionary virtualEditionsMapping = new Dictionary(); + + if (!string.IsNullOrEmpty(virtualEditionsPath)) + { + using (FileStream virtualEditionsStream = new(virtualEditionsPath, FileMode.Open, FileAccess.Read)) + { + var veDoc = XDocument.Load(virtualEditionsStream); + virtualEditionsMapping = veDoc.Descendants("Edition") + .Where(e => e.Attribute("virtual")?.Value == "true" && e.Element("Name") != null && e.Element("ParentEdition") != null) + .ToDictionary(e => e.Element("Name").Value, e => e.Element("ParentEdition").Value); + } + } using (FileStream softHiveStream = new(softwareHivePath, FileMode.Open, FileAccess.Read)) using (RegistryHive softHive = new(softHiveStream)) @@ -457,6 +488,16 @@ private static (string baseSku, string sku) ExtractEditionFromRegistry(string sy string edition = subkey.GetValue("EditionID") as string; string compositionEdition = subkey.GetValue("CompositionEditionID") as string; + if (virtualEditionsMapping.ContainsKey(edition)) + { + return (virtualEditionsMapping[edition], edition); + } + + if (!string.IsNullOrEmpty(virtualEditionsPath)) + { + return (edition, edition); + } + if (!string.IsNullOrEmpty(edition) && !string.IsNullOrEmpty(compositionEdition)) { return (compositionEdition, edition); @@ -783,7 +824,7 @@ private static VersionInfo2 ExtractVersionInfo2(string softwareHivePath, string x.LCID == int.Parse(langid, NumberStyles.HexNumber, CultureInfo.CurrentCulture)).Name; if (result.LanguageCodes == null || result.LanguageCodes != null && !result.LanguageCodes.Any(x => - x.Equals(name, StringComparison.InvariantCultureIgnoreCase))) + x.Equals(name, StringComparison.OrdinalIgnoreCase))) { if (result.LanguageCodes == null) { @@ -805,7 +846,7 @@ private static string[] GatherUnstagedEditions(IWindowsInstallProviderInterface SortedSet report = new(); IEnumerable packages = installProvider.GetFileSystemEntries().Where(x => - x.StartsWith(@"packages\", StringComparison.InvariantCultureIgnoreCase)); + x.StartsWith(@"packages\", StringComparison.OrdinalIgnoreCase)); IEnumerable files = packages.Where(x => x.Count(y => y == '\\') == 1 && x.Contains('.')); @@ -824,7 +865,7 @@ private static string[] GatherUnstagedEditions(IWindowsInstallProviderInterface fileArray.Contains(name + ".xml")) { // This should be a target edition package - report.Add(name.Replace(@"packages\", "", StringComparison.InvariantCultureIgnoreCase)); + report.Add(name.Replace(@"packages\", "", StringComparison.OrdinalIgnoreCase)); } } } @@ -834,7 +875,7 @@ private static string[] GatherUnstagedEditions(IWindowsInstallProviderInterface IEnumerable editionFolders = packages.Where(x => x.Count(y => y == '\\') == 1); IEnumerable editions = editionFolders.Select(x => - x.Replace(@"packages\", "", StringComparison.InvariantCultureIgnoreCase)); + x.Replace(@"packages\", "", StringComparison.OrdinalIgnoreCase)); report.UnionWith(editions); } diff --git a/Windows Build Identifier/Identification/MediaHandler.cs b/Windows Build Identifier/Identification/MediaHandler.cs index 36aa90f..1d2ab35 100644 --- a/Windows Build Identifier/Identification/MediaHandler.cs +++ b/Windows Build Identifier/Identification/MediaHandler.cs @@ -84,11 +84,11 @@ private static WindowsImageIndex[] IdentifyWindowsNTFromWim(Stream wimstream, bo Console.WriteLine($"Found {wim.IMAGE.Length} images in the wim according to the XML"); Console.WriteLine("Evaluating relevant images in the WIM according to the XML"); - int irelevantcount2 = (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("winpe", StringComparison.InvariantCultureIgnoreCase)) ? 1 : 0) + - (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("setup", StringComparison.InvariantCultureIgnoreCase)) ? 1 : 0) + - (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("preinstallation", StringComparison.InvariantCultureIgnoreCase)) ? 1 : 0) + - (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("winre", StringComparison.InvariantCultureIgnoreCase)) ? 1 : 0) + - (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("recovery", StringComparison.InvariantCultureIgnoreCase)) ? 1 : 0); + int irelevantcount2 = (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("winpe", StringComparison.OrdinalIgnoreCase)) ? 1 : 0) + + (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("setup", StringComparison.OrdinalIgnoreCase)) ? 1 : 0) + + (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("preinstallation", StringComparison.OrdinalIgnoreCase)) ? 1 : 0) + + (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("winre", StringComparison.OrdinalIgnoreCase)) ? 1 : 0) + + (wim.IMAGE.Any(x => x.DESCRIPTION != null && x.DESCRIPTION.Contains("recovery", StringComparison.OrdinalIgnoreCase)) ? 1 : 0); Console.WriteLine($"Found {irelevantcount2} irrelevant images in the wim according to the XML"); @@ -103,11 +103,11 @@ private static WindowsImageIndex[] IdentifyWindowsNTFromWim(Stream wimstream, bo // If what we're trying to identify isn't just a winpe, and we are accessing a winpe image // skip the image // - int irelevantcount = (image.DESCRIPTION != null && image.DESCRIPTION.Contains("winpe", StringComparison.InvariantCultureIgnoreCase) ? 1 : 0) + - (image.DESCRIPTION != null && image.DESCRIPTION.Contains("setup", StringComparison.InvariantCultureIgnoreCase) ? 1 : 0) + - (image.DESCRIPTION != null && image.DESCRIPTION.Contains("preinstallation", StringComparison.InvariantCultureIgnoreCase) ? 1 : 0) + - (image.DESCRIPTION != null && image.DESCRIPTION.Contains("winre", StringComparison.InvariantCultureIgnoreCase) ? 1 : 0) + - (image.DESCRIPTION != null && image.DESCRIPTION.Contains("recovery", StringComparison.InvariantCultureIgnoreCase) ? 1 : 0); + int irelevantcount = (image.DESCRIPTION != null && image.DESCRIPTION.Contains("winpe", StringComparison.OrdinalIgnoreCase) ? 1 : 0) + + (image.DESCRIPTION != null && image.DESCRIPTION.Contains("setup", StringComparison.OrdinalIgnoreCase) ? 1 : 0) + + (image.DESCRIPTION != null && image.DESCRIPTION.Contains("preinstallation", StringComparison.OrdinalIgnoreCase) ? 1 : 0) + + (image.DESCRIPTION != null && image.DESCRIPTION.Contains("winre", StringComparison.OrdinalIgnoreCase) ? 1 : 0) + + (image.DESCRIPTION != null && image.DESCRIPTION.Contains("recovery", StringComparison.OrdinalIgnoreCase) ? 1 : 0); Console.WriteLine( $"Index contains {irelevantcount} flags indicating this is a preinstallation environment"); @@ -141,6 +141,10 @@ private static WindowsImageIndex[] IdentifyWindowsNTFromWim(Stream wimstream, bo provider.SetIndex(index); WindowsImage report = DetectionHandler.IdentifyWindowsNT(provider); + if (report == null) + { + continue; + } // fallback if ((string.IsNullOrEmpty(report.Sku) || report.Sku == "TerminalServer") && @@ -152,17 +156,17 @@ private static WindowsImageIndex[] IdentifyWindowsNTFromWim(Stream wimstream, bo report.Types = new HashSet(); - if (report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - report.Sku.EndsWith("hyperv", StringComparison.InvariantCultureIgnoreCase) || - report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase) && - report.Sku.EndsWith("v", StringComparison.InvariantCultureIgnoreCase)) + if (report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase) && + report.Sku.EndsWith("hyperv", StringComparison.OrdinalIgnoreCase) || + report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase) && + report.Sku.EndsWith("v", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.ServerV)) { report.Types.Add(Type.ServerV); } } - else if (report.Sku.Contains("server", StringComparison.InvariantCultureIgnoreCase)) + else if (report.Sku.Contains("server", StringComparison.OrdinalIgnoreCase)) { if (!report.Types.Contains(Type.Server)) { @@ -226,6 +230,7 @@ private static WindowsImageIndex[] IdentifyWindowsNTFromRootFs(TxtSetupBridge fi WindowsImage report = DetectionHandler.IdentifyWindowsNT(provider); report.Sku = fileSystem.GetSkuFromTxtSetupMedia(report.BuildNumber); + report.Licensing = fileSystem.GetLicensingFromTxtSetupMedia(); report = DetectionHandler.FixSkuNames(report, false); Common.DisplayReport(report); @@ -393,7 +398,7 @@ private static FileItem[] HandleFacade(IFileSystem facade, bool recursivity = fa try { if (fileItem.Location.Contains("txtsetup.sif", - StringComparison.InvariantCultureIgnoreCase)) + StringComparison.OrdinalIgnoreCase)) { TxtSetupBridge bridge = new(facade, string.Join("\\", fileItem.Location.Split('\\')[..^1])); diff --git a/Windows Build Identifier/Interfaces/TxtSetupBridge.cs b/Windows Build Identifier/Interfaces/TxtSetupBridge.cs index 5a18357..b8fbad9 100644 --- a/Windows Build Identifier/Interfaces/TxtSetupBridge.cs +++ b/Windows Build Identifier/Interfaces/TxtSetupBridge.cs @@ -95,7 +95,7 @@ public byte[] ReadBootCode() public bool DirectoryExists(string path) { - return _entries.Any(x => x.Path.StartsWith(@"\" + path + @"\", StringComparison.InvariantCultureIgnoreCase)); + return _entries.Any(x => x.Path.StartsWith(@"\" + path + @"\", StringComparison.OrdinalIgnoreCase)); } public bool Exists(string path) @@ -105,24 +105,24 @@ public bool Exists(string path) public bool FileExists(string path) { - return _entries.Any(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + return _entries.Any(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); } public FileAttributes GetAttributes(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetAttributes(_pathInFs + @"\" + entry.DiscLocation); } public DateTime GetCreationTime(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetCreationTime(_pathInFs + @"\" + entry.DiscLocation); } public DateTime GetCreationTimeUtc(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetCreationTimeUtc(_pathInFs + @"\" + entry.DiscLocation); } @@ -165,7 +165,7 @@ public string[] GetDirectories(string path, string searchPattern, SearchOption s public long GetFileLength(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetFileLength(_pathInFs + @"\" + entry.DiscLocation); } @@ -207,31 +207,31 @@ public string[] GetFiles(string path, string searchPattern, SearchOption searchO public DateTime GetLastAccessTime(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetLastAccessTime(_pathInFs + @"\" + entry.DiscLocation); } public DateTime GetLastAccessTimeUtc(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetLastAccessTimeUtc(_pathInFs + @"\" + entry.DiscLocation); } public DateTime GetLastWriteTime(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetLastWriteTime(_pathInFs + @"\" + entry.DiscLocation); } public DateTime GetLastWriteTimeUtc(string path) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); return _originalFs.GetLastWriteTimeUtc(_pathInFs + @"\" + entry.DiscLocation); } public SparseStream OpenFile(string path, FileMode mode) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); if (_originalFs.Exists(_pathInFs + @"\" + entry.DiscLocation)) { @@ -273,7 +273,7 @@ public SparseStream OpenFile(string path, FileMode mode) public SparseStream OpenFile(string path, FileMode mode, FileAccess access) { - TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.InvariantCultureIgnoreCase)); + TxtSetupFileEntry entry = _entries.First(x => x.Path.Equals(@"\" + path, StringComparison.OrdinalIgnoreCase)); if (_originalFs.Exists(_pathInFs + @"\" + entry.DiscLocation)) { @@ -507,6 +507,34 @@ public string GetSkuFromTxtSetupMedia(ulong build) return sku; } + public Identification.Licensing GetLicensingFromTxtSetupMedia() + { + FileIniDataParser parser = new(); + + parser.Parser.Configuration.AllowDuplicateSections = true; + parser.Parser.Configuration.AllowDuplicateKeys = true; + parser.Parser.Configuration.SkipInvalidLines = true; + + SparseStream stream = _originalFs.OpenFile(_pathInFs + @"\setupp.ini", FileMode.Open); + StreamReader setuppIni = new(stream); + IniData data = parser.ReadData(setuppIni); + + if (data.TryGetKey("Pid.Pid", out string pid)) + { + if (pid.EndsWith("270", StringComparison.Ordinal)) + { + return Identification.Licensing.Volume; + } + + if (pid.EndsWith("OEM", StringComparison.Ordinal)) + { + return Identification.Licensing.OEM; + } + } + + return Identification.Licensing.Retail; + } + private void LoadTxtSetupData(IFileSystem originalFs, string pathInFs) { List fileList = new(); diff --git a/Windows Build Identifier/Program.cs b/Windows Build Identifier/Program.cs index fe4e5d9..b8a20e8 100644 --- a/Windows Build Identifier/Program.cs +++ b/Windows Build Identifier/Program.cs @@ -88,7 +88,9 @@ private static (string, string) GetAdequateNameFromImageIndexes(WindowsImageInde SortedSet licensings = new() { f.Licensing }; SortedSet languages = new(f.LanguageCodes ?? new[] { "lang-unknown" }); SortedSet skus = new() { f.Sku.Replace("Server", "") }; - SortedSet baseSkus = new() { f.Sku.Replace("Server", "") }; + SortedSet baseSkus = string.IsNullOrEmpty(f.BaseSku) + ? new() + : new() { f.BaseSku.Replace("Server", "") }; SortedSet archs = new() { $"{f.Architecture}{f.BuildType}" }; for (int i = 1; i < imageIndexes.Length; i++) @@ -207,16 +209,16 @@ private static int RunBulkRenameAndReturnExitCode(BulkRenameOptions opts) WindowsImageIndex[] windowsImageIndexes = files[0].Metadata.WindowsImageIndexes; - if (files.Any(x => x.Location.EndsWith("install.wim", StringComparison.InvariantCultureIgnoreCase))) + if (files.Any(x => x.Location.EndsWith("install.wim", StringComparison.OrdinalIgnoreCase))) { - windowsImageIndexes = files.First(x => x.Location.EndsWith("install.wim")).Metadata + windowsImageIndexes = files.First(x => x.Location.EndsWith("install.wim", StringComparison.OrdinalIgnoreCase)).Metadata .WindowsImageIndexes; } else if (files.Any( - x => x.Location.EndsWith("txtsetup.sif", StringComparison.InvariantCultureIgnoreCase))) + x => x.Location.EndsWith("txtsetup.sif", StringComparison.OrdinalIgnoreCase))) { foreach (FileItem vfile in files.Where(x => - x.Location.EndsWith("txtsetup.sif", StringComparison.InvariantCultureIgnoreCase))) + x.Location.EndsWith("txtsetup.sif", StringComparison.OrdinalIgnoreCase))) { windowsImageIndexes = windowsImageIndexes.Union(vfile.Metadata.WindowsImageIndexes).ToArray(); } @@ -340,16 +342,16 @@ private static int RunBulkSortAndReturnExitCode(BulkSortOptions opts) WindowsImageIndex[] windowsImageIndexes = files[0].Metadata.WindowsImageIndexes; - if (files.Any(x => x.Location.EndsWith("install.wim", StringComparison.InvariantCultureIgnoreCase))) + if (files.Any(x => x.Location.EndsWith("install.wim", StringComparison.OrdinalIgnoreCase))) { - windowsImageIndexes = files.First(x => x.Location.EndsWith("install.wim")).Metadata + windowsImageIndexes = files.First(x => x.Location.EndsWith("install.wim", StringComparison.OrdinalIgnoreCase)).Metadata .WindowsImageIndexes; } else if (files.Any( - x => x.Location.EndsWith("txtsetup.sif", StringComparison.InvariantCultureIgnoreCase))) + x => x.Location.EndsWith("txtsetup.sif", StringComparison.OrdinalIgnoreCase))) { foreach (FileItem vfile in files.Where(x => - x.Location.EndsWith("txtsetup.sif", StringComparison.InvariantCultureIgnoreCase))) + x.Location.EndsWith("txtsetup.sif", StringComparison.OrdinalIgnoreCase))) { windowsImageIndexes = windowsImageIndexes.Union(vfile.Metadata.WindowsImageIndexes).ToArray(); } @@ -900,7 +902,7 @@ private static void PrintBanner() Console.WriteLine(); Console.WriteLine("Windows Build Identifier (WBI)"); Console.WriteLine("Gustave Monce (@gus33000) (c) 2018-2021"); - Console.WriteLine("Thomas Hounsell (c) 2021"); + Console.WriteLine("Thomas Hounsell (c) 2021-2022"); Console.WriteLine(); } diff --git a/Windows Build Identifier/Windows Build Identifier.csproj b/Windows Build Identifier/Windows Build Identifier.csproj index 540c6de..eb30ace 100644 --- a/Windows Build Identifier/Windows Build Identifier.csproj +++ b/Windows Build Identifier/Windows Build Identifier.csproj @@ -28,7 +28,7 @@ - +