diff --git a/WDACConfig/.editorconfig b/WDACConfig/.editorconfig index b1c5a22c7..7106e2984 100644 --- a/WDACConfig/.editorconfig +++ b/WDACConfig/.editorconfig @@ -17,3 +17,12 @@ dotnet_diagnostic.CA1401.severity = error # CA1303: Do not pass literals as localized parameters dotnet_diagnostic.CA1303.severity = silent + +# CA1309: Use ordinal string comparison +dotnet_diagnostic.CA1309.severity = error + +# CA1311: Specify a culture or use an invariant version +dotnet_diagnostic.CA1311.severity = error + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = error diff --git a/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/RuleOptionsx.cs b/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/RuleOptionsx.cs index 297982c5f..3cd9ed054 100644 --- a/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/RuleOptionsx.cs +++ b/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/RuleOptionsx.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Xml; +#nullable enable + namespace WDACConfig { public interface IValidateSetValuesGenerator @@ -13,10 +15,11 @@ public interface IValidateSetValuesGenerator public class RuleOptionsx : IValidateSetValuesGenerator { + public string[] GetValidValues() { // Load the CI Schema content - XmlDocument schemaData = new XmlDocument(); + XmlDocument schemaData = new(); schemaData.Load(Path.Combine(WDACConfig.GlobalVars.CISchemaPath)); // Create a namespace manager to handle namespaces @@ -26,18 +29,33 @@ public string[] GetValidValues() // Define the XPath query to fetch enumeration values string xpathQuery = "//xs:simpleType[@name='OptionType']/xs:restriction/xs:enumeration/@value"; - // Fetch enumeration values from the schema + // Create a new HashSet to store the valid policy rule options HashSet validOptions = new HashSet(StringComparer.OrdinalIgnoreCase); - XmlNodeList optionNodes = schemaData.SelectNodes(xpathQuery, nsManager); + + // Fetch enumeration values from the schema + XmlNodeList? optionNodes = schemaData.SelectNodes(xpathQuery, nsManager) ?? throw new Exception("No valid options found in the Code Integrity Schema."); + foreach (XmlNode node in optionNodes) { - validOptions.Add(node.Value); + if (node.Value != null) + { + validOptions.Add(node.Value); + } } - // Read PolicyRuleOptions.Json + // Construct the full path to PolicyRuleOptions.Json string jsonFilePath = Path.Combine(WDACConfig.GlobalVars.ModuleRootPath, "Resources", "PolicyRuleOptions.Json"); + + // Read PolicyRuleOptions.Json string jsonContent = File.ReadAllText(jsonFilePath); - Dictionary intel = System.Text.Json.JsonSerializer.Deserialize>(jsonContent); + + // Deserialize the JSON content + Dictionary? intel = System.Text.Json.JsonSerializer.Deserialize>(jsonContent); + + if (intel == null) + { + throw new Exception("The PolicyRuleOptions.Json file did not have valid JSON content to be deserialized."); + } // Perform validation foreach (string key in intel.Values) @@ -53,7 +71,7 @@ public string[] GetValidValues() if (!intel.Values.Contains(option, StringComparer.OrdinalIgnoreCase)) { // this should be a verbose or warning message - // throw new Exception($"Rule option '{option}' exists in the Code Integrity Schema but not being used by the module."); + // throw new Exception($"Rule option '{option}' exists in the Code Integrity Schema but not being used by the module."); } } diff --git a/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/ScanLevelz.cs b/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/ScanLevelz.cs index c058d48e3..56e9219be 100644 --- a/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/ScanLevelz.cs +++ b/WDACConfig/WDACConfig Module Files/C#/ArgumentCompleters/ScanLevelz.cs @@ -1,6 +1,8 @@ using System; using System.Management.Automation; +#nullable enable + namespace WDACConfig { // Argument tab auto-completion and ValidateSet for Levels and Fallbacks parameters in the entire module diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/AuthenticodePageHashes.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/AuthenticodePageHashes.cs index faf8854b6..4b2e6333e 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/AuthenticodePageHashes.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/AuthenticodePageHashes.cs @@ -1,3 +1,7 @@ +using System; + +#nullable enable + namespace WDACConfig { public class AuthenticodePageHashes diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateDetailsCreator.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateDetailsCreator.cs index 0a6590718..6c0f5b88d 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateDetailsCreator.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateDetailsCreator.cs @@ -1,3 +1,6 @@ + +#nullable enable + namespace WDACConfig { public class CertificateDetailsCreator diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateSignerCreator.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateSignerCreator.cs index 065797d6e..b6129ddca 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateSignerCreator.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/CertificateSignerCreator.cs @@ -1,4 +1,7 @@ using System; + +#nullable enable + namespace WDACConfig { public class CertificateSignerCreator diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainElement.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainElement.cs index f22336d48..9d952f012 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainElement.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainElement.cs @@ -1,6 +1,8 @@ using System; using System.Security.Cryptography.X509Certificates; +#nullable enable + namespace WDACConfig { // the enum for CertificateType diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainPackage.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainPackage.cs index afc9ea262..03725a6eb 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainPackage.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/ChainPackage.cs @@ -1,6 +1,8 @@ using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; +#nullable enable + namespace WDACConfig { public class ChainPackage diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/FileBasedInfoPackage.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/FileBasedInfoPackage.cs index 04a7c46ca..e77835e45 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/FileBasedInfoPackage.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/FileBasedInfoPackage.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace WDACConfig { // Used by the BuildSignerAndHashObjects method to store and return the output diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/FilePublisherSignerCreator.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/FilePublisherSignerCreator.cs index 8a49f038d..5b36b2c37 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/FilePublisherSignerCreator.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/FilePublisherSignerCreator.cs @@ -1,20 +1,22 @@ using System; using System.Collections.Generic; +#nullable enable + namespace WDACConfig { public class FilePublisherSignerCreator { public List CertificateDetails { get; set; } - public Version FileVersion { get; set; } - public string FileDescription { get; set; } - public string InternalName { get; set; } - public string OriginalFileName { get; set; } - public string PackageFamilyName { get; set; } - public string ProductName { get; set; } - public string FileName { get; set; } - public string AuthenticodeSHA256 { get; set; } - public string AuthenticodeSHA1 { get; set; } + public Version? FileVersion { get; set; } + public string? FileDescription { get; set; } + public string? InternalName { get; set; } + public string? OriginalFileName { get; set; } + public string? PackageFamilyName { get; set; } + public string? ProductName { get; set; } + public string? FileName { get; set; } + public string? AuthenticodeSHA256 { get; set; } + public string? AuthenticodeSHA1 { get; set; } public int SiSigningScenario { get; set; } public FilePublisherSignerCreator(List certificateDetails, Version fileVersion, string fileDescription, string internalName, string originalFileName, string packageFamilyName, string productName, string fileName, string authenticodeSHA256, string authenticodeSHA1, int siSigningScenario) diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/HashCreator.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/HashCreator.cs index ea60af5ec..93c7f0710 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/HashCreator.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/HashCreator.cs @@ -1,3 +1,6 @@ + +#nullable enable + namespace WDACConfig { public class HashCreator diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/OpusSigner.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/OpusSigner.cs index bfa8ad84b..fc709d1ec 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/OpusSigner.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/OpusSigner.cs @@ -1,3 +1,6 @@ + +#nullable enable + namespace WDACConfig { public class OpusSigner diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/PolicyHashObj.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/PolicyHashObj.cs index 54e5bde9e..176b2a1df 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/PolicyHashObj.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/PolicyHashObj.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + // Used by WDAC Simulations namespace WDACConfig { @@ -21,7 +23,7 @@ public PolicyHashObj(string hashvalue, string hashtype, string filepathforhash) // Making sure any HashSet or collection using this class will only keep unique objects based on their HashValue property // Override the Equals method - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj == null || GetType() != obj.GetType()) { diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/PublisherSignerCreator.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/PublisherSignerCreator.cs index 5cb9e0444..d083421ea 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/PublisherSignerCreator.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/PublisherSignerCreator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + namespace WDACConfig { public class PublisherSignerCreator diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/Signer.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/Signer.cs index 35d97f1f4..0d467b284 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/Signer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/Signer.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace WDACConfig { public class Signer diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationInput.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationInput.cs index c033bca1e..fdc82aba6 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationInput.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationInput.cs @@ -1,3 +1,6 @@ + +#nullable enable + // Used by WDAC Simulations namespace WDACConfig { diff --git a/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationOutput.cs b/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationOutput.cs index f5c187319..15391c0aa 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationOutput.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Custom Types/SimulationOutput.cs @@ -1,3 +1,6 @@ + +#nullable enable + // Used by WDAC Simulations, the output of the comparer function/method namespace WDACConfig { diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/AllCertificatesGrabber.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/AllCertificatesGrabber.cs index bd8f8c652..3f6ea0b35 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/AllCertificatesGrabber.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/AllCertificatesGrabber.cs @@ -6,7 +6,6 @@ // The following functions and methods use the Windows APIs to grab all of the certificates from a signed file -#nullable disable namespace WDACConfig.AllCertificatesGrabber { diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/AuthenticodeHashCalc.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/AuthenticodeHashCalc.cs index 8b40a36ab..84a67806e 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/AuthenticodeHashCalc.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/AuthenticodeHashCalc.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using System.Text; using System.IO; +using System.Globalization; namespace WDACConfig { @@ -101,7 +102,7 @@ private static string GetAuthenticodeHash(string filePath, string hashAlgorithm) // Marshal.ReadByte returns a byte from the hashValue buffer at the specified offset byte b = Marshal.ReadByte(hashValue, offset); // Append the byte to the hashString as a hexadecimal string - hashString.Append(b.ToString("X2")); + hashString.Append(b.ToString("X2", CultureInfo.InvariantCulture)); } } } diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/CIPolicyVersion.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/CIPolicyVersion.cs index 292171a4b..f1f751322 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/CIPolicyVersion.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/CIPolicyVersion.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace WDACConfig { public static class CIPolicyVersion diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/CertificateHelper.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/CertificateHelper.cs index b921d95ba..14b3c21d4 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/CertificateHelper.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/CertificateHelper.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Formats.Asn1; // to use the AsnReader and AsnWriter classes +using System.Formats.Asn1; + +#nullable enable namespace WDACConfig { diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/CiPolicyUtility.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/CiPolicyUtility.cs index 3f5bc6b95..a6bcd9413 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/CiPolicyUtility.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/CiPolicyUtility.cs @@ -2,6 +2,8 @@ using System.IO; using System.Xml; +#nullable enable + namespace WDACConfig { public static class CiPolicyUtility @@ -43,21 +45,21 @@ public static void CopyCiRules(string sourceFilePath, string destinationFilePath nsmgr.AddNamespace("ns", "urn:schemas-microsoft-com:sipolicy"); // Select the Rules node in the source XML document - XmlNode sourceRulesNode = sourceXmlDoc.SelectSingleNode("/ns:SiPolicy/ns:Rules", nsmgr); + XmlNode? sourceRulesNode = sourceXmlDoc.SelectSingleNode("/ns:SiPolicy/ns:Rules", nsmgr); if (sourceRulesNode == null) { throw new Exception("The node was not found in the source XML file."); } // Select the SiPolicy node in the destination XML document - XmlNode destinationSiPolicyNode = destinationXmlDoc.SelectSingleNode("/ns:SiPolicy", nsmgr); + XmlNode? destinationSiPolicyNode = destinationXmlDoc.SelectSingleNode("/ns:SiPolicy", nsmgr); if (destinationSiPolicyNode == null) { throw new Exception("The node was not found in the destination XML file."); } // Select the existing Rules node in the destination XML document - XmlNode destinationRulesNode = destinationSiPolicyNode.SelectSingleNode("ns:Rules", nsmgr); + XmlNode? destinationRulesNode = destinationSiPolicyNode.SelectSingleNode("ns:Rules", nsmgr); if (destinationRulesNode == null) { throw new Exception("The node was not found in the destination XML file."); diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/CodeIntegritySigner.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/CodeIntegritySigner.cs index 57af68992..712029681 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/CodeIntegritySigner.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/CodeIntegritySigner.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using System.IO; +#nullable enable + namespace WDACConfig { public static class CodeIntegritySigner diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/Crypt32CertCN.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/Crypt32CertCN.cs index 913543070..b2977bfd3 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/Crypt32CertCN.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/Crypt32CertCN.cs @@ -2,6 +2,8 @@ using System.Runtime.InteropServices; using System.Text; +#nullable enable + namespace WDACConfig { public class CryptoAPI @@ -9,7 +11,7 @@ public class CryptoAPI // Importing function from crypt32.dll to access certificate information // https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetnamestringa [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern bool CertGetNameString( + internal static extern bool CertGetNameString( IntPtr pCertContext, // the handle property of the certificate object int dwType, int dwFlags, diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/DebugLogger.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/DebugLogger.cs index 8ac36f32d..06cad352b 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/DebugLogger.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/DebugLogger.cs @@ -1,6 +1,8 @@ using System; using System.Management.Automation.Host; +#nullable enable + namespace WDACConfig { public static class DebugLogger diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/DirectorySelector.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/DirectorySelector.cs index 08ade6a8d..afb988241 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/DirectorySelector.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/DirectorySelector.cs @@ -5,13 +5,19 @@ using System.Linq; using System.Runtime.InteropServices; +#nullable enable + namespace WDACConfig { public static class DirectorySelector { - // Keeps asking for directories until the user cancels the selection - // returns unique DirectoryInfo[] of selected directories - public static DirectoryInfo[] SelectDirectories() + /// + /// Keeps asking for directories until the user cancels the selection + /// returns unique DirectoryInfo[] of selected directories if user actually selected directories + /// returns null if user did not select any categories + /// + /// + public static DirectoryInfo[]? SelectDirectories() { // HashSet to store unique selected directories HashSet programsPaths = new HashSet(new DirectoryInfoComparer()); @@ -47,22 +53,35 @@ public static DirectoryInfo[] SelectDirectories() return programsPaths.Count > 0 ? programsPaths.ToArray() : null; } - // Comparer for DirectoryInfo to ensure uniqueness and do it in a case-insensitive way + // Comparer for DirectoryInfo to ensure uniqueness and do it in a case-insensitive way private class DirectoryInfoComparer : IEqualityComparer { - public bool Equals(DirectoryInfo x, DirectoryInfo y) + public bool Equals(DirectoryInfo? x, DirectoryInfo? y) { + // If both are null, they are considered equal + if (x == null && y == null) + { + return true; + } + + // If one is null but not the other, they are not equal + if (x == null || y == null) + { + return false; + } + // Compare full path in a case-insensitive way return string.Equals(x.FullName, y.FullName, StringComparison.OrdinalIgnoreCase); } - // Get hash code of the full path in a case-insensitive way + // Get hash code of the full path in a case-insensitive way using StringComparer.OrdinalIgnoreCase public int GetHashCode(DirectoryInfo obj) { - return obj.FullName.ToLowerInvariant().GetHashCode(); + return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.FullName); } } + // P/Invoke declarations [DllImport("user32.dll")] // Get the handle of the foreground window diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/DriveLetterMapper.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/DriveLetterMapper.cs index c98595e4b..62e31bac1 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/DriveLetterMapper.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/DriveLetterMapper.cs @@ -3,6 +3,8 @@ using System.Runtime.InteropServices; using System.Text; +#nullable enable + namespace WDACConfig { public static class DriveLetterMapper @@ -10,7 +12,7 @@ public static class DriveLetterMapper // Importing the GetVolumePathNamesForVolumeNameW function from kernel32.dll [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetVolumePathNamesForVolumeNameW( + private static extern bool GetVolumePathNamesForVolumeNameW( [MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeName, [MarshalAs(UnmanagedType.LPWStr)][Out] StringBuilder lpszVolumeNamePaths, uint cchBuferLength, @@ -18,20 +20,20 @@ public static extern bool GetVolumePathNamesForVolumeNameW( // Importing the FindFirstVolume function from kernel32.dll [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr FindFirstVolume( + private static extern IntPtr FindFirstVolume( [Out] StringBuilder lpszVolumeName, uint cchBufferLength); // Importing the FindNextVolume function from kernel32.dll [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool FindNextVolume( + private static extern bool FindNextVolume( IntPtr hFindVolume, [Out] StringBuilder lpszVolumeName, uint cchBufferLength); // Importing the QueryDosDevice function from kernel32.dll [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint QueryDosDevice( + private static extern uint QueryDosDevice( string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); @@ -40,11 +42,11 @@ public static extern uint QueryDosDevice( public class DriveMapping { // Property to store drive letter - public string DriveLetter { get; set; } + public string? DriveLetter { get; set; } // Property to store device path - public string DevicePath { get; set; } + public string? DevicePath { get; set; } // Property to store volume name - public string VolumeName { get; set; } + public string? VolumeName { get; set; } } /// diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/EditGUIDs.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/EditGUIDs.cs index a215bb016..c132c8b7b 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/EditGUIDs.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/EditGUIDs.cs @@ -2,6 +2,8 @@ using System.IO; using System.Xml; +#nullable enable + namespace WDACConfig { public static class PolicyEditor @@ -24,13 +26,13 @@ public static void EditGuids(string policyIdInput, FileInfo policyFilePathInput) XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); nsMgr.AddNamespace("ns", "urn:schemas-microsoft-com:sipolicy"); - XmlNode policyIdNode = xmlDoc.SelectSingleNode("/ns:SiPolicy/ns:PolicyID", nsMgr); + XmlNode? policyIdNode = xmlDoc.SelectSingleNode("/ns:SiPolicy/ns:PolicyID", nsMgr); if (policyIdNode != null) { policyIdNode.InnerText = newPolicyId; } - XmlNode basePolicyIdNode = xmlDoc.SelectSingleNode("/ns:SiPolicy/ns:BasePolicyID", nsMgr); + XmlNode? basePolicyIdNode = xmlDoc.SelectSingleNode("/ns:SiPolicy/ns:BasePolicyID", nsMgr); if (basePolicyIdNode != null) { basePolicyIdNode.InnerText = newBasePolicyId; diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/EventLogUtility.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/EventLogUtility.cs index 051b129bd..c11057a57 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/EventLogUtility.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/EventLogUtility.cs @@ -2,6 +2,8 @@ using System.Diagnostics.Eventing.Reader; using System.IO; +#nullable enable + namespace WDACConfig { public static class EventLogUtility diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/FileDirectoryPathComparer.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/FileDirectoryPathComparer.cs index 4d720430a..31efde2ef 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/FileDirectoryPathComparer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/FileDirectoryPathComparer.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; +#nullable enable + namespace WDACConfig { public class FileDirectoryPathComparer diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/GetExtendedFileAttrib.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/GetExtendedFileAttrib.cs index 2c53b6247..19a4bb97f 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/GetExtendedFileAttrib.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/GetExtendedFileAttrib.cs @@ -2,6 +2,8 @@ using System.Globalization; using System.Runtime.InteropServices; +#nullable enable + namespace WDACConfig { public class ExFileInfo @@ -13,11 +15,11 @@ public class ExFileInfo public const int HR_ERROR_RESOURCE_TYPE_NOT_FOUND = -2147023083; // Properties to hold file information - public string OriginalFileName { get; private set; } - public string InternalName { get; private set; } - public string ProductName { get; private set; } - public string Version { get; private set; } - public string FileDescription { get; private set; } + public string? OriginalFileName { get; private set; } + public string? InternalName { get; private set; } + public string? ProductName { get; private set; } + public string? Version { get; private set; } + public string? FileDescription { get; private set; } // Importing external functions from Version.dll to work with file version info // https://learn.microsoft.com/he-il/windows/win32/api/winver/nf-winver-getfileversioninfosizeexa @@ -55,19 +57,19 @@ public static ExFileInfo GetExtendedFileInfo(string filePath) // Extract version from the version data if (!TryGetVersion(spanData, out var version)) - throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())!; ExFileInfo.Version = CheckAndSetNull(version); // Extract locale and encoding information if (!TryGetLocaleAndEncoding(spanData, out var locale, out var encoding)) - throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())!; // Retrieve various file information based on locale and encoding - ExFileInfo.OriginalFileName = CheckAndSetNull(GetLocalizedResource(spanData, encoding, locale, "\\OriginalFileName")); - ExFileInfo.InternalName = CheckAndSetNull(GetLocalizedResource(spanData, encoding, locale, "\\InternalName")); - ExFileInfo.FileDescription = CheckAndSetNull(GetLocalizedResource(spanData, encoding, locale, "\\FileDescription")); - ExFileInfo.ProductName = CheckAndSetNull(GetLocalizedResource(spanData, encoding, locale, "\\ProductName")); + ExFileInfo.OriginalFileName = CheckAndSetNull(GetLocalizedResource(spanData, encoding!, locale!, "\\OriginalFileName")); + ExFileInfo.InternalName = CheckAndSetNull(GetLocalizedResource(spanData, encoding!, locale!, "\\InternalName")); + ExFileInfo.FileDescription = CheckAndSetNull(GetLocalizedResource(spanData, encoding!, locale!, "\\FileDescription")); + ExFileInfo.ProductName = CheckAndSetNull(GetLocalizedResource(spanData, encoding!, locale!, "\\ProductName")); } catch { @@ -82,7 +84,7 @@ public static ExFileInfo GetExtendedFileInfo(string filePath) } // Extract the version from the data - private static bool TryGetVersion(Span data, out string version) + private static bool TryGetVersion(Span data, out string? version) { version = null; // Query the root block for version info @@ -98,7 +100,7 @@ private static bool TryGetVersion(Span data, out string version) } // Extract locale and encoding information from the data - private static bool TryGetLocaleAndEncoding(Span data, out string locale, out string encoding) + private static bool TryGetLocaleAndEncoding(Span data, out string? locale, out string? encoding) { locale = null; encoding = null; @@ -117,7 +119,7 @@ private static bool TryGetLocaleAndEncoding(Span data, out string locale, } // Get localized resource string based on encoding and locale - private static string GetLocalizedResource(Span versionBlock, string encoding, string locale, string resource) + private static string? GetLocalizedResource(Span versionBlock, string encoding, string locale, string resource) { var encodings = new[] { encoding, Cp1252FallbackCode, UnicodeFallbackCode }; foreach (var enc in encodings) @@ -128,13 +130,13 @@ private static string GetLocalizedResource(Span versionBlock, string encod // If error is not resource type not found, throw the error if (Marshal.GetHRForLastWin32Error() != HR_ERROR_RESOURCE_TYPE_NOT_FOUND) - throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())!; } return null; } // Check if a string is null or whitespace and return null if it is - private static string CheckAndSetNull(string value) + private static string? CheckAndSetNull(string? value) { return string.IsNullOrWhiteSpace(value) ? null : value; } diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/GetFilesFast.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/GetFilesFast.cs index ccf30baae..00a69089b 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/GetFilesFast.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/GetFilesFast.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; +#nullable enable + namespace WDACConfig { public class FileUtility diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/GetOpusData.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/GetOpusData.cs index 3ed522cb6..9b21e49fd 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/GetOpusData.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/GetOpusData.cs @@ -4,6 +4,8 @@ using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; +#nullable enable + namespace WDACConfig { public static class Opus @@ -84,7 +86,7 @@ public struct OpusInfoObj else { // Converting the unmanaged memory block to OpusInfoObj structure - Opus.OpusInfoObj structure = (Opus.OpusInfoObj)Marshal.PtrToStructure(decodedDataPtr, typeof(Opus.OpusInfoObj)); + Opus.OpusInfoObj structure = (Opus.OpusInfoObj)Marshal.PtrToStructure(decodedDataPtr, typeof(Opus.OpusInfoObj))!; // Adding the structure to OEMOpusData list OEMOpusData.Add(structure); } diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/Initializer.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/Initializer.cs index a34945b43..d9469a569 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/Initializer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/Initializer.cs @@ -2,6 +2,8 @@ using Microsoft.Win32; using System.Globalization; +#nullable enable + namespace WDACConfig { // Prepares the environment. It also runs commands that would otherwise run in the default constructor for the GlobalVars Class @@ -10,11 +12,11 @@ public class Initializer /// These are the codes that were present in the GlobalVar class's default constructor but defining them as a separate method allows any errors thrown in them to be properly displayed in PowerShell instead of showing an error occurred in the default constructor of a class public static void Initialize() { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion") ?? throw new Exception("Could not get the current Windows version from the registry")) { if (key != null) { - object ubrValue = key.GetValue("UBR"); + object? ubrValue = key.GetValue("UBR"); if (ubrValue != null && int.TryParse(ubrValue.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out int ubr)) { WDACConfig.GlobalVars.UBR = ubr; diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/LoggerInitializer.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/LoggerInitializer.cs index a457fa112..5d9856ee6 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/LoggerInitializer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/LoggerInitializer.cs @@ -1,6 +1,8 @@ using System; using System.Management.Automation.Host; +#nullable enable + namespace WDACConfig { public class LoggerInitializer @@ -28,7 +30,5 @@ public static void Initialize(string verbosePreference, string debugPreference, WDACConfig.GlobalVars.Host = host; } } - - } } diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/MeowOpener.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/MeowOpener.cs index 49db42186..f774a7331 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/MeowOpener.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/MeowOpener.cs @@ -3,6 +3,8 @@ using System.Runtime.InteropServices; using System.Xml; +#nullable enable + namespace WDACConfig { // Declares a public static class that cannot be instantiated. diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/MoveUserModeToKernelMode.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/MoveUserModeToKernelMode.cs index 851473d41..57aafc55e 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/MoveUserModeToKernelMode.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/MoveUserModeToKernelMode.cs @@ -4,20 +4,26 @@ using static System.Formats.Asn1.AsnWriter; using System.Xml.Linq; +#nullable enable + namespace WDACConfig { public static class MoveUserModeToKernelMode { - // Moves all User mode AllowedSigners in the User mode signing scenario to the Kernel mode signing scenario and then - // deletes the entire User mode signing scenario block - // This is used during the creation of Strict Kernel-mode WDAC policy for complete BYOVD protection scenario. - // It doesn't consider node in the SigningScenario 12 when deleting it because for kernel-mode policy everything is signed and we don't deal with unsigned files. + /// + /// Moves all User mode AllowedSigners in the User mode signing scenario to the Kernel mode signing scenario and then + /// deletes the entire User mode signing scenario block + /// This is used during the creation of Strict Kernel-mode WDAC policy for complete BYOVD protection scenario. + /// It doesn't consider node in the SigningScenario 12 when deleting it because for kernel-mode policy everything is signed and we don't deal with unsigned files. + /// + /// The path to the XML file + /// public static void Move(string filePath) { try { // Create an XmlDocument object - XmlDocument xml = new XmlDocument(); + XmlDocument xml = new(); // Load the XML file xml.Load(filePath); @@ -28,11 +34,17 @@ public static void Move(string filePath) nsManager.AddNamespace("sip", "urn:schemas-microsoft-com:sipolicy"); // Get all SigningScenario nodes in the XML file - XmlNodeList signingScenarios = xml.SelectNodes("//sip:SigningScenario", nsManager); + XmlNodeList? signingScenarios = xml.SelectNodes("//sip:SigningScenario", nsManager); // Variables to store SigningScenario nodes with specific values 12 and 131 - XmlNode signingScenario12 = null; - XmlNode signingScenario131 = null; + XmlNode? signingScenario12 = null; + XmlNode? signingScenario131 = null; + + // If there is no SigningScenarios block in the XML then exit the method + if (signingScenarios == null) + { + return; + } // Find SigningScenario nodes with Value 12 and 131 foreach (XmlNode signingScenario in signingScenarios) @@ -52,7 +64,7 @@ public static void Move(string filePath) if (signingScenario12 != null && signingScenario131 != null) { // Get AllowedSigners from SigningScenario with Value 12 - XmlNode allowedSigners12 = signingScenario12.SelectSingleNode("./sip:ProductSigners/sip:AllowedSigners", nsManager); + XmlNode? allowedSigners12 = signingScenario12.SelectSingleNode("./sip:ProductSigners/sip:AllowedSigners", nsManager); // If AllowedSigners node exists in SigningScenario 12 and has child nodes if (allowedSigners12 != null && allowedSigners12.HasChildNodes) @@ -77,13 +89,13 @@ public static void Move(string filePath) XmlAttribute newSignerIdAttr = xml.CreateAttribute("SignerId"); // Set the value of the new SignerId attribute to the value of the existing SignerId attribute - newSignerIdAttr.Value = allowedSigner.Attributes["SignerId"].Value; + newSignerIdAttr.Value = allowedSigner.Attributes["SignerId"]!.Value; // Append the new SignerId attribute to the new AllowedSigner node - newAllowedSigner.Attributes.Append(newSignerIdAttr); + newAllowedSigner.Attributes!.Append(newSignerIdAttr); // Find the AllowedSigners node in SigningScenario 131 - XmlNode allowedSigners131 = signingScenario131.SelectSingleNode("./sip:ProductSigners/sip:AllowedSigners", nsManager); + XmlNode? allowedSigners131 = signingScenario131.SelectSingleNode("./sip:ProductSigners/sip:AllowedSigners", nsManager); // If the AllowedSigners node exists in SigningScenario 131 if (allowedSigners131 != null) diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/PageHashCalc.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/PageHashCalc.cs index b58417d27..944dcb228 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/PageHashCalc.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/PageHashCalc.cs @@ -1,17 +1,22 @@ -// necessary logics for Page hash calculation using System; using System.IO; -using System.Runtime.InteropServices; // for interoperability with unmanaged code -using System.Security.Cryptography; // for cryptographic algorithms +using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Text; +using System.Globalization; + +#nullable enable namespace WDACConfig { + /// + /// necessary logics for Page hash calculation + /// public static class PageHashCalculator { // a method to compute the hash of the first page of a file using a native function from Wintrust.dll [DllImport("Wintrust.dll", CharSet = CharSet.Unicode)] // an attribute to specify the DLL name and the character set - public static extern int ComputeFirstPageHash( // the method signature + internal static extern int ComputeFirstPageHash( // the method signature string pszAlgId, // the first parameter: the name of the hash algorithm to use string filename, // the second parameter: the name of the file to hash IntPtr buffer, // the third parameter: a pointer to a buffer to store the hash value @@ -19,32 +24,60 @@ public static extern int ComputeFirstPageHash( // the method signature ); // a method to get the hash of the first page of a file as a hexadecimal string - public static string GetPageHash(string algName, string fileName) // the method signature + public static string? GetPageHash(string algName, string fileName) // the method signature { - IntPtr buffer = IntPtr.Zero; // initialize the buffer pointer to zero - int bufferSize = 0; // initialize the buffer size to zero - StringBuilder stringBuilder = new StringBuilder(62); // create a string builder to append the hash value + // initialize the buffer pointer to zero + IntPtr buffer = IntPtr.Zero; + + // initialize the buffer size to zero + int bufferSize = 0; - try // a try block to handle any exceptions + // create a string builder to append the hash value + StringBuilder stringBuilder = new StringBuilder(62); + + try { - int firstPageHash1 = ComputeFirstPageHash(algName, fileName, buffer, bufferSize); // call the native function with the given parameters and store the return value - if (firstPageHash1 == 0) // if the return value is zero, it means the function failed - return null; // return null to indicate an error + // call the native function with the given parameters and store the return value + int firstPageHash1 = ComputeFirstPageHash(algName, fileName, buffer, bufferSize); + + // if the return value is zero, it means the function failed + if (firstPageHash1 == 0) + { + // return null to indicate an error + return null; + } + + // allocate memory for the buffer using the return value as the size + buffer = Marshal.AllocHGlobal(firstPageHash1); + + // call the native function again with the same parameters and the allocated buffer + int firstPageHash2 = ComputeFirstPageHash(algName, fileName, buffer, firstPageHash1); + + // if the return value is zero, it means the function failed + if (firstPageHash2 == 0) + { + // return null to indicate an error + return null; + } - buffer = Marshal.AllocHGlobal(firstPageHash1); // allocate memory for the buffer using the return value as the size - int firstPageHash2 = ComputeFirstPageHash(algName, fileName, buffer, firstPageHash1); // call the native function again with the same parameters and the allocated buffer - if (firstPageHash2 == 0) // if the return value is zero, it means the function failed - return null; // return null to indicate an error + // loop through the buffer bytes + for (int ofs = 0; ofs < firstPageHash2; ++ofs) - for (int ofs = 0; ofs < firstPageHash2; ++ofs) // loop through the buffer bytes - stringBuilder.Append(Marshal.ReadByte(buffer, ofs).ToString("X2")); // read each byte, convert it to a hexadecimal string, and append it to the string builder + // read each byte, convert it to a hexadecimal string, and append it to the string builder + stringBuilder.Append(Marshal.ReadByte(buffer, ofs).ToString("X2", CultureInfo.InvariantCulture)); - return stringBuilder.ToString(); // return the final string + // return the final string + return stringBuilder.ToString(); } - finally // a finally block to execute regardless of the outcome + // a finally block to execute regardless of the outcome + finally { - if (buffer != IntPtr.Zero) // if the buffer pointer is not zero, it means it was allocated - Marshal.FreeHGlobal(buffer); // free the allocated memory + // if the buffer pointer is not zero, it means it was allocated + if (buffer != IntPtr.Zero) + { + // free the allocated memory + Marshal.FreeHGlobal(buffer); + } } } } diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/SecureStringComparer.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/SecureStringComparer.cs index f54ddba69..302813a24 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/SecureStringComparer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/SecureStringComparer.cs @@ -2,6 +2,8 @@ using System.Runtime.InteropServices; using System.Security; +#nullable enable + namespace WDACConfig { public static class SecureStringComparer diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/StagingArea.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/StagingArea.cs index 7af6e2850..6254c443b 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/StagingArea.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/StagingArea.cs @@ -1,6 +1,8 @@ using System; using System.IO; +#nullable enable + namespace WDACConfig { public static class StagingArea diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/TestCiPolicy.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/TestCiPolicy.cs index b29fa299f..dfa8a995f 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/TestCiPolicy.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/TestCiPolicy.cs @@ -7,11 +7,13 @@ using System.Xml; using System.Xml.Schema; +#nullable enable + namespace WDACConfig { public static class CiPolicyTest { - public static object TestCiPolicy(string xmlFilePath, string cipFilePath) + public static object? TestCiPolicy(string xmlFilePath, string cipFilePath) { // Make sure the parameters are mutually exclusive if (!string.IsNullOrEmpty(xmlFilePath) && !string.IsNullOrEmpty(cipFilePath)) diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/VerboseLogger.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/VerboseLogger.cs index 1930dbad9..88fa16f5a 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/VerboseLogger.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/VerboseLogger.cs @@ -1,6 +1,8 @@ using System; using System.Management.Automation.Host; +#nullable enable + namespace WDACConfig { public static class VerboseLogger diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/VersionIncrementer.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/VersionIncrementer.cs index 66dc24d62..73240fcfe 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/VersionIncrementer.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/VersionIncrementer.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace WDACConfig { public class VersionIncrementer diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/WDAC Simulation/GetFileRuleOutput.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/WDAC Simulation/GetFileRuleOutput.cs index 127cc7c17..c86434344 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/WDAC Simulation/GetFileRuleOutput.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/WDAC Simulation/GetFileRuleOutput.cs @@ -4,6 +4,8 @@ using System.Xml; using System.Text.RegularExpressions; +#nullable enable + namespace WDACConfig { public static class GetFileRuleOutput diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/WldpQuerySecurityPolicy.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/WldpQuerySecurityPolicy.cs index 1001c017a..bcb917e4b 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/WldpQuerySecurityPolicy.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/WldpQuerySecurityPolicy.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; +#nullable enable + namespace WDACConfig { public enum WLDP_SECURE_SETTING_VALUE_TYPE @@ -23,7 +25,7 @@ public struct UNICODE_STRING public class WldpQuerySecurityPolicyWrapper { [DllImport("Wldp.dll", CharSet = CharSet.Unicode)] - public static extern int WldpQuerySecurityPolicy( + internal static extern int WldpQuerySecurityPolicy( ref UNICODE_STRING Provider, ref UNICODE_STRING Key, ref UNICODE_STRING ValueName, diff --git a/WDACConfig/WDACConfig Module Files/C#/Functions/XmlFilePathExtractor.cs b/WDACConfig/WDACConfig Module Files/C#/Functions/XmlFilePathExtractor.cs index 2ac837304..1b6ef3948 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Functions/XmlFilePathExtractor.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Functions/XmlFilePathExtractor.cs @@ -3,6 +3,8 @@ using System.IO; using System.Xml; +#nullable enable + namespace WDACConfig { public class XmlFilePathExtractor @@ -20,14 +22,19 @@ public static HashSet GetFilePaths(string xmlFilePath) nsmgr.AddNamespace("ns", "urn:schemas-microsoft-com:sipolicy"); // Select all nodes with the "Allow" tag - XmlNodeList allowNodes = doc.SelectNodes("//ns:Allow", nsmgr); + XmlNodeList? allowNodes = doc.SelectNodes("//ns:Allow", nsmgr); - foreach (XmlNode node in allowNodes) + if (allowNodes != null) { - if (node.Attributes["FilePath"] != null) + + foreach (XmlNode node in allowNodes) { - // Add the file path to the HashSet - filePaths.Add(node.Attributes["FilePath"].Value); + // Ensure node.Attributes is not null + if (node.Attributes != null && node.Attributes["FilePath"] != null) + { + // Add the file path to the HashSet + filePaths.Add(node.Attributes["FilePath"]!.Value); + } } } diff --git a/WDACConfig/WDACConfig Module Files/C#/Variables/CILogIntel.cs b/WDACConfig/WDACConfig Module Files/C#/Variables/CILogIntel.cs index 6f65a48be..e41a2fb30 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Variables/CILogIntel.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Variables/CILogIntel.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace WDACConfig { // Application Control event tags intelligence diff --git a/WDACConfig/WDACConfig Module Files/C#/Variables/GlobalVariables.cs b/WDACConfig/WDACConfig Module Files/C#/Variables/GlobalVariables.cs index edc2333bb..05530b052 100644 --- a/WDACConfig/WDACConfig Module Files/C#/Variables/GlobalVariables.cs +++ b/WDACConfig/WDACConfig Module Files/C#/Variables/GlobalVariables.cs @@ -3,6 +3,8 @@ using System.Globalization; using System.Management.Automation.Host; +#nullable disable + namespace WDACConfig { // This class defines constant variables and makes them available app-domain-wide for PowerShell diff --git a/WDACConfig/WDACConfig Module Files/C#/XMLOps/SignerAndHashBuilder.cs b/WDACConfig/WDACConfig Module Files/C#/XMLOps/SignerAndHashBuilder.cs index 027b54ef6..f7189476d 100644 --- a/WDACConfig/WDACConfig Module Files/C#/XMLOps/SignerAndHashBuilder.cs +++ b/WDACConfig/WDACConfig Module Files/C#/XMLOps/SignerAndHashBuilder.cs @@ -97,7 +97,11 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s { WDACConfig.VerboseLogger.Write("BuildSignerAndHashObjects: Found a null item in data."); } - else if (string.Equals(item["SignatureStatus"].ToString(), "Signed", StringComparison.OrdinalIgnoreCase) && !publisherToHash) + else if ( + item.ContainsKey("SignatureStatus") && + item["SignatureStatus"] != null && + string.Equals(item["SignatureStatus"]?.ToString(), "Signed", StringComparison.OrdinalIgnoreCase) && + !publisherToHash) { signedPublisherData.Add(item); } @@ -122,7 +126,10 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s } // If the file's version is empty or it has no file attribute, then add it to the Publishers array // because FilePublisher rule cannot be created for it - else if (string.Equals(item["SignatureStatus"].ToString(), "Signed", StringComparison.OrdinalIgnoreCase)) + else if ( + item.ContainsKey("SignatureStatus") && + item["SignatureStatus"] != null && + string.Equals(item["SignatureStatus"]?.ToString(), "Signed", StringComparison.OrdinalIgnoreCase)) { // Safely get values from the item and check for null or whitespace bool hasNoFileAttributes = string.IsNullOrWhiteSpace(item.ContainsKey("OriginalFileName") ? item["OriginalFileName"]?.ToString() : null) && @@ -170,61 +177,64 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s // Create a new FilePublisherSignerCreator object WDACConfig.FilePublisherSignerCreator currentFilePublisherSigner = new WDACConfig.FilePublisherSignerCreator(); - // Get the certificate details of the current event data based on the incoming type, they can be stored under different names - ICollection correlatedEventsDataValues = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) - ? ((Hashtable)signedData["CorrelatedEventsData"]).Values - : ((Hashtable)signedData["SignerInfo"]).Values; + // Get the certificate details of the current event data based on the incoming type, they can be stored under different names. + // Safely casting the objects to a HashTable, returning null if the cast fails instead of throwing an exception. + ICollection? correlatedEventsDataValues = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) + ? (signedData["CorrelatedEventsData"] as Hashtable)?.Values + : (signedData["SignerInfo"] as Hashtable)?.Values; if (correlatedEventsDataValues == null) { WDACConfig.VerboseLogger.Write("BuildSignerAndHashObjects: correlatedEventsDataValues is null."); } - - // Loop through each correlated event and process the certificate details - foreach (Hashtable corDataValue in correlatedEventsDataValues) + else { - // currentCorData to store the current SignerInfo/Correlated - WDACConfig.CertificateDetailsCreator? currentCorData = null; + // Loop through each correlated event and process the certificate details + foreach (Hashtable corDataValue in correlatedEventsDataValues) + { + // currentCorData to store the current SignerInfo/Correlated + WDACConfig.CertificateDetailsCreator? currentCorData = null; - // If the file doesn't have Issuer TBS hash (aka Intermediate certificate hash), use the leaf cert's TBS hash and CN instead (aka publisher TBS hash) - // This is according to the ConfigCI's workflow when encountering specific files - // MDE doesn't generate Issuer TBS hash for some files - // For those files, the FilePublisher rule will be created with the file's leaf Certificate details only (Publisher certificate) + // If the file doesn't have Issuer TBS hash (aka Intermediate certificate hash), use the leaf cert's TBS hash and CN instead (aka publisher TBS hash) + // This is according to the ConfigCI's workflow when encountering specific files + // MDE doesn't generate Issuer TBS hash for some files + // For those files, the FilePublisher rule will be created with the file's leaf Certificate details only (Publisher certificate) - // Safely access dictionary values and handle nulls - string? issuerTBSHash = corDataValue.ContainsKey("IssuerTBSHash") ? corDataValue["IssuerTBSHash"]?.ToString() : null; - string? publisherTBSHash = corDataValue.ContainsKey("PublisherTBSHash") ? corDataValue["PublisherTBSHash"]?.ToString() : null; + // Safely access dictionary values and handle nulls + string? issuerTBSHash = corDataValue.ContainsKey("IssuerTBSHash") ? corDataValue["IssuerTBSHash"]?.ToString() : null; + string? publisherTBSHash = corDataValue.ContainsKey("PublisherTBSHash") ? corDataValue["PublisherTBSHash"]?.ToString() : null; - // Perform the check with null-safe values - if (string.IsNullOrWhiteSpace(issuerTBSHash) && !string.IsNullOrWhiteSpace(publisherTBSHash)) - { - WDACConfig.VerboseLogger.Write($"BuildSignerAndHashObjects: Intermediate Certificate TBS hash is empty for the file: {(string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["FileName"] : signedData["File Name"])}, using the leaf certificate TBS hash instead"); - - currentCorData = new WDACConfig.CertificateDetailsCreator( - corDataValue["PublisherTBSHash"].ToString(), - corDataValue["PublisherName"].ToString(), - corDataValue["PublisherTBSHash"].ToString(), - corDataValue["PublisherName"].ToString() - ); - } - else - { - currentCorData = new WDACConfig.CertificateDetailsCreator( - corDataValue["IssuerTBSHash"].ToString(), - corDataValue["IssuerName"].ToString(), - corDataValue["PublisherTBSHash"].ToString(), - corDataValue["PublisherName"].ToString() - ); - } + // Perform the check with null-safe values + if (string.IsNullOrWhiteSpace(issuerTBSHash) && !string.IsNullOrWhiteSpace(publisherTBSHash)) + { + WDACConfig.VerboseLogger.Write($"BuildSignerAndHashObjects: Intermediate Certificate TBS hash is empty for the file: {(string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["FileName"] : signedData["File Name"])}, using the leaf certificate TBS hash instead"); - // Add the Certificate details to the CurrentFilePublisherSigner's CertificateDetails property - if (currentCorData != null) - { - currentFilePublisherSigner.CertificateDetails.Add(currentCorData); + currentCorData = new WDACConfig.CertificateDetailsCreator( + corDataValue["PublisherTBSHash"]!.ToString()!, + corDataValue["PublisherName"]!.ToString()!, + corDataValue["PublisherTBSHash"]!.ToString()!, + corDataValue["PublisherName"]!.ToString()! + ); + + } + else + { + currentCorData = new WDACConfig.CertificateDetailsCreator( + corDataValue["IssuerTBSHash"]!.ToString()!, + corDataValue["IssuerName"]!.ToString()!, + corDataValue["PublisherTBSHash"]!.ToString()!, + corDataValue["PublisherName"]!.ToString()! + ); + } + + // Add the Certificate details to the CurrentFilePublisherSigner's CertificateDetails property + if (currentCorData != null) + { + currentFilePublisherSigner.CertificateDetails.Add(currentCorData); + } } } - #region Initialize properties with null-safe checks string? fileVersionString = signedData.ContainsKey("FileVersion") ? signedData["FileVersion"]?.ToString() : null; string? fileDescription = signedData.ContainsKey("FileDescription") ? signedData["FileDescription"]?.ToString() : null; @@ -260,7 +270,7 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s currentFilePublisherSigner.AuthenticodeSHA256 = sha256; currentFilePublisherSigner.AuthenticodeSHA1 = sha1; - currentFilePublisherSigner.SiSigningScenario = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? int.Parse(signedData["SiSigningScenario"].ToString(), CultureInfo.InvariantCulture) : (string.Equals(signedData["SI Signing Scenario"].ToString(), "Kernel-Mode", StringComparison.OrdinalIgnoreCase) ? 0 : 1); + currentFilePublisherSigner.SiSigningScenario = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? int.Parse(signedData["SiSigningScenario"]!.ToString()!, CultureInfo.InvariantCulture) : (string.Equals(signedData["SI Signing Scenario"]!.ToString(), "Kernel-Mode", StringComparison.OrdinalIgnoreCase) ? 0 : 1); #endregion // Check if necessary details are not empty @@ -286,61 +296,66 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s WDACConfig.PublisherSignerCreator currentPublisherSigner = new WDACConfig.PublisherSignerCreator(); // Get the certificate details of the current event data based on the incoming type, they can be stored under different names - ICollection correlatedEventsDataValues = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) - ? ((Hashtable)signedData["CorrelatedEventsData"]).Values - : ((Hashtable)signedData["SignerInfo"]).Values; + ICollection? correlatedEventsDataValues = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) + ? (signedData?["CorrelatedEventsData"] as Hashtable)?.Values + : (signedData?["SignerInfo"] as Hashtable)?.Values; if (correlatedEventsDataValues == null) { WDACConfig.VerboseLogger.Write("BuildSignerAndHashObjects: correlatedEventsDataValues is null."); } - - // Process each correlated event - foreach (Hashtable corDataValue in correlatedEventsDataValues) + else { - WDACConfig.CertificateDetailsCreator currentCorData = null; + // Process each correlated event + foreach (Hashtable corDataValue in correlatedEventsDataValues) + { + WDACConfig.CertificateDetailsCreator? currentCorData = null; - // Safely access dictionary values and handle nulls - string? issuerTBSHash = corDataValue.ContainsKey("IssuerTBSHash") ? corDataValue["IssuerTBSHash"]?.ToString() : null; - string? issuerName = corDataValue.ContainsKey("IssuerName") ? corDataValue["IssuerName"]?.ToString() : null; - string? publisherTBSHash = corDataValue.ContainsKey("PublisherTBSHash") ? corDataValue["PublisherTBSHash"]?.ToString() : null; - string? publisherName = corDataValue.ContainsKey("PublisherName") ? corDataValue["PublisherName"]?.ToString() : null; + // Safely access dictionary values and handle nulls + string? issuerTBSHash = corDataValue.ContainsKey("IssuerTBSHash") ? corDataValue["IssuerTBSHash"]?.ToString() : null; + string? issuerName = corDataValue.ContainsKey("IssuerName") ? corDataValue["IssuerName"]?.ToString() : null; + string? publisherTBSHash = corDataValue.ContainsKey("PublisherTBSHash") ? corDataValue["PublisherTBSHash"]?.ToString() : null; + string? publisherName = corDataValue.ContainsKey("PublisherName") ? corDataValue["PublisherName"]?.ToString() : null; - // Perform the check with null-safe values - if (string.IsNullOrWhiteSpace(issuerTBSHash) && !string.IsNullOrWhiteSpace(publisherTBSHash)) - { - WDACConfig.VerboseLogger.Write($"BuildSignerAndHashObjects: Intermediate Certificate TBS hash is empty for the file: {(string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["FileName"] : signedData["File Name"])}, using the leaf certificate TBS hash instead"); - - // Create a new CertificateDetailsCreator object with the safely retrieved and used values - currentCorData = new WDACConfig.CertificateDetailsCreator( - publisherTBSHash, - publisherName, - publisherTBSHash, - publisherName - ); - } - else - { - // Create a new CertificateDetailsCreator object with the safely retrieved and used values - currentCorData = new WDACConfig.CertificateDetailsCreator( - issuerTBSHash, - issuerName, - publisherTBSHash, - publisherName - ); - } + // Perform the check with null-safe values + if (string.IsNullOrWhiteSpace(issuerTBSHash) && !string.IsNullOrWhiteSpace(publisherTBSHash)) + { + WDACConfig.VerboseLogger.Write($"BuildSignerAndHashObjects: Intermediate Certificate TBS hash is empty for the file: {(string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData!["FileName"] : signedData!["File Name"])}, using the leaf certificate TBS hash instead"); + + // Create a new CertificateDetailsCreator object with the safely retrieved and used values + currentCorData = new WDACConfig.CertificateDetailsCreator( + publisherTBSHash, + publisherName!, + publisherTBSHash, + publisherName! + ); + } + else + { + // Create a new CertificateDetailsCreator object with the safely retrieved and used values + currentCorData = new WDACConfig.CertificateDetailsCreator( + issuerTBSHash!, + issuerName!, + publisherTBSHash!, + publisherName! + ); + } - // Add the Certificate details to the CurrentPublisherSigner's CertificateDetails property - if (currentCorData != null) - { - currentPublisherSigner.CertificateDetails.Add(currentCorData); + // Add the Certificate details to the CurrentPublisherSigner's CertificateDetails property + if (currentCorData != null) + { + currentPublisherSigner.CertificateDetails.Add(currentCorData); + } } } + // Need to spend more time on this part to properly inspect how the methods getting data from the current method handle the nulls in this properties +#nullable disable currentPublisherSigner.FileName = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["FileName"].ToString() : signedData["File Name"].ToString(); currentPublisherSigner.AuthenticodeSHA256 = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["SHA256"].ToString() : signedData["SHA256 Hash"].ToString(); currentPublisherSigner.AuthenticodeSHA1 = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? signedData["SHA1"].ToString() : signedData["SHA1 Hash"].ToString(); currentPublisherSigner.SiSigningScenario = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) ? int.Parse(signedData["SiSigningScenario"].ToString(), CultureInfo.InvariantCulture) : (string.Equals(signedData["SI Signing Scenario"].ToString(), "Kernel-Mode", StringComparison.OrdinalIgnoreCase) ? 0 : 1); +#nullable restore // Add the completed PublisherSigner to the list publisherSigners.Add(currentPublisherSigner); @@ -369,7 +384,7 @@ public static FileBasedInfoPackage BuildSignerAndHashObjects(Hashtable[] data, s : (hashData.ContainsKey("File Name") ? hashData["File Name"]?.ToString() : null); int siSigningScenario = string.Equals(incomingDataType, "MDEAH", StringComparison.OrdinalIgnoreCase) - ? (hashData.ContainsKey("SiSigningScenario") ? int.Parse(hashData["SiSigningScenario"]?.ToString(), CultureInfo.InvariantCulture) : 1) + ? (hashData.ContainsKey("SiSigningScenario") ? int.Parse(hashData["SiSigningScenario"]?.ToString()!, CultureInfo.InvariantCulture) : 1) : (hashData.ContainsKey("SI Signing Scenario") ? (string.Equals(hashData["SI Signing Scenario"]?.ToString(), "Kernel-Mode", StringComparison.OrdinalIgnoreCase) ? 0 : 1) : 1); if (string.IsNullOrWhiteSpace(sha256) || string.IsNullOrWhiteSpace(sha1) || string.IsNullOrWhiteSpace(fileName)) diff --git a/WDACConfig/WDACConfig.csproj b/WDACConfig/WDACConfig.csproj index 2f0f7924a..3f313c2de 100644 --- a/WDACConfig/WDACConfig.csproj +++ b/WDACConfig/WDACConfig.csproj @@ -14,6 +14,8 @@ WDACConfig Violet Hansen SpyNetGirl + WDACConfig + https://github.com/HotCakeX/Harden-Windows-Security/releases @@ -25,7 +27,7 @@ - +