From 07dad4dd56b7a8683ef293ba08f4f866f4557579 Mon Sep 17 00:00:00 2001 From: Violet Hansen Date: Mon, 27 Jan 2025 13:08:44 +0200 Subject: [PATCH] Added compliance check result export and other improvements (#571) You can now export the results of compliance check in the GUI using a new button that was added. Improved Username detection, making it more resilient. Further improved the GUI and code behinds to be more consistent. Improved the comments in the code to be more accurate. --- .../C#/CimInstances/BitLocker-Enable.cs | 2 +- .../Main files/C#/GUI/BitLocker/Variables.cs | 5 +- .../Main files/C#/GUI/Confirm/View.cs | 229 +++++++++++------- .../Main files/C#/GUI/Log/View.cs | 3 +- .../Main files/C#/GUI/Protection/View.cs | 4 +- .../Others/ConfirmSystemComplianceMethods.cs | 2 +- .../Others/ControlledFolderAccessHandler.cs | 10 +- .../C#/Others/ExportSecurityPolicy.cs | 3 +- .../C#/Others/GetOneDriveDirectories.cs | 10 +- .../Main files/C#/Others/GitExesFinder.cs | 9 +- .../C#/Others/GitHubDesktopFinder.cs | 6 +- .../Main files/C#/Others/GlobalVars.cs | 28 +-- .../Main files/C#/Others/SSHConfigurations.cs | 10 +- .../Main files/C#/Others/WinIdentityUser.cs | 65 ----- .../C#/Protect Methods/BitLockerSettings.cs | 2 +- .../DownloadsDefenseMeasures.cs | 1 + .../MiscellaneousConfigurations.cs | 7 +- .../C#/Protect Methods/WindowsNetworking.cs | 2 +- .../UnprotectWindowsSecurity.cs | 2 +- .../Core/Confirm-SystemCompliance.psm1 | 86 ++----- .../Core/Protect-WindowsSecurity.psm1 | 8 +- .../Core/Unprotect-WindowsSecurity.psm1 | 6 +- .../Main files/Resources/XAML/ASRRules.xaml | 76 +++--- .../Main files/Resources/XAML/Confirm.xaml | 25 +- 24 files changed, 257 insertions(+), 344 deletions(-) delete mode 100644 Harden-Windows-Security Module/Main files/C#/Others/WinIdentityUser.cs diff --git a/Harden-Windows-Security Module/Main files/C#/CimInstances/BitLocker-Enable.cs b/Harden-Windows-Security Module/Main files/C#/CimInstances/BitLocker-Enable.cs index ce9607663..798b3cb37 100644 --- a/Harden-Windows-Security Module/Main files/C#/CimInstances/BitLocker-Enable.cs +++ b/Harden-Windows-Security Module/Main files/C#/CimInstances/BitLocker-Enable.cs @@ -345,7 +345,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace) // Make sure the OS Drive is encrypted first, or else we would add recovery password key protector and then get error about the same problem during auto-unlock key protector enablement - BitLockerVolume OSDriveVolumeInfo = GetEncryptedVolumeInfo(Environment.GetEnvironmentVariable("SystemDrive") ?? "C:\\"); + BitLockerVolume OSDriveVolumeInfo = GetEncryptedVolumeInfo(GlobalVars.SystemDrive); if (OSDriveVolumeInfo.ProtectionStatus is not ProtectionStatus.Protected) { Logger.LogMessage($"Operation System drive must be encrypted first before encrypting Non-OS drives.", LogTypeIntel.ErrorInteractionRequired); diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/BitLocker/Variables.cs b/Harden-Windows-Security Module/Main files/C#/GUI/BitLocker/Variables.cs index 321ce059d..96e71a914 100644 --- a/Harden-Windows-Security Module/Main files/C#/GUI/BitLocker/Variables.cs +++ b/Harden-Windows-Security Module/Main files/C#/GUI/BitLocker/Variables.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Windows; using System.Windows.Controls; using static HardenWindowsSecurity.BitLocker; @@ -77,7 +76,6 @@ public static void CreateBitLockerVolumeViewModel(bool ExportToFile) // List of BitLockerVolumeViewModel objects List viewModelList = []; - foreach (BitLockerVolume Volume in AllBitLockerVolumes) { if (Volume.KeyProtector is not null) @@ -153,8 +151,7 @@ public static void CreateBitLockerVolumeViewModel(bool ExportToFile) } // Notify the user - _ = MessageBox.Show($"BitLocker Recovery Keys have been successfully backed up to {filePath}"); - + Logger.LogMessage($"BitLocker Recovery Keys have been successfully backed up to {filePath}", LogTypeIntel.InformationInteractionRequired); } } diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/Confirm/View.cs b/Harden-Windows-Security Module/Main files/C#/GUI/Confirm/View.cs index cda6029e1..1e5c9b3e7 100644 --- a/Harden-Windows-Security Module/Main files/C#/GUI/Confirm/View.cs +++ b/Harden-Windows-Security Module/Main files/C#/GUI/Confirm/View.cs @@ -46,18 +46,15 @@ private void ConfirmView(object obj) // Parse the XAML content to create a UserControl object UserControl View = (UserControl)XamlReader.Parse(xamlContent); - // Find the SecOpsDataGrid + // Finding elements DataGrid SecOpsDataGrid = (DataGrid)View.FindName("SecOpsDataGrid"); - TextBlock TotalCurrentlyDisplayedSecOpsTextBlock = (TextBlock)View.FindName("TotalCurrentlyDisplayedSecOps"); - - #region ToggleButtons ToggleButton CompliantItemsToggleButton = (ToggleButton)View.FindName("CompliantItemsToggleButton"); ToggleButton NonCompliantItemsToggleButton = (ToggleButton)View.FindName("NonCompliantItemsToggleButton"); - - CompliantItemsToggleButton.IsChecked = true; - NonCompliantItemsToggleButton.IsChecked = true; - #endregion + Button ExportResultsButton = (Button)View.FindName("ExportResultsButton"); + TextBox textBoxFilter = (TextBox)View.FindName("textBoxFilter"); + TextBlock TotalCountTextBlock = (TextBlock)View.FindName("TotalCountTextBlock"); + ComboBox ComplianceCategoriesSelectionComboBox = (ComboBox)View.FindName("ComplianceCategoriesSelectionComboBox"); // Initialize an empty security options collection ObservableCollection SecOpsObservableCollection = []; @@ -73,7 +70,7 @@ private void ConfirmView(object obj) void UpdateCurrentVisibleItemsTextBlock() { // Get the count of all of the current items in the CollectionView - int totalDisplayedItemsCount = SecOpsCollectionView!.OfType().Count(); + int totalDisplayedItemsCount = SecOpsCollectionView.OfType().Count(); // Display the count in a text box in the GUI TotalCurrentlyDisplayedSecOpsTextBlock.Text = $"Showing {totalDisplayedItemsCount} Items"; @@ -83,7 +80,7 @@ void UpdateCurrentVisibleItemsTextBlock() void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompliant) { // Apply a filter to the collection view based on the filter text and toggle buttons - SecOpsCollectionView!.Filter = memberObj => + SecOpsCollectionView.Filter = memberObj => { if (memberObj is SecOp member) { @@ -109,9 +106,6 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl UpdateCurrentVisibleItemsTextBlock(); } - // Finding the textboxFilter element - TextBox textBoxFilter = (TextBox)View.FindName("textBoxFilter"); - #region event handlers for data filtration // Attach event handlers to the text box filter and toggle buttons textBoxFilter.TextChanged += (sender, e) => ApplyFilters(textBoxFilter.Text, CompliantItemsToggleButton.IsChecked ?? false, NonCompliantItemsToggleButton.IsChecked ?? false); @@ -149,101 +143,83 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl #endregion - #region ComboBox - // Finding the ComplianceCategoriesSelectionComboBox ComboBox - ComboBox ComplianceCategoriesSelectionComboBox = (ComboBox)View.FindName("ComplianceCategoriesSelectionComboBox"); // Get the valid compliance category names List catsList = [.. Enum.GetNames()]; - // Add an empty item to the list at the beginning - catsList.Insert(0, ""); + // Add an item to the list at the beginning to be the default value + catsList.Insert(0, "All Categories"); // Set the ComboBox's ItemsSource to the updated list ComplianceCategoriesSelectionComboBox.ItemsSource = catsList; #endregion - // Register the RefreshButton as an element that will be enabled/disabled based on current activity + // Register the elements that will be enabled/disabled based on current global activity ActivityTracker.RegisterUIElement(RefreshButton); + ActivityTracker.RegisterUIElement(ComplianceCategoriesSelectionComboBox); // Set up the Click event handler for the Refresh button RefreshButton.Click += async (sender, e) => { - // Only continue if there is no activity other places - if (!ActivityTracker.IsActive) + if (ActivityTracker.IsActive) { - // mark as activity started - ActivityTracker.IsActive = true; - - // Clear the current security options before starting data generation - SecOpsObservableCollection.Clear(); - SecOpsCollectionView.Refresh(); // Refresh the collection view to clear the DataGrid + return; + } - // Disable the Refresh button while processing - // Set text blocks to empty while new data is being generated - Application.Current.Dispatcher.Invoke(() => - { - TextBlock TotalCountTextBlock = (TextBlock)View.FindName("TotalCountTextBlock"); + // mark as activity started + ActivityTracker.IsActive = true; - if (TotalCountTextBlock is not null) - { - // Update the text of the TextBlock to show the total count - TotalCountTextBlock.Text = "Loading..."; - } + // Clear the current security options before starting data generation + SecOpsObservableCollection.Clear(); + SecOpsCollectionView.Refresh(); // Refresh the collection view to clear the DataGrid - UpdateCurrentVisibleItemsTextBlock(); - }); + // Set text blocks to empty while new data is being generated + Application.Current.Dispatcher.Invoke(() => + { + TotalCountTextBlock.Text = "Loading..."; + CompliantItemsToggleButton.Content = "Compliant Items"; + NonCompliantItemsToggleButton.Content = "Non-Compliant Items"; - // Run the method asynchronously in a different thread - await Task.Run(() => - { - // Get fresh data for compliance checking - Initializer.Initialize(null, true); + UpdateCurrentVisibleItemsTextBlock(); + }); - // initialize the variable to null - string? SelectedCategory = null; + // Run the method asynchronously in a different thread + await Task.Run(() => + { + // Get fresh data for compliance checking + Initializer.Initialize(null, true); - // Use the App dispatcher since this is being done in a different thread - app.Dispatcher.Invoke(() => - { - if (ComplianceCategoriesSelectionComboBox.SelectedItem is not null) - { - // Get the currently selected value in the Compliance Checking category ComboBox if it exists - var SelectedComplianceCategories = ComplianceCategoriesSelectionComboBox.SelectedItem; - - // Get the currently selected compliance category - SelectedCategory = SelectedComplianceCategories?.ToString(); - } - }); - - // if user selected a category for compliance checking - if (!string.IsNullOrEmpty(SelectedCategory)) - { - // Perform the compliance check using the selected compliance category - InvokeConfirmation.Invoke([SelectedCategory]); - } - else - { - // Perform the compliance check for all categories - InvokeConfirmation.Invoke(null); - } - }); + // Get the currently selected compliance category - Use the App dispatcher since this is being done in a different thread + string SelectedCategory = app.Dispatcher.Invoke(() => ComplianceCategoriesSelectionComboBox.SelectedItem.ToString()!); - // After InvokeConfirmation is completed, update the security options collection - await Application.Current.Dispatcher.InvokeAsync(() => + // If all categories is selected which is the default + if (string.Equals(SelectedCategory, "All Categories", StringComparison.OrdinalIgnoreCase)) + { + // Perform the compliance check for all categories + InvokeConfirmation.Invoke(null); + } + // if user selected a category for compliance checking + else { - LoadMembers(); // Load updated security options - RefreshButton.IsChecked = false; // Uncheck the Refresh button + // Perform the compliance check using the selected compliance category + InvokeConfirmation.Invoke([SelectedCategory]); + } + }); - UpdateCurrentVisibleItemsTextBlock(); - }); + // After InvokeConfirmation is completed, update the security options collection + await Application.Current.Dispatcher.InvokeAsync(() => + { + LoadMembers(); // Load updated security options + RefreshButton.IsChecked = false; // Uncheck the Refresh button - // mark as activity completed - ActivityTracker.IsActive = false; - } + UpdateCurrentVisibleItemsTextBlock(); + }); + + // mark as activity completed + ActivityTracker.IsActive = false; }; /// @@ -289,17 +265,11 @@ void LoadMembers() /// If set to true, this method will display end of confirmation toast notification void UpdateTotalCount(bool ShowNotification) { - // calculates the total number of all security options across all lists, so all the items in each category that exist in the values of the main dictionary object int totalCount = GlobalVars.FinalMegaObject.Values.Sum(list => list.Count); - // Find the TextBlock used to display the total count - TextBlock TotalCountTextBlock = (TextBlock)View.FindName("TotalCountTextBlock"); - if (TotalCountTextBlock is not null) - { - // Update the text of the TextBlock to show the total count - TotalCountTextBlock.Text = $"{totalCount} Total Verifiable Security Checks"; - } + // Update the text of the TextBlock to show the total count + TotalCountTextBlock.Text = $"{totalCount} Total Verifiable Security Checks"; // Get the count of the compliant items string CompliantItemsCount = SecOpsCollectionView.SourceCollection @@ -324,6 +294,89 @@ void UpdateTotalCount(bool ShowNotification) } } + // Event handler for the Export Results button + ExportResultsButton.Click += async (sender, e) => + { + try + { + ExportResultsButton.IsEnabled = false; + + await Task.Run(() => + { + // Show the save file dialog to let the user pick the save location + Microsoft.Win32.SaveFileDialog saveFileDialog = new() + { + FileName = $"Harden Windows Security Compliance Check Results at {DateTime.Now:yyyy-MM-dd HH-mm-ss}", // Default file name + DefaultExt = ".csv", // Default file extension + Filter = "CSV File (.csv)|*.csv" // Filter files by extension + }; + + // Show the dialog and check if the user picked a file + bool? result = saveFileDialog.ShowDialog(); + + if (result == true) + { + // Get the selected file path from the dialog + string filePath = saveFileDialog.FileName; + + try + { + ExportSecOpsToCsv(filePath, SecOpsObservableCollection); + } + catch (Exception ex) + { + Logger.LogMessage($"Failed to export the results to the file: {ex.Message}", LogTypeIntel.ErrorInteractionRequired); + } + + Logger.LogMessage($"Compliance check results have been successfully exported.", LogTypeIntel.InformationInteractionRequired); + } + }); + } + finally + { + ExportResultsButton.IsEnabled = true; + } + }; + + + // To Export the results of the compliance checking to a file + void ExportSecOpsToCsv(string filePath, ObservableCollection secOps) + { + // Defining the header row + string[] headers = ["FriendlyName", "Compliant", "Value", "Name", "Category", "Method"]; + + // Open the file for writing + using StreamWriter writer = new(filePath); + + // Write the header row + writer.WriteLine(string.Join(",", headers.Select(header => $"\"{header}\""))); + + // Write each SecOp object as a row in the CSV + foreach (SecOp secOp in secOps) + { + string[] row = [ + EscapeForCsv(secOp.FriendlyName), + EscapeForCsv(secOp.Compliant.ToString(CultureInfo.InvariantCulture)), + EscapeForCsv(secOp.Value), + EscapeForCsv(secOp.Name), + EscapeForCsv(secOp.Category.ToString()), + EscapeForCsv(secOp.Method) + ]; + + writer.WriteLine(string.Join(",", row)); + } + } + + // Local method to enclose values in double quotes + string EscapeForCsv(string? value) + { + // If the value is null, empty or whitespace, return an empty string + if (string.IsNullOrWhiteSpace(value)) return string.Empty; + + // Otherwise, escape double quotes and wrap the value in double quotes + return $"\"{value.Replace("\"", "\"\"", StringComparison.OrdinalIgnoreCase)}\""; + } + // Cache the Confirm view for future use _viewCache["ConfirmView"] = View; diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/Log/View.cs b/Harden-Windows-Security Module/Main files/C#/GUI/Log/View.cs index c3cd681c5..2dbc5e306 100644 --- a/Harden-Windows-Security Module/Main files/C#/GUI/Log/View.cs +++ b/Harden-Windows-Security Module/Main files/C#/GUI/Log/View.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Markup; @@ -74,7 +73,7 @@ private void LogsView(object obj) // Write the text content from the TextBox to the file File.WriteAllText(filePath, GUILogs.MainLoggerTextBox.Text); - _ = MessageBox.Show("Logs successfully saved.", "Success", MessageBoxButton.OK, MessageBoxImage.Information); + Logger.LogMessage("Logs successfully saved.", LogTypeIntel.InformationInteractionRequired); } }; diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/Protection/View.cs b/Harden-Windows-Security Module/Main files/C#/GUI/Protection/View.cs index abd854c99..62cf0a602 100644 --- a/Harden-Windows-Security Module/Main files/C#/GUI/Protection/View.cs +++ b/Harden-Windows-Security Module/Main files/C#/GUI/Protection/View.cs @@ -609,9 +609,7 @@ void AddEventHandlers() #region Display a Welcome message - string nameToDisplay = (!string.IsNullOrWhiteSpace(GlobalVars.userFullName)) ? GlobalVars.userFullName : GlobalVars.userName; - - Logger.LogMessage(Environment.IsPrivilegedProcess ? $"Hello {nameToDisplay}, you have Administrator privileges" : $"Hello {nameToDisplay}, you don't have Administrator privileges, some categories are disabled", LogTypeIntel.Information); + Logger.LogMessage(Environment.IsPrivilegedProcess ? $"Hello {Environment.UserName}, you have Administrator privileges" : $"Hello {Environment.UserName}, you don't have Administrator privileges, some categories are disabled", LogTypeIntel.Information); #endregion // Use Dispatcher.Invoke to update the UI thread diff --git a/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs b/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs index bfbf6c34e..8e7c11766 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs @@ -529,7 +529,7 @@ private static Task VerifyBitLockerSettings() // OS Drive encryption verifications // Check if BitLocker is on for the OS Drive // The ProtectionStatus remains off while the drive is encrypting or decrypting - BitLocker.BitLockerVolume volumeInfo = BitLocker.GetEncryptedVolumeInfo(Environment.GetEnvironmentVariable("SystemDrive") ?? "C:\\"); + BitLocker.BitLockerVolume volumeInfo = BitLocker.GetEncryptedVolumeInfo(GlobalVars.SystemDrive); if (volumeInfo.ProtectionStatus is BitLocker.ProtectionStatus.Protected) { diff --git a/Harden-Windows-Security Module/Main files/C#/Others/ControlledFolderAccessHandler.cs b/Harden-Windows-Security Module/Main files/C#/Others/ControlledFolderAccessHandler.cs index bce2019e0..054637d5e 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/ControlledFolderAccessHandler.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/ControlledFolderAccessHandler.cs @@ -100,18 +100,10 @@ public static void Start(bool PowerShell, bool PowerCFG) if (PowerCFG && !PowerCfgAdded) { - // Get the powercfg.exe path - string? systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); - - if (string.IsNullOrEmpty(systemDrive)) - { - throw new InvalidOperationException("SystemDrive environment variable is not set."); - } - Logger.LogMessage("Temporarily adding the PowerCfg.exe executable to the Controlled Folder Access allowed apps list to set Hibernate type to full in BitLocker category.", LogTypeIntel.Information); // Add the powercfg.exe path to the CFA Exclusion list - _ = CFAExclusionsToBeAdded.Add(Path.Combine(systemDrive, "Windows", "System32", "powercfg.exe")); + _ = CFAExclusionsToBeAdded.Add(Path.Combine(GlobalVars.SystemDrive, "Windows", "System32", "powercfg.exe")); PowerCfgAdded = true; } diff --git a/Harden-Windows-Security Module/Main files/C#/Others/ExportSecurityPolicy.cs b/Harden-Windows-Security Module/Main files/C#/Others/ExportSecurityPolicy.cs index f534a121e..51ac58547 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/ExportSecurityPolicy.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/ExportSecurityPolicy.cs @@ -12,12 +12,11 @@ public static void ExportSecurityPolicy() { // Assuming securityPolicyInfPath is defined in your environment string securityPolicyInfPath = GlobalVars.securityPolicyInfPath; - string? systemDrive = Environment.GetEnvironmentVariable("SystemDrive") ?? throw new InvalidOperationException("SystemDrive environment variable is not set."); // Create the process start info ProcessStartInfo processStartInfo = new() { - FileName = $"{systemDrive}\\Windows\\System32\\Secedit.exe", + FileName = $"{GlobalVars.SystemDrive}\\Windows\\System32\\Secedit.exe", Arguments = $"/export /cfg \"{securityPolicyInfPath}\"", // RedirectStandardOutput = false, RedirectStandardError = true, // Redirect the StandardError stream diff --git a/Harden-Windows-Security Module/Main files/C#/Others/GetOneDriveDirectories.cs b/Harden-Windows-Security Module/Main files/C#/Others/GetOneDriveDirectories.cs index 99eb0991e..cf33c1d04 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/GetOneDriveDirectories.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/GetOneDriveDirectories.cs @@ -18,16 +18,8 @@ internal static List Get() // List to store the OneDrive directories found List directoriesList = []; - // Retrieve the system drive - string systemDrive = Environment.GetEnvironmentVariable("SystemDrive") ?? string.Empty; - - if (string.IsNullOrEmpty(systemDrive)) - { - throw new InvalidOperationException("SystemDrive environment variable is not set."); - } - // Combine system drive with "Users" to get the path to the Users directory - string usersPath = Path.Combine(systemDrive, "Users"); + string usersPath = Path.Combine(GlobalVars.SystemDrive, "Users"); try { diff --git a/Harden-Windows-Security Module/Main files/C#/Others/GitExesFinder.cs b/Harden-Windows-Security Module/Main files/C#/Others/GitExesFinder.cs index 6983cd36c..39fe4e05d 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/GitExesFinder.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/GitExesFinder.cs @@ -6,11 +6,14 @@ namespace HardenWindowsSecurity; internal static class GitExesFinder { - // This method searches for .exe files in the specified path for Standalone Git program and returns a list of FileInfo objects + /// + /// This method searches for .exe files in the specified path for Standalone Git program and returns a list of FileInfo objects + /// + /// internal static List? Find() { // Define the base path to search - string basePath = @"C:\Program Files\Git"; + string basePath = Path.Combine(GlobalVars.SystemDrive, "Program Files", "Git"); // Check if the base path exists if (!Directory.Exists(basePath)) @@ -34,7 +37,7 @@ internal static class GitExesFinder } // Return null if no files were found - if (fileList.Count == 0) + if (fileList.Count is 0) { return null; } diff --git a/Harden-Windows-Security Module/Main files/C#/Others/GitHubDesktopFinder.cs b/Harden-Windows-Security Module/Main files/C#/Others/GitHubDesktopFinder.cs index 2fbcaf46b..f4bb717ea 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/GitHubDesktopFinder.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/GitHubDesktopFinder.cs @@ -10,10 +10,8 @@ internal static class GitHubDesktopFinder // This method searches for .exe files in the specified path and returns a list of FileInfo objects internal static List? Find() { - // Get the current user's name - string userName = Environment.UserName; // Define the base path to search - string basePath = $@"C:\Users\{userName}\AppData\Local\GitHubDesktop"; + string basePath = Path.Combine(GlobalVars.SystemDrive, "Users", Environment.UserName, "AppData", "Local", "GitHubDesktop"); // Check if the base path exists if (!Directory.Exists(basePath)) @@ -38,7 +36,7 @@ internal static class GitHubDesktopFinder } // Return null if no files were found - if (fileList.Count == 0) + if (fileList.Count is 0) { return null; } diff --git a/Harden-Windows-Security Module/Main files/C#/Others/GlobalVars.cs b/Harden-Windows-Security Module/Main files/C#/Others/GlobalVars.cs index bfcbf041f..cadacc8b2 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/GlobalVars.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/GlobalVars.cs @@ -27,23 +27,15 @@ public static class GlobalVars public static string path; #pragma warning restore - // Stores the output of Get-MpComputerStatus which happens early on in the root module .psm1 file + // Stores the MpComputerStatus CIM results public static dynamic? MDAVConfigCurrent; - // Stores the output of Get-MpPreference which happens early on in the root module .psm1 file + // Stores the MpPreference CIM results public static dynamic? MDAVPreferencesCurrent; - // - // The following variables are only used by the Confirm-SystemCompliance cmdlet - // - // Total number of Compliant values public static int TotalNumberOfTrueCompliantValues; - // - // The following variables are only used by the Protect-WindowsSecurity cmdlet - // - // The working directory used by the Protect-WindowsSecurity cmdlet internal static string WorkingDir = Path.Combine(Path.GetTempPath(), "HardeningXStuff"); @@ -101,13 +93,13 @@ public static class GlobalVars // Create an empty dictionary to store the System Security Policies from the security_policy.inf file internal static Dictionary> SystemSecurityPoliciesIniObject = []; - // a variable to store the security policies CSV file parse output + // To store the security policies CSV file parse output internal static List? SecurityPolicyRecords; - // the explicit path to save the CurrentlyAppliedMitigations.xml file + // The explicit path to save the CurrentlyAppliedMitigations.xml file internal static string CurrentlyAppliedMitigations = Path.Combine(WorkingDir, "CurrentlyAppliedMitigations.xml"); - // variable that contains the results of all of the related MDM CimInstances that can be interacted with using Administrator privilege + // Contains the results of all of the related MDM CimInstances that can be interacted with using Administrator privilege internal static List? MDMResults; // To store the Firewall Domain MDM profile parsed JSON output @@ -125,12 +117,6 @@ public static class GlobalVars // To store the System MDM parsed JSON output internal static Hashtable? MDM_Policy_Result01_System02; - // Call GetCurrentIdentity() once and store the result in a private static variable - private static readonly CurrentUserIdentityResult _identity = WinIdentityUser.GetCurrentIdentity(); - - // Initialize the properties using the properties of the single _identity instance - internal static readonly string userName = _identity.userName; - internal static readonly string userSID = _identity.userSID; - internal static readonly string? userFullName = _identity.userFullName; - + // To store the path to the system drive + internal static readonly string SystemDrive = Environment.GetEnvironmentVariable("SystemDrive") ?? "C:"; } diff --git a/Harden-Windows-Security Module/Main files/C#/Others/SSHConfigurations.cs b/Harden-Windows-Security Module/Main files/C#/Others/SSHConfigurations.cs index 28927b37e..575892392 100644 --- a/Harden-Windows-Security Module/Main files/C#/Others/SSHConfigurations.cs +++ b/Harden-Windows-Security Module/Main files/C#/Others/SSHConfigurations.cs @@ -7,7 +7,8 @@ namespace HardenWindowsSecurity; internal static class SSHConfigurations { - private static readonly string SSHClientUserConfigDirectory = Path.Combine(Environment.GetEnvironmentVariable("SystemDrive")!, "Users", GlobalVars.userName, ".ssh"); + private static readonly string UserDirectory = Path.Combine(GlobalVars.SystemDrive, "Users", Environment.UserName); + private static readonly string SSHClientUserConfigDirectory = Path.Combine(UserDirectory, ".ssh"); private static readonly string SSHClientUserConfigFile = Path.Combine(SSHClientUserConfigDirectory, "config"); // Secure MACs configurations for SSH @@ -18,6 +19,13 @@ internal static void SecureMACs() Logger.LogMessage("Checking for SSH client user configuration", LogTypeIntel.Information); + // First ensure the detected username is valid so we don't create a directory in non-existent user directory + if (!Path.Exists(UserDirectory)) + { + Logger.LogMessage($"User directory not found at {UserDirectory} because the username {Environment.UserName} was not valid, skipping SSH client configuration check.", LogTypeIntel.Warning); + return; + } + // Ensure the SSH client directory exists if (!Directory.Exists(SSHClientUserConfigDirectory)) { diff --git a/Harden-Windows-Security Module/Main files/C#/Others/WinIdentityUser.cs b/Harden-Windows-Security Module/Main files/C#/Others/WinIdentityUser.cs deleted file mode 100644 index 6cf8b54b0..000000000 --- a/Harden-Windows-Security Module/Main files/C#/Others/WinIdentityUser.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Linq; -using System.Security.Principal; - -namespace HardenWindowsSecurity; - - -internal sealed class CurrentUserIdentityResult -{ - internal required string userName; - internal required string userSID; - internal string? userFullName; -} - - -internal static class WinIdentityUser -{ - internal static CurrentUserIdentityResult GetCurrentIdentity() - { - // Save the username in the class variable - WindowsIdentity CurrentUserResult = WindowsIdentity.GetCurrent(); - string userSID = CurrentUserResult.User!.Value.ToString(); - - string userName; - string? userFullName = null; - - // The LocalUserRetriever.Get() method doesn't return SYSTEM so we have to handle it separately - // In case the Harden Windows Security is running as SYSTEM - if (CurrentUserResult.IsSystem) - { - userName = "SYSTEM"; - userFullName = "SYSTEM"; - } - - else - { - - try - { - - LocalUser CurrentLocalUser = LocalUserRetriever.Get() - .First(Lu => string.Equals(Lu.SID, userSID, StringComparison.OrdinalIgnoreCase)); - - userName = CurrentLocalUser.Name!; - userFullName = CurrentLocalUser.FullName; - - } - // We don't fail it if username or full name can't be detected - // Any parts of the Harden Windows Security relying on their values must gracefully handle empty or inaccurate values. - catch - { - userName = string.Empty; - Logger.LogMessage($"Could not find UserName or FullName of the current user with the SID {userSID}", LogTypeIntel.Warning); - } - } - - - return new CurrentUserIdentityResult - { - userName = userName, - userSID = userSID, - userFullName = userFullName - }; - } -} diff --git a/Harden-Windows-Security Module/Main files/C#/Protect Methods/BitLockerSettings.cs b/Harden-Windows-Security Module/Main files/C#/Protect Methods/BitLockerSettings.cs index 1e25aa411..40ecab702 100644 --- a/Harden-Windows-Security Module/Main files/C#/Protect Methods/BitLockerSettings.cs +++ b/Harden-Windows-Security Module/Main files/C#/Protect Methods/BitLockerSettings.cs @@ -45,7 +45,7 @@ public static void Invoke() // Only perform the check if the system is not a virtual machine var isVirtualMachine = PropertyHelper.GetPropertyValue(GlobalVars.MDAVConfigCurrent, "IsVirtualMachine"); // Get the OS Drive encryption status - BitLocker.BitLockerVolume volumeInfo = BitLocker.GetEncryptedVolumeInfo(Environment.GetEnvironmentVariable("SystemDrive") ?? "C:\\"); + BitLocker.BitLockerVolume volumeInfo = BitLocker.GetEncryptedVolumeInfo(GlobalVars.SystemDrive); // Only attempt to set Hibernate file size to full if the OS drive is BitLocker encrypted // And system is not virtual machine diff --git a/Harden-Windows-Security Module/Main files/C#/Protect Methods/DownloadsDefenseMeasures.cs b/Harden-Windows-Security Module/Main files/C#/Protect Methods/DownloadsDefenseMeasures.cs index fcf6a08a2..1a21b0177 100644 --- a/Harden-Windows-Security Module/Main files/C#/Protect Methods/DownloadsDefenseMeasures.cs +++ b/Harden-Windows-Security Module/Main files/C#/Protect Methods/DownloadsDefenseMeasures.cs @@ -57,6 +57,7 @@ public static void Invoke() // Get the System Downloads folder path int result = NativeMethods.SHGetKnownFolderPath(ref FolderDownloads, 0, IntPtr.Zero, out pathPtr); + // E.g., this will return non-zero if running as SYSTEM if (result is 0) // S_OK { downloadsPath = Marshal.PtrToStringUni(pathPtr); diff --git a/Harden-Windows-Security Module/Main files/C#/Protect Methods/MiscellaneousConfigurations.cs b/Harden-Windows-Security Module/Main files/C#/Protect Methods/MiscellaneousConfigurations.cs index af3aabe63..90c50d7e3 100644 --- a/Harden-Windows-Security Module/Main files/C#/Protect Methods/MiscellaneousConfigurations.cs +++ b/Harden-Windows-Security Module/Main files/C#/Protect Methods/MiscellaneousConfigurations.cs @@ -56,17 +56,16 @@ public static void Invoke() // auditpol /list /subcategory:* /r // Event Viewer custom views are saved in "$env:SystemDrive\ProgramData\Microsoft\Event Viewer\Views". files in there can be backed up and restored on new Windows installations. - string? systemDrive = Environment.GetEnvironmentVariable("SystemDrive") ?? throw new ArgumentNullException("SystemDrive cannot be null."); // Create the directory if it doesn't exist - if (!Directory.Exists(Path.Combine(systemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script"))) + if (!Directory.Exists(Path.Combine(GlobalVars.SystemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script"))) { - _ = Directory.CreateDirectory(Path.Combine(systemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script")); + _ = Directory.CreateDirectory(Path.Combine(GlobalVars.SystemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script")); } foreach (string file in Directory.GetFiles(Path.Combine(GlobalVars.path, "Resources", "EventViewerCustomViews"))) { - File.Copy(file, Path.Combine(systemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script", Path.GetFileName(file)), true); + File.Copy(file, Path.Combine(GlobalVars.SystemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script", Path.GetFileName(file)), true); } SSHConfigurations.SecureMACs(); diff --git a/Harden-Windows-Security Module/Main files/C#/Protect Methods/WindowsNetworking.cs b/Harden-Windows-Security Module/Main files/C#/Protect Methods/WindowsNetworking.cs index 6c6a9148b..48ff2beaa 100644 --- a/Harden-Windows-Security Module/Main files/C#/Protect Methods/WindowsNetworking.cs +++ b/Harden-Windows-Security Module/Main files/C#/Protect Methods/WindowsNetworking.cs @@ -6,7 +6,7 @@ namespace HardenWindowsSecurity; public static partial class WindowsNetworking { /// - /// Runs the Windows Networking Hardening category + /// Runs the Windows Networking Hardening category /// /// public static void Invoke() diff --git a/Harden-Windows-Security Module/Main files/C#/Unprotect Methods/UnprotectWindowsSecurity.cs b/Harden-Windows-Security Module/Main files/C#/Unprotect Methods/UnprotectWindowsSecurity.cs index da7b5d28a..81a5fee4d 100644 --- a/Harden-Windows-Security Module/Main files/C#/Unprotect Methods/UnprotectWindowsSecurity.cs +++ b/Harden-Windows-Security Module/Main files/C#/Unprotect Methods/UnprotectWindowsSecurity.cs @@ -128,7 +128,7 @@ public static void Unprotect() #region Custom event viewer views // Defining the directory path to the Harden Windows Security's event viewer custom views - string directoryPath = Path.Combine(Environment.GetEnvironmentVariable("SystemDrive")!, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script"); + string directoryPath = Path.Combine(GlobalVars.SystemDrive, "ProgramData", "Microsoft", "Event Viewer", "Views", "Hardening Script"); // Check if the directory exists if (Directory.Exists(directoryPath)) diff --git a/Harden-Windows-Security Module/Main files/Core/Confirm-SystemCompliance.psm1 b/Harden-Windows-Security Module/Main files/Core/Confirm-SystemCompliance.psm1 index 896c3bd73..4f3c5ea38 100644 --- a/Harden-Windows-Security Module/Main files/Core/Confirm-SystemCompliance.psm1 +++ b/Harden-Windows-Security Module/Main files/Core/Confirm-SystemCompliance.psm1 @@ -4,13 +4,11 @@ function Confirm-SystemCompliance { [ArgumentCompleter({ param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters) $Existing = $CommandAst.FindAll( - # The predicate scriptblock to define the criteria for filtering the AST nodes { $Args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst] }, - $false # The recurse flag, whether to search nested scriptblocks or not. + $false ).Value - foreach ($Item in [Enum]::GetNames([HardenWindowsSecurity.ComplianceCategories])) { if ($Item -notin $Existing) { $Item } } @@ -36,72 +34,26 @@ function Confirm-SystemCompliance { [HardenWindowsSecurity.Logger]::LogMessage('Checking for updates...', [HardenWindowsSecurity.LogTypeIntel]::Information) Update-HardenWindowsSecurity -InvocationStatement $MyInvocation.Statement } - if ((Get-CimInstance -ClassName Win32_OperatingSystem -Verbose:$false).OperatingSystemSKU -in '101', '100') { Write-Warning -Message 'The Windows Home edition has been detected, many features are unavailable in this edition.' } - #Region Colors [System.Collections.Hashtable]$global:ColorsMap = @{ - Plum = @{ - Code = '221', '160', '221' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(221,160,221))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - Orchid = @{ - Code = '218', '112', '214' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(218,112,214))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - Fuchsia = @{ - Code = '255', '0', '255' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,0,255))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - MediumOrchid = @{ - Code = '186', '85', '211' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(186,85,211))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - MediumPurple = @{ - Code = '147', '112', '219' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(147,112,219))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - BlueViolet = @{ - Code = '138', '43', '226' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(138,43,226))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - AndroidGreen = @{ - Code = '176', '191', '26' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(176,191,26))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - Pink = @{ - Code = '255', '192', '203' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,192,203))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - HotPink = @{ - Code = '255', '105', '180' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,105,180))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - DeepPink = @{ - Code = '255', '20', '147' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,20,147))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - MintGreen = @{ - Code = '152', '255', '152' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(152,255,152))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - Orange = @{ - Code = '255', '165', '0' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,165,0))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - SkyBlue = @{ - Code = '135', '206', '235' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(135,206,235))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } - Daffodil = @{ - Code = '255', '255', '49' - ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,255,49))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } - } + Plum = @{ Code = '221', '160', '221'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(221,160,221))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + Orchid = @{ Code = '218', '112', '214'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(218,112,214))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + Fuchsia = @{ Code = '255', '0', '255'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,0,255))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + MediumOrchid = @{ Code = '186', '85', '211'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(186,85,211))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + MediumPurple = @{ Code = '147', '112', '219'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(147,112,219))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + BlueViolet = @{ Code = '138', '43', '226'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(138,43,226))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + AndroidGreen = @{ Code = '176', '191', '26'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(176,191,26))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + Pink = @{ Code = '255', '192', '203'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,192,203))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + HotPink = @{ Code = '255', '105', '180'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,105,180))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + DeepPink = @{ Code = '255', '20', '147'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,20,147))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + MintGreen = @{ Code = '152', '255', '152'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(152,255,152))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + Orange = @{ Code = '255', '165', '0'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,165,0))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + SkyBlue = @{ Code = '135', '206', '235'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(135,206,235))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } + Daffodil = @{ Code = '255', '255', '49'; ScriptBlock = { Write-Output -InputObject "$($PSStyle.Foreground.FromRGB(255,255,49))$($PSStyle.Reverse)$($Args[0])$($PSStyle.Reset)" } } } - - # Defining a validate set class for the colors Class Colorsx : System.Management.Automation.IValidateSetValuesGenerator { [System.String[]] GetValidValues() { $Colorsx = @($global:ColorsMap.Keys) @@ -109,7 +61,6 @@ function Confirm-SystemCompliance { } } [System.Drawing.Color[]]$Global:Colors = @([System.Drawing.Color]::SkyBlue, [System.Drawing.Color]::Pink, [System.Drawing.Color]::HotPink, [System.Drawing.Color]::Lavender, [System.Drawing.Color]::LightGreen, [System.Drawing.Color]::Coral, [System.Drawing.Color]::Plum, [System.Drawing.Color]::Gold) - [System.Management.Automation.ScriptBlock]$WriteRainbow = { Param([System.String]$Text) $StringBuilder = [System.Text.StringBuilder]::new() @@ -125,7 +76,6 @@ function Confirm-SystemCompliance { try { Write-Progress -Activity 'Performing Compliance Check...' -Status 'Running' -PercentComplete 50 [HardenWindowsSecurity.InvokeConfirmation]::Invoke($Categories) - if ($ExportToCSV) { # Create an empty list to store the results based on the category order by sorting the concurrent hashtable $AllOrderedResults = [System.Collections.Generic.List[HardenWindowsSecurity.IndividualResult]]::new() @@ -137,7 +87,6 @@ function Confirm-SystemCompliance { } } } - # Store the results in the current working directory in a CSV files $AllOrderedResults | ConvertTo-Csv | Out-File -FilePath ".\Compliance Check Output $(Get-Date -Format "MM-dd-yyyy 'at' HH-mm-ss").CSV" -Force } function Set-CategoryFormat { @@ -238,11 +187,12 @@ function Confirm-SystemCompliance { } <# .SYNOPSIS - Checks the compliance of a system with the Harden Windows Security script guidelines + Checks the compliance of a system according to the Harden Windows Security repository's guidelines. + Use the GUI for much better experience: Protect-WindowsSecurity -GUI .LINK https://github.com/HotCakeX/Harden-Windows-Security/wiki/Harden%E2%80%90Windows%E2%80%90Security%E2%80%90Module .DESCRIPTION - Checks the compliance of a system with the Harden Windows Security script. Checks the applied Group policies, registry keys and PowerShell cmdlets used by the hardening script. + Checks the compliance of a system according to the Harden Windows Security repository's guidelines. .EXAMPLE $Result = Confirm-SystemCompliance -ShowAsObjectsOnly $Result['MicrosoftDefender'] diff --git a/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 b/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 index e17738c5f..ef891b46c 100644 --- a/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 +++ b/Harden-Windows-Security Module/Main files/Core/Protect-WindowsSecurity.psm1 @@ -14,7 +14,6 @@ Function Protect-WindowsSecurity { }, $false # The recurse flag, whether to search nested scriptblocks or not. ).Value - ([HardenWindowsSecurity.GlobalVars]::HardeningCategorieX) | ForEach-Object -Process { if ($_ -notin $Existing) { $_ @@ -418,17 +417,18 @@ Function Protect-WindowsSecurity { [HardenWindowsSecurity.Logger]::LogMessage("Protect-WindowsSecurity completed in $($StopWatch.Elapsed.Hours) Hours - $($StopWatch.Elapsed.Minutes) Minutes - $($StopWatch.Elapsed.Seconds) Seconds - $($StopWatch.Elapsed.Milliseconds) Milliseconds - $($StopWatch.Elapsed.Microseconds) Microseconds - $($StopWatch.Elapsed.Nanoseconds) Nanoseconds", [HardenWindowsSecurity.LogTypeIntel]::Information) } - # If no errors Occurred, recycle the current session for there can't be more than 1 Application in the same App Domain + # If no errors occurred, recycle the current session for there can't be more than 1 Application in the same App Domain if (!$ErrorsOccurred) { pwsh.exe -NoProfile -NoLogo -NoExit } } } <# .SYNOPSIS Applies the hardening measures described in the GitHub readme. + Use the GUI for much better experience: Protect-WindowsSecurity -GUI .LINK https://github.com/HotCakeX/Harden-Windows-Security .DESCRIPTION - Applies the hardening measures on a Windows client OS. You can run this cmdlet in interactive or headless/unattended mode. + Applies the hardening security measures on Windows OS. You can run this cmdlet in interactive or headless/unattended mode. In interactive mode, you will be prompted for confirmation before applying each category and sub-category. In headless/unattended mode, you can specify which categories to apply without the need for user interaction. When running in headless/unattended mode, you can control the sub-categories of each category by using the following switch parameters: @@ -503,7 +503,7 @@ Function Protect-WindowsSecurity { .EXAMPLE Protect-WindowsSecurity -GUI - This example will allow you to use the Graphical User Interface. + This example will allow you to use the Graphical User Interface. (highly recommended) .EXAMPLE Protect-WindowsSecurity -GUI -Offline diff --git a/Harden-Windows-Security Module/Main files/Core/Unprotect-WindowsSecurity.psm1 b/Harden-Windows-Security Module/Main files/Core/Unprotect-WindowsSecurity.psm1 index 48239f306..afda51adb 100644 --- a/Harden-Windows-Security Module/Main files/Core/Unprotect-WindowsSecurity.psm1 +++ b/Harden-Windows-Security Module/Main files/Core/Unprotect-WindowsSecurity.psm1 @@ -31,7 +31,7 @@ Function Unprotect-WindowsSecurity { # Prompt for confirmation before proceeding if ($PSCmdlet.ShouldProcess('This PC', 'Removing the Hardening Measures Applied by the Protect-WindowsSecurity Cmdlet')) { - # doing a try-finally block on the entire script so that when CTRL + C is pressed to forcefully exit the script, + # doing a try-finally block on the entire section so that when CTRL + C is pressed to forcefully exit the script, # or break is passed, clean up will still happen for secure exit try { Write-Progress -Activity 'Removing protections from Windows' -Status 'Unprotecting' -PercentComplete 50 @@ -69,11 +69,11 @@ Function Unprotect-WindowsSecurity { } <# .SYNOPSIS - Removes the hardening measures applied by Protect-WindowsSecurity cmdlet + Removes the hardening security measures from the system. Use the GUI for much better experience: Protect-WindowsSecurity -GUI .LINK https://github.com/HotCakeX/Harden-Windows-Security/wiki/Harden%E2%80%90Windows%E2%80%90Security%E2%80%90Module .DESCRIPTION - Removes the hardening measures applied by Protect-WindowsSecurity cmdlet + Removes the hardening security measures from the system. Use the GUI for much better experience: Protect-WindowsSecurity -GUI .PARAMETER OnlyProcessMitigations Only removes the Process Mitigations / Exploit Protection settings and doesn't change anything else .PARAMETER WDACPoliciesToRemove diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/ASRRules.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/ASRRules.xaml index 4332f8da5..0a6e67c33 100644 --- a/Harden-Windows-Security Module/Main files/Resources/XAML/ASRRules.xaml +++ b/Harden-Windows-Security Module/Main files/Resources/XAML/ASRRules.xaml @@ -26,9 +26,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -38,9 +38,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -50,9 +50,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -62,9 +62,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -74,9 +74,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -86,9 +86,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -98,9 +98,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -110,9 +110,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -122,9 +122,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -134,9 +134,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -148,9 +148,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -160,9 +160,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -172,9 +172,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -184,9 +184,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -196,9 +196,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -208,9 +208,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -220,9 +220,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -232,9 +232,9 @@ - + + Width="110" Margin="10,0,0,0"> @@ -244,9 +244,9 @@ - + + Width="110" Margin="10,0,0,0"> diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/Confirm.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/Confirm.xaml index d854debeb..1ff583317 100644 --- a/Harden-Windows-Security Module/Main files/Resources/XAML/Confirm.xaml +++ b/Harden-Windows-Security Module/Main files/Resources/XAML/Confirm.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" FontSize="16"> - + @@ -281,10 +281,10 @@ - - + + - - - - - + + + + -