diff --git a/Harden-Windows-Security Module/Harden Windows Security.csproj b/Harden-Windows-Security Module/Harden Windows Security.csproj
index 032be1e46..df1c2abaa 100644
--- a/Harden-Windows-Security Module/Harden Windows Security.csproj
+++ b/Harden-Windows-Security Module/Harden Windows Security.csproj
@@ -60,6 +60,7 @@
+
@@ -96,6 +97,9 @@
MSBuild:Compile
+
+ MSBuild:Compile
+ MSBuild:Compile
diff --git a/Harden-Windows-Security Module/Main files/.NETAssembliesToLoad.txt b/Harden-Windows-Security Module/Main files/.NETAssembliesToLoad.txt
index d6ba62e66..3692d71f4 100644
--- a/Harden-Windows-Security Module/Main files/.NETAssembliesToLoad.txt
+++ b/Harden-Windows-Security Module/Main files/.NETAssembliesToLoad.txt
@@ -34,6 +34,7 @@ System.Security.Principal
System.Diagnostics.Process
Microsoft.Win32.Primitives
System.Diagnostics.EventLog
+System.Security.Cryptography
System.Management.Automation
System.IO.Compression.zipfile
System.Security.AccessControl
@@ -45,6 +46,7 @@ System.Text.RegularExpressions
System.Text.Encoding.Extensions
System.ComponentModel.Primitives
System.Security.Principal.Windows
+System.Diagnostics.FileVersionInfo
System.ComponentModel.TypeConverter
Microsoft.Management.Infrastructure
System.DirectoryServices.AccountManagement
\ No newline at end of file
diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/Variables.cs b/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/Variables.cs
new file mode 100644
index 000000000..94ef94cbe
--- /dev/null
+++ b/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/Variables.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Text.RegularExpressions;
+using System.Windows.Controls;
+using Windows.Management.Deployment;
+
+namespace HardenWindowsSecurity;
+
+public static class GUIAppControlManager
+{
+ internal static UserControl? View;
+
+ internal static Grid? ParentGrid;
+
+ internal static PackageManager packageMgr = new();
+
+ // Pattern for AppControl Manager version and architecture extraction from file path and download link URL
+ internal static readonly Regex regex = new(@"_(?\d+\.\d+\.\d+\.\d+)_(?x64|arm64)\.msix$",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled
+ );
+
+ internal static readonly Uri AppUpdateDownloadLinkURL = new("https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/refs/heads/main/AppControl%20Manager/DownloadURL.txt");
+
+}
diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/View.cs b/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/View.cs
new file mode 100644
index 000000000..61096b251
--- /dev/null
+++ b/Harden-Windows-Security Module/Main files/C#/GUI/AppControlManager/View.cs
@@ -0,0 +1,393 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Management;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Markup;
+using System.Windows.Media.Imaging;
+using Windows.ApplicationModel;
+using Windows.Management.Deployment;
+
+#pragma warning disable IDE0063
+
+namespace HardenWindowsSecurity;
+
+public partial class GUIMain
+{
+
+ // Partial class definition for handling navigation and view models
+ public partial class NavigationVM : ViewModelBase
+ {
+
+ // Method to handle the AppControlManager view, including loading
+ private void AppControlManagerView(object obj)
+ {
+
+ // Check if the view is already cached
+ if (_viewCache.TryGetValue("AppControlManagerView", out var cachedView))
+ {
+ CurrentView = cachedView;
+ return;
+ }
+
+ // if Admin privileges are not available, return and do not proceed any further
+ // Will prevent the page from being loaded since the CurrentView won't be set/changed
+ if (!UserPrivCheck.IsAdmin())
+ {
+ Logger.LogMessage("AppControl Manager section can only be used when running the Harden Windows Security Application with Administrator privileges", LogTypeIntel.ErrorInteractionRequired);
+ return;
+ }
+
+ // Construct the file path for the AppControlManager view XAML
+ string xamlPath = Path.Combine(GlobalVars.path, "Resources", "XAML", "AppControlManager.xaml");
+
+ // Read the XAML content from the file
+ string xamlContent = File.ReadAllText(xamlPath);
+
+ // Parse the XAML content to create a UserControl
+ GUIAppControlManager.View = (UserControl)XamlReader.Parse(xamlContent);
+
+ // Find the Parent Grid
+ GUIAppControlManager.ParentGrid = (Grid)GUIAppControlManager.View.FindName("ParentGrid");
+
+ // Finding other elements
+ Button InstallAppControlManagerButton = GUIAppControlManager.ParentGrid.FindName("InstallAppControlManagerButton") as Button ?? throw new InvalidOperationException("InstallAppControlManagerButton could not be found in the AppControlManager view");
+ Button ViewDemoOnYouTubeButton = GUIAppControlManager.ParentGrid.FindName("ViewDemoOnYouTubeButton") as Button ?? throw new InvalidOperationException("ViewDemoOnYouTubeButton could not be found in the AppControlManager view");
+ Button AccessTheGuideOnGitHubButton = GUIAppControlManager.ParentGrid.FindName("AccessTheGuideOnGitHubButton") as Button ?? throw new InvalidOperationException("AccessTheGuideOnGitHubButton could not be found in the AppControlManager view");
+ Image ViewDemoOnYouTubeButtonIcon = GUIAppControlManager.ParentGrid.FindName("ViewDemoOnYouTubeButtonIcon") as Image ?? throw new InvalidOperationException("ViewDemoOnYouTubeButtonIcon could not be found in the AppControlManager view");
+ Image AccessTheGuideOnGitHubButtonIcon = GUIAppControlManager.ParentGrid.FindName("AccessTheGuideOnGitHubButtonIcon") as Image ?? throw new InvalidOperationException("AccessTheGuideOnGitHubButtonIcon could not be found in the AppControlManager view");
+ Image InstallAppControlManagerButtonIcon = GUIAppControlManager.ParentGrid.FindName("InstallAppControlManagerButtonIcon") as Image ?? throw new InvalidOperationException("InstallAppControlManagerButtonIcon could not be found in the AppControlManager view");
+ ProgressBar MainProgressBar = GUIAppControlManager.ParentGrid.FindName("MainProgressBar") as ProgressBar ?? throw new InvalidOperationException("MainProgressBar could not be found in the AppControlManager view");
+
+ #region Assigning icon images for the buttons
+
+ BitmapImage BitmapImage1 = new();
+ BitmapImage1.BeginInit();
+ BitmapImage1.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "YouTubeIcon.png"));
+ BitmapImage1.CacheOption = BitmapCacheOption.OnLoad;
+ BitmapImage1.EndInit();
+ ViewDemoOnYouTubeButtonIcon.Source = BitmapImage1;
+
+ BitmapImage BitmapImage2 = new();
+ BitmapImage2.BeginInit();
+ BitmapImage2.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "GitHubIcon.png"));
+ BitmapImage2.CacheOption = BitmapCacheOption.OnLoad;
+ BitmapImage2.EndInit();
+ AccessTheGuideOnGitHubButtonIcon.Source = BitmapImage2;
+
+ BitmapImage BitmapImage3 = new();
+ BitmapImage3.BeginInit();
+ BitmapImage3.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "InstallAppControlManagerIcon.png"));
+ BitmapImage3.CacheOption = BitmapCacheOption.OnLoad;
+ BitmapImage3.EndInit();
+ InstallAppControlManagerButtonIcon.Source = BitmapImage3;
+
+ #endregion
+
+ // Register the elements that will be enabled/disabled based on current activity
+ ActivityTracker.RegisterUIElement(InstallAppControlManagerButton);
+
+ // Event handler for the Install button
+ InstallAppControlManagerButton.Click += async (sender, e) =>
+ {
+ // Only continue if there is no activity other places
+ if (ActivityTracker.IsActive)
+ {
+ return;
+ }
+
+ // mark as activity started
+ ActivityTracker.IsActive = true;
+
+ try
+ {
+
+ #region Ensure the app is not already installed
+
+ bool alreadyInstalled = false;
+ Package? PossibleExistingPackage = null;
+
+ await Task.Run(() =>
+ {
+ IEnumerable PossibleExistingApp = GUIAppControlManager.packageMgr.FindPackages("AppControlManager", "CN=SelfSignedCertForAppControlManager");
+
+ PossibleExistingPackage = PossibleExistingApp.FirstOrDefault();
+
+ alreadyInstalled = PossibleExistingPackage is not null;
+ });
+
+ if (alreadyInstalled)
+ {
+ Logger.LogMessage($"AppControl Manager version {PossibleExistingPackage?.Id.Version.Major}.{PossibleExistingPackage?.Id.Version.Minor}.{PossibleExistingPackage?.Id.Version.Build}.{PossibleExistingPackage?.Id.Version.Revision} is already installed. If you want to update it, please start the AppControl Manager then navigate to the Update page to update it. If you want to reinstall it, please uninstall it first.", LogTypeIntel.InformationInteractionRequired);
+ return;
+ }
+
+ #endregion
+
+
+
+ using HttpClient client1 = new();
+
+ Logger.LogMessage("Getting the download link from GitHub", LogTypeIntel.Information);
+
+ // Store the download link to the latest available version
+ Uri onlineDownloadURL = new(await client1.GetStringAsync(GUIAppControlManager.AppUpdateDownloadLinkURL));
+
+ // The Uri will be used to detect the version and architecture of the MSIX package being installed
+ string sourceForRegex = onlineDownloadURL.ToString();
+
+ Logger.LogMessage("Downloading the AppControl Manager MSIX package...", LogTypeIntel.Information);
+
+ string AppControlManagerSavePath = Path.Combine(GlobalVars.WorkingDir, "AppControlManager.msix");
+
+ MainProgressBar.Visibility = Visibility.Visible;
+
+ using (HttpClient client = new())
+ {
+ // Send an Async get request to the url and specify to stop reading after headers are received for better efficiently
+ using (HttpResponseMessage response = await client.GetAsync(onlineDownloadURL, HttpCompletionOption.ResponseHeadersRead))
+ {
+ // Ensure that the response is successful (status code 2xx); otherwise, throw an exception
+ _ = response.EnsureSuccessStatusCode();
+
+ // Retrieve the total file size from the Content-Length header (if available)
+ long? totalBytes = response.Content.Headers.ContentLength;
+
+ // Open a stream to read the response content asynchronously
+ await using (Stream contentStream = await response.Content.ReadAsStreamAsync())
+ {
+ // Open a file stream to save the downloaded data locally
+ await using (FileStream fileStream = new(
+ AppControlManagerSavePath, // Path to save the file
+ FileMode.Create, // Create a new file or overwrite if it exists
+ FileAccess.Write, // Write-only access
+ FileShare.None, // Do not allow other processes to access the file
+ bufferSize: 8192, // Set buffer size to 8 KB
+ useAsync: true)) // Enable asynchronous operations for the file stream
+ {
+ // Define a buffer to hold data chunks as they are read
+ byte[] buffer = new byte[8192];
+ long totalReadBytes = 0; // Track the total number of bytes read
+ int readBytes; // Holds the count of bytes read in each iteration
+ double lastReportedProgress = 0; // Tracks the last reported download progress
+
+ // Loop to read from the content stream in chunks until no more data is available
+ while ((readBytes = await contentStream.ReadAsync(buffer)) > 0)
+ {
+ // Write the buffer to the file stream
+ await fileStream.WriteAsync(buffer.AsMemory(0, readBytes));
+ totalReadBytes += readBytes; // Update the total bytes read so far
+
+ // If the total file size is known, calculate and report progress
+ if (totalBytes.HasValue)
+ {
+ // Calculate the current download progress as a percentage
+ double progressPercentage = (double)totalReadBytes / totalBytes.Value * 100;
+
+ // Only update the ProgressBar if progress has increased by at least 1% to avoid constantly interacting with the UI thread
+ if (progressPercentage - lastReportedProgress >= 1)
+ {
+ // Update the last reported progress
+ lastReportedProgress = progressPercentage;
+
+ // Update the UI ProgressBar value on the dispatcher thread
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ MainProgressBar.Value = progressPercentage;
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Logger.LogMessage($"The AppControl Manager MSIX package has been successfully downloaded to {AppControlManagerSavePath}", LogTypeIntel.Information);
+
+ MainProgressBar.Visibility = Visibility.Collapsed;
+
+ Logger.LogMessage("Detecting/Downloading the SignTool.exe from the Microsoft servers", LogTypeIntel.Information);
+
+ string signToolPath = await Task.Run(() => SignToolHelper.GetSignToolPath());
+
+ Logger.LogMessage("All Downloads finished, installing the new AppControl Manager version", LogTypeIntel.Information);
+
+ await Task.Run(() =>
+ {
+
+ string randomPassword = Guid.NewGuid().ToString("N");
+
+ // Common name of the certificate
+ string commonName = "SelfSignedCertForAppControlManager";
+
+ // Path where the .cer file will be saved
+ string CertificateOutputPath = Path.Combine(GlobalVars.WorkingDir, $"{commonName}.cer");
+
+ // Remove any certificates with the specified common name that may already exist on the system form previous attempts
+ CertificateGenerator.DeleteCertificateByCN(commonName);
+
+ // Generate a new certificate
+ X509Certificate2 generatedCert = CertificateGenerator.GenerateSelfSignedCertificate(
+ subjectName: commonName,
+ validityInYears: 100,
+ keySize: 4096,
+ hashAlgorithm: HashAlgorithmName.SHA512,
+ storeLocation: CertificateGenerator.CertificateStoreLocation.Machine,
+ cerExportFilePath: CertificateOutputPath,
+ friendlyName: commonName,
+ UserProtectedPrivateKey: false,
+ ExportablePrivateKey: false);
+
+ // Get the version and architecture of the installing MSIX package app
+ Match RegexMatch = GUIAppControlManager.regex.Match(sourceForRegex);
+
+ string InstallingAppVersion;
+ string InstallingAppArchitecture;
+
+ if (RegexMatch.Success)
+ {
+ InstallingAppVersion = RegexMatch.Groups["Version"].Value;
+ InstallingAppArchitecture = RegexMatch.Groups["Architecture"].Value;
+ }
+ else
+ {
+ throw new InvalidOperationException("Could not get the version of the installing app");
+ }
+
+ // Signing the App Control Manager MSIX package
+ // In this step the SignTool detects the cert to use based on Common name + ThumbPrint + Hash Algo + Store Type + Store Name
+ ProcessStarter.RunCommand(signToolPath, $"sign /debug /n \"{commonName}\" /fd Sha512 /sm /s Root /sha1 {generatedCert.Thumbprint} \"{AppControlManagerSavePath}\"");
+
+ // Remove any certificates with the specified common name again
+ // Because the existing one contains private keys and don't want that
+ CertificateGenerator.DeleteCertificateByCN(commonName);
+
+ // Adding the certificate to the 'Local Machine/Trusted Root Certification Authorities' store with public key only. This safely stores the certificate on your device, ensuring its private key does not exist so cannot be used to sign anything else
+ CertificateGenerator.StoreCertificateInStore(generatedCert, CertificateGenerator.CertificateStoreLocation.Machine, true);
+
+ try
+ {
+
+ // Get the current Defender configurations
+ GlobalVars.MDAVPreferencesCurrent = MpPreferenceHelper.GetMpPreference();
+
+ // Get the current ASR Rules Exclusions lists
+ string[]? currentAttackSurfaceReductionExclusions = PropertyHelper.GetPropertyValue(GlobalVars.MDAVPreferencesCurrent, "AttackSurfaceReductionOnlyExclusions");
+
+ // If there are ASR rule exclusions, find ones that belong to AppControl Manager and remove them
+ // Before adding new ones for the new version
+ if (currentAttackSurfaceReductionExclusions is not null)
+ {
+
+ List asrRulesToRemove = [];
+
+ // Find all the rules that belong to the AppControl Manager
+ foreach (string item in currentAttackSurfaceReductionExclusions)
+ {
+ if (Regex.Match(item, "__sadt7br7jpt02").Success)
+ {
+ asrRulesToRemove.Add(item);
+ }
+ }
+
+ // If any of the rules belong to the AppControl Manager
+ if (asrRulesToRemove.Count > 0)
+ {
+ string[] stringArrayRepo = [.. asrRulesToRemove];
+
+ // Remove ASR rule exclusions that belong to the previous app version
+ using ManagementClass managementClass = new(@"root\Microsoft\Windows\Defender", "MSFT_MpPreference", null);
+ ManagementBaseObject inParams = managementClass.GetMethodParameters("Remove");
+ inParams["AttackSurfaceReductionOnlyExclusions"] = stringArrayRepo;
+ _ = managementClass.InvokeMethod("Remove", inParams, null);
+ }
+ }
+
+
+ #region Add new exclusions
+
+ StringBuilder InstallingAppLocationToAdd = new();
+ _ = InstallingAppLocationToAdd.Append("C:\\Program Files\\WindowsApps\\AppControlManager_");
+ _ = InstallingAppLocationToAdd.Append(InstallingAppVersion);
+ _ = InstallingAppLocationToAdd.Append('_');
+ _ = InstallingAppLocationToAdd.Append(InstallingAppArchitecture);
+ _ = InstallingAppLocationToAdd.Append("__sadt7br7jpt02\\");
+
+ string path1 = Path.Combine(InstallingAppLocationToAdd.ToString(), "AppControlManager.exe");
+ string path2 = Path.Combine(InstallingAppLocationToAdd.ToString(), "AppControlManager.dll");
+
+ ConfigDefenderHelper.ManageMpPreference("AttackSurfaceReductionOnlyExclusions", new string[] { path1, path2 }, false);
+
+ #endregion
+
+ }
+
+ catch (Exception ex)
+ {
+ Logger.LogMessage($"An error occurred while trying to add the ASR rule exclusions which you can ignore: {ex.Message}", LogTypeIntel.Information);
+ }
+
+
+ Logger.LogMessage($"Installing AppControl Manager MSIX package version '{InstallingAppVersion}' with architecture '{InstallingAppArchitecture}'", LogTypeIntel.Information);
+
+ // https://learn.microsoft.com/en-us/uwp/api/windows.management.deployment.addpackageoptions
+ AddPackageOptions options = new()
+ {
+ DeferRegistrationWhenPackagesAreInUse = false,
+ ForceUpdateFromAnyVersion = true
+ };
+
+ _ = GUIAppControlManager.packageMgr.AddPackageByUriAsync(new Uri(AppControlManagerSavePath), options);
+
+ Logger.LogMessage($"AppControl Manager Installing has been successful.", LogTypeIntel.InformationInteractionRequired);
+
+ });
+ }
+
+ finally
+ {
+ // mark as activity completed
+ ActivityTracker.IsActive = false;
+ }
+ };
+
+
+ ViewDemoOnYouTubeButton.Click += (sender, e) =>
+ {
+ _ = Process.Start(new ProcessStartInfo
+ {
+ FileName = "https://youtu.be/SzMs13n7elE?si=S70QiB5ZlYdhMk9r",
+ UseShellExecute = true // Ensure the link opens in the default browser
+ });
+ };
+
+
+ AccessTheGuideOnGitHubButton.Click += (sender, e) =>
+ {
+ _ = Process.Start(new ProcessStartInfo
+ {
+ FileName = "https://github.com/HotCakeX/Harden-Windows-Security/wiki/AppControl-Manager",
+ UseShellExecute = true // Ensure the link opens in the default browser
+ });
+ };
+
+
+ // Cache the view before setting it as the CurrentView
+ _viewCache["AppControlManagerView"] = GUIAppControlManager.View;
+
+ // Set the CurrentView to the Protect view
+ CurrentView = GUIAppControlManager.View;
+ }
+ }
+}
diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/FileReputation/View.cs b/Harden-Windows-Security Module/Main files/C#/GUI/FileReputation/View.cs
index 6c72774eb..c3181f75f 100644
--- a/Harden-Windows-Security Module/Main files/C#/GUI/FileReputation/View.cs
+++ b/Harden-Windows-Security Module/Main files/C#/GUI/FileReputation/View.cs
@@ -49,21 +49,10 @@ private void FileReputationView(object obj)
#endregion
- // Register the elements that will be enabled/disabled based on current activity
- ActivityTracker.RegisterUIElement(BrowseForFileButton);
-
-
// Event handler for Retrieve ASR Status Button
BrowseForFileButton.Click += async (sender, e) =>
{
- // Only continue if there is no activity other places
- if (ActivityTracker.IsActive)
- {
- return;
- }
-
- // mark as activity started
- ActivityTracker.IsActive = true;
+ BrowseForFileButton.IsEnabled = false;
FileReputationTextBlock.Text = null;
ReputationSourceTextBlock.Text = null;
@@ -122,8 +111,7 @@ await Task.Run(() =>
}
finally
{
- // mark as activity completed
- ActivityTracker.IsActive = false;
+ BrowseForFileButton.IsEnabled = true;
}
};
diff --git a/Harden-Windows-Security Module/Main files/C#/GUI/Main/GUI.cs b/Harden-Windows-Security Module/Main files/C#/GUI/Main/GUI.cs
index 7fb963a27..a59858480 100644
--- a/Harden-Windows-Security Module/Main files/C#/GUI/Main/GUI.cs
+++ b/Harden-Windows-Security Module/Main files/C#/GUI/Main/GUI.cs
@@ -145,6 +145,7 @@ public object CurrentView
public ICommand LogsCommand { get; set; }
public ICommand OptionalFeaturesCommand { get; set; }
public ICommand FileReputationCommand { get; set; }
+ public ICommand AppControlManagerCommand { get; set; }
// Dictionary to cache views by their identifiers
private readonly Dictionary _viewCache = [];
@@ -162,6 +163,7 @@ public NavigationVM()
LogsCommand = new RelayCommand(LogsView); // Command to handle the Logs action
OptionalFeaturesCommand = new RelayCommand(OptionalFeaturesView); // Command to handle the OptionalFeatures action
FileReputationCommand = new RelayCommand(FileReputationView); // Command to handle the FileReputation action
+ AppControlManagerCommand = new RelayCommand(AppControlManagerView); // Command to handle the AppControlManager action
// Load the Logs view initially to make it ready for logs to be written to it
LogsView(null);
@@ -520,6 +522,16 @@ Harden Windows Security operation log end
FileReputationButtonImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
FileReputationButtonImage.EndInit();
FileReputationButtonIcon.Source = FileReputationButtonImage;
+
+ // AppControlManager button icon
+ Grid AppControlManagerButtonGrid = SidebarGrid.FindName("AppControlManagerButtonGrid") as Grid;
+ Image AppControlManagerButtonIcon = AppControlManagerButtonGrid.FindName("AppControlManagerButtonIcon") as Image;
+ BitmapImage AppControlManagerButtonImage = new();
+ AppControlManagerButtonImage.BeginInit();
+ AppControlManagerButtonImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "AppControlManagerMenuButtonIcon.png"));
+ AppControlManagerButtonImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
+ AppControlManagerButtonImage.EndInit();
+ AppControlManagerButtonIcon.Source = AppControlManagerButtonImage;
#endregion
}
diff --git a/Harden-Windows-Security Module/Main files/C#/Others/CertificateGenerator.cs b/Harden-Windows-Security Module/Main files/C#/Others/CertificateGenerator.cs
new file mode 100644
index 000000000..8945580c8
--- /dev/null
+++ b/Harden-Windows-Security Module/Main files/C#/Others/CertificateGenerator.cs
@@ -0,0 +1,264 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace HardenWindowsSecurity;
+
+
+internal static class CertificateGenerator
+{
+
+ // Enum representing the applicable certificate stores
+ internal enum CertificateStoreLocation
+ {
+ User,
+ Machine
+ }
+
+ internal static X509Certificate2 GenerateSelfSignedCertificate(
+ string subjectName,
+ int validityInYears,
+ int keySize,
+ HashAlgorithmName hashAlgorithm,
+ CertificateStoreLocation? storeLocation,
+ bool UserProtectedPrivateKey,
+ bool ExportablePrivateKey,
+ string? cerExportFilePath = null,
+ string? friendlyName = null,
+ string? pfxExportFilePath = null,
+ string? pfxPassword = null)
+ {
+ X500DistinguishedName distinguishedName = new($"CN={subjectName}");
+
+ using RSA rsa = RSA.Create(keySize);
+
+ CertificateRequest request = new(distinguishedName, rsa, hashAlgorithm, RSASignaturePadding.Pkcs1);
+
+ // adds basic constraints to the certificate request to make it a non-CA and end entity certificate.
+ request.CertificateExtensions.Add(
+ new X509BasicConstraintsExtension(false, false, 0, true));
+
+ // Add key usage
+ request.CertificateExtensions.Add(
+ new X509KeyUsageExtension(
+ X509KeyUsageFlags.DigitalSignature,
+ true));
+
+
+
+ // Add subject key identifier
+ // Its raw data which is a byte array will always start with 4, 20
+ // 4: This indicates the ASN.1 type is an OCTET STRING.
+ // 20: The length of the OCTET STRING is 20 bytes.
+ // Remaining bytes are generated randomly for each certificate
+
+ // adds "[1]Application Certificate Policy:Policy Identifier=Code Signing" as the value for Application Policies extension. The certificate made in CA role in Windows Server (using Code Signing template) also adds this extension.
+ request.CertificateExtensions.Add(
+ new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
+
+
+
+ // Add enhanced key usage
+ // Code Signing
+ request.CertificateExtensions.Add(
+ new X509EnhancedKeyUsageExtension(
+ [new Oid("1.3.6.1.5.5.7.3.3")],
+ false)
+ );
+
+ // Add custom extension for "Application Policies"
+ // Application Policies OID
+ Oid appPoliciesOid = new("1.3.6.1.4.1.311.21.10");
+ // this must be set as specified and not randomly generated
+ byte[] appPoliciesValue = [48, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 3];
+ X509Extension appPoliciesExtension = new(appPoliciesOid, appPoliciesValue, false);
+ request.CertificateExtensions.Add(appPoliciesExtension);
+
+
+ DateTimeOffset notBefore = DateTimeOffset.UtcNow;
+ DateTimeOffset notAfter = notBefore.AddYears(validityInYears);
+
+ // Generate the certificate
+ using X509Certificate2 cert = request.CreateSelfSigned(notBefore, notAfter);
+
+
+ // Export the certificate for .PFX file as Byte Array
+ byte[] certData = cert.Export(X509ContentType.Pfx, pfxPassword);
+
+ // https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificateloader.loadpkcs12
+ // https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509keystorageflags
+
+ X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.PersistKeySet;
+
+ if (UserProtectedPrivateKey)
+ {
+ keyStorageFlags |= X509KeyStorageFlags.UserProtected;
+ }
+
+ if (ExportablePrivateKey)
+ {
+ keyStorageFlags |= X509KeyStorageFlags.Exportable;
+ }
+
+
+ // Requires .NET 9
+ // X509Certificate2 generatedCert = X509CertificateLoader.LoadPkcs12(certData, pfxPassword, keyStorageFlags);
+
+#pragma warning disable SYSLIB0057, CA2000
+ X509Certificate2 generatedCert = new(certData, pfxPassword, keyStorageFlags);
+#pragma warning restore
+
+
+ // Set the friendly name if provided
+ if (!string.IsNullOrEmpty(friendlyName))
+ {
+ generatedCert.FriendlyName = friendlyName;
+ }
+
+ // If path to export .cer file is provided, export the certificate (public key only)
+ if (!string.IsNullOrEmpty(cerExportFilePath))
+ {
+ // Export as DER-encoded X.509 .cer file
+ byte[] cerData = cert.Export(X509ContentType.Cert);
+ File.WriteAllBytes(cerExportFilePath, cerData);
+ }
+
+ // If path to export .pfx file is provided, export the certificate (public + private keys)
+ if (!string.IsNullOrEmpty(pfxExportFilePath))
+ {
+ File.WriteAllBytes(pfxExportFilePath, certData);
+ }
+
+ if (storeLocation is not null)
+ {
+ // Store the certificate in the specified certificate store
+ StoreCertificateInStore(generatedCert, storeLocation, false);
+ }
+
+ return generatedCert;
+ }
+
+
+
+ ///
+ /// Stores the certificate in one of the pre-defined certificate stores
+ ///
+ ///
+ ///
+ internal static void StoreCertificateInStore(X509Certificate2 cert, CertificateStoreLocation? storeLocation, bool publicKeyOnly)
+ {
+ // Choose the store based on the user selection
+ StoreName storeName = storeLocation == CertificateStoreLocation.User ? StoreName.My : StoreName.Root;
+ StoreLocation location = storeLocation == CertificateStoreLocation.User ? StoreLocation.CurrentUser : StoreLocation.LocalMachine;
+
+
+ if (publicKeyOnly)
+ {
+ // Export the certificate as a public key only (DER-encoded)
+ byte[] publicKeyData = cert.Export(X509ContentType.Cert);
+
+ // Reload the certificate from the exported public key data and replace the incoming data to eliminate the private key
+ // https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificateloader.loadcertificate
+
+ // requires .NET 9
+ // cert = X509CertificateLoader.LoadCertificate(publicKeyData);
+
+#pragma warning disable SYSLIB0057, CA2000
+ cert = new(publicKeyData);
+#pragma warning restore
+
+ }
+
+
+ using X509Store store = new(storeName, location);
+ store.Open(OpenFlags.ReadWrite);
+ store.Add(cert);
+ store.Close();
+ }
+
+
+
+ ///
+ /// Searches through all the relevant certificate stores for any certificate with a given Subject Common Name
+ /// And deletes all of the detected instances
+ ///
+ ///
+ internal static void DeleteCertificateByCN(string subjectName)
+ {
+ // Search through both user and machine certificate stores
+ DeleteCertificateFromAllStores(subjectName, StoreLocation.CurrentUser);
+ DeleteCertificateFromAllStores(subjectName, StoreLocation.LocalMachine);
+ }
+
+
+ ///
+ /// Deletes the certificate
+ ///
+ ///
+ ///
+ private static void DeleteCertificateFromAllStores(string subjectName, StoreLocation storeLocation)
+ {
+ // List of all known certificate store names
+ string[] allStoreNames =
+ [
+ "My", // Personal / Certificates
+ "Root", // Trusted Root Certification Authorities / Certificates
+ "CA", // Intermediate Certification Authorities / Certificates
+ "AuthRoot", // Third-Party Root Certification Authorities / Certificates
+ "TrustedPeople", // Trusted People
+ "TrustedPublisher" // Trusted Publishers
+ ];
+
+
+ // Iterate through all specified store names
+ foreach (string storeName in allStoreNames)
+ {
+
+ using X509Store store = new(storeName, storeLocation);
+ // MaxAllowed is necessary otherwise we'd get access denied error
+ store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived | OpenFlags.OpenExistingOnly);
+
+ // Loop through the certificates in the store and find the one with the matching CN
+ foreach (X509Certificate2 cert in store.Certificates)
+ {
+ if (cert.SubjectName.Name.Contains($"CN={subjectName}", StringComparison.OrdinalIgnoreCase))
+ {
+ // Certificate found with the matching CN, so delete it
+ store.Remove(cert);
+ Logger.LogMessage($"Deleted certificate with CN: {subjectName} from store: {storeName}", LogTypeIntel.Information);
+ }
+ }
+
+ store.Close();
+
+ }
+ }
+
+
+ internal static List GetCertificatesFromPersonalStore(string subjectName)
+ {
+ List matchingCertificates = [];
+
+ using X509Store store = new("My", StoreLocation.CurrentUser);
+ store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived | OpenFlags.OpenExistingOnly);
+
+ // Loop through certificates in the "My" store
+ foreach (X509Certificate2 cert in store.Certificates)
+ {
+ if (cert.SubjectName.Name.Contains($"CN={subjectName}", StringComparison.OrdinalIgnoreCase))
+ {
+ // Add certificate to the list if it matches the CN
+ matchingCertificates.Add(cert);
+ }
+ }
+
+ store.Close();
+
+ return matchingCertificates;
+ }
+
+
+
+}
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 00d06382e..9df9e40b1 100644
--- a/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs
+++ b/Harden-Windows-Security Module/Main files/C#/Others/ConfirmSystemComplianceMethods.cs
@@ -56,22 +56,22 @@ internal static void OrchestrateComplianceChecks(params string[] methodNames)
// Defining delegates for the methods
private static readonly Dictionary> methodDictionary = new(StringComparer.OrdinalIgnoreCase)
- {
- { "AttackSurfaceReductionRules", VerifyAttackSurfaceReductionRules },
- { "WindowsUpdateConfigurations", VerifyWindowsUpdateConfigurations },
- { "NonAdminCommands", VerifyNonAdminCommands },
- { "EdgeBrowserConfigurations", VerifyEdgeBrowserConfigurations },
- { "DeviceGuard", VerifyDeviceGuard },
- { "BitLockerSettings", VerifyBitLockerSettings },
- { "MiscellaneousConfigurations", VerifyMiscellaneousConfigurations },
- { "WindowsNetworking", VerifyWindowsNetworking },
- { "LockScreen", VerifyLockScreen },
- { "UserAccountControl", VerifyUserAccountControl },
- { "OptionalWindowsFeatures", VerifyOptionalWindowsFeatures },
- { "TLSSecurity", VerifyTLSSecurity },
- { "WindowsFirewall", VerifyWindowsFirewall },
- { "MicrosoftDefender", VerifyMicrosoftDefender }
- };
+ {
+ { "AttackSurfaceReductionRules", VerifyAttackSurfaceReductionRules },
+ { "WindowsUpdateConfigurations", VerifyWindowsUpdateConfigurations },
+ { "NonAdminCommands", VerifyNonAdminCommands },
+ { "EdgeBrowserConfigurations", VerifyEdgeBrowserConfigurations },
+ { "DeviceGuard", VerifyDeviceGuard },
+ { "BitLockerSettings", VerifyBitLockerSettings },
+ { "MiscellaneousConfigurations", VerifyMiscellaneousConfigurations },
+ { "WindowsNetworking", VerifyWindowsNetworking },
+ { "LockScreen", VerifyLockScreen },
+ { "UserAccountControl", VerifyUserAccountControl },
+ { "OptionalWindowsFeatures", VerifyOptionalWindowsFeatures },
+ { "TLSSecurity", VerifyTLSSecurity },
+ { "WindowsFirewall", VerifyWindowsFirewall },
+ { "MicrosoftDefender", VerifyMicrosoftDefender }
+ };
// Task status codes: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskstatus
diff --git a/Harden-Windows-Security Module/Main files/C#/Others/SignToolHelper.cs b/Harden-Windows-Security Module/Main files/C#/Others/SignToolHelper.cs
new file mode 100644
index 000000000..54931aef1
--- /dev/null
+++ b/Harden-Windows-Security Module/Main files/C#/Others/SignToolHelper.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net.Http;
+using System.Runtime.InteropServices;
+using System.Text.Json;
+
+namespace HardenWindowsSecurity;
+
+internal static class SignToolHelper
+{
+ ///
+ /// Invokes SignTool.exe to sign a Code Integrity Policy file.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void Sign(FileInfo ciPath, FileInfo signToolPathFinal, string certCN)
+ {
+ // Build the arguments for the process
+ string arguments = $"sign /v /n \"{certCN}\" /p7 . /p7co 1.3.6.1.4.1.311.79.1 /fd certHash \"{ciPath.Name}\"";
+
+ Logger.LogMessage($"Signing {ciPath.FullName}", LogTypeIntel.Information);
+
+ // Set up the process start info
+ ProcessStartInfo startInfo = new()
+ {
+ FileName = signToolPathFinal.FullName,
+ Arguments = arguments,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true,
+ WorkingDirectory = ciPath.DirectoryName // Set the working directory so that SignTool.exe will know where the .cip file is and where to save the output
+ };
+
+ // Start the process
+ using Process process = new() { StartInfo = startInfo };
+ _ = process.Start();
+
+ // Wait for the process to exit
+ process.WaitForExit();
+
+ // Read the output and error streams
+ string output = process.StandardOutput.ReadToEnd();
+ string error = process.StandardError.ReadToEnd();
+
+ // Log the output and error
+ Logger.LogMessage(output, LogTypeIntel.Information);
+
+ // Check if there is any error and throw an exception if there is
+ if (!string.IsNullOrEmpty(error))
+ {
+ throw new InvalidOperationException($"SignTool failed with exit code {process.ExitCode}. Error: {error}");
+ }
+
+ // Check the exit code
+ if (process.ExitCode != 0)
+ {
+ throw new InvalidOperationException($"SignTool failed with exit code {process.ExitCode}. Error: {error}");
+ }
+ }
+
+
+ ///
+ /// Downloads the latest version of the microsoft.windows.sdk.buildtools NuGet package.
+ /// Extracts the SignTool.exe from it and returns the path to it.
+ /// Copies it to the User Configurations directory.
+ ///
+ private static string Download()
+ {
+ using HttpClient client = new();
+
+ string packageName = "microsoft.windows.sdk.buildtools"; // Important that this stays all lower case
+
+ Logger.LogMessage("Finding the latest version of the microsoft.windows.sdk.buildtools package from NuGet", LogTypeIntel.Information);
+
+ // Get the list of versions
+ Uri versionsUrl = new($"https://api.nuget.org/v3-flatcontainer/{packageName}/index.json");
+ string versionsResponse = client.GetStringAsync(versionsUrl).GetAwaiter().GetResult();
+
+ // Parse the JSON to get the latest version
+ JsonDocument versionsJson = JsonDocument.Parse(versionsResponse);
+ JsonElement versions = versionsJson.RootElement.GetProperty("versions");
+ string? latestVersion = versions[versions.GetArrayLength() - 1].GetString() ?? throw new InvalidOperationException("Failed to get the latest version of the package.");
+
+ // Construct the download link for the latest version's .nupkg
+ Uri downloadUrl = new($"https://api.nuget.org/v3-flatcontainer/{packageName}/{latestVersion}/{packageName}.{latestVersion}.nupkg");
+
+ Logger.LogMessage($"Downloading the latest .nupkg package file version '{latestVersion}' from the following URL: {downloadUrl}", LogTypeIntel.Information);
+
+ // Download the .nupkg file
+ string filePath = Path.Combine(GlobalVars.WorkingDir, $"{packageName}.{latestVersion}.nupkg");
+ using (Stream downloadStream = client.GetStreamAsync(downloadUrl).GetAwaiter().GetResult())
+ using (FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ downloadStream.CopyTo(fileStream);
+ }
+
+ Logger.LogMessage($"Downloaded package to {filePath}", LogTypeIntel.Information);
+
+ // Extract the .nupkg file
+ string extractPath = Path.Combine(GlobalVars.WorkingDir, "extracted");
+ ZipFile.ExtractToDirectory(filePath, extractPath);
+
+ Logger.LogMessage($"Extracted package to {extractPath}", LogTypeIntel.Information);
+
+ string binDirectoryPath = Path.Combine(extractPath, "bin");
+
+ // Get the directory that has the version, since it varies we need to get it implicitly
+ string[] versionDirectories = Directory.GetDirectories(binDirectoryPath);
+
+ if (versionDirectories.Length == 0)
+ {
+ throw new DirectoryNotFoundException("No version directories found in 'bin'.");
+ }
+
+ // There should be only one
+ string versionDirectory = versionDirectories.First();
+ string signtoolPath = Path.Combine(versionDirectory, "x64", "signtool.exe");
+
+ if (!File.Exists(signtoolPath))
+ {
+ throw new FileNotFoundException("signtool.exe not found in the expected path.");
+ }
+
+ string finalSignToolPath = Path.Combine(GlobalVars.WorkingDir, "SignTool.exe");
+
+ File.Copy(signtoolPath, finalSignToolPath, true);
+
+ Logger.LogMessage($"Path to signtool.exe: {finalSignToolPath}", LogTypeIntel.Information);
+
+ return finalSignToolPath;
+
+ }
+
+
+ ///
+ /// Verifies if the SignTool.exe is of a version greater than one specified in the method
+ ///
+ ///
+ ///
+ private static bool Verify(string filePath)
+ {
+ try
+ {
+ FileVersionInfo fileInfo = FileVersionInfo.GetVersionInfo(filePath);
+ return (new Version(fileInfo.ProductVersion!) > new Version("10.0.22621.2428"));
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+
+
+ ///
+ /// Returns the architecture of the current OS
+ ///
+ ///
+ ///
+ private static string GetArchitecture()
+ {
+ if (RuntimeInformation.OSArchitecture is Architecture.X64)
+ {
+ return "x64";
+ }
+ else if (RuntimeInformation.OSArchitecture is Architecture.Arm64)
+ {
+ return "arm64";
+ }
+ else
+ {
+ throw new InvalidOperationException("Only X64 and ARM64 platforms are supported.");
+ }
+ }
+
+
+
+ ///
+ /// Gets the path to SignTool.exe and verifies it to make sure it's valid
+ /// If the SignTool.exe path is not provided by parameter, it will try to detect it automatically by checking if Windows SDK is installed
+ /// If the SignTool.exe path is not provided by parameter and it could not be detected automatically, it will try to download it from NuGet
+ ///
+ ///
+ ///
+ internal static string GetSignToolPath(string? filePath = null)
+ {
+ // The path to return at the end
+ string? signToolPath = null;
+
+ // If Sign tool path wasn't provided by parameter or it doesn't exist on the file system, try to detect it automatically
+ if (string.IsNullOrWhiteSpace(filePath) && !Path.Exists(filePath))
+ {
+
+ try
+ {
+
+ Logger.LogMessage("SignTool.exe path was not provided by parameter, trying to detect it automatically", LogTypeIntel.Information);
+
+ string baseDir = @"C:\Program Files (x86)\Windows Kits\10\bin";
+ string targetArchitecture = GetArchitecture();
+
+ // Get the directory with the highest version in its name
+ string? latestSigntoolPath = Directory.GetDirectories(baseDir)
+ .Select(dir => new DirectoryInfo(dir))
+ .Where(dir => Version.TryParse(Path.GetFileName(dir.Name), out _)) // Ensure directory is a version
+ .OrderByDescending(dir => new Version(dir.Name)) // Order by version
+ .First().ToString(); // Get the highest version
+
+ if (latestSigntoolPath is not null)
+ {
+ // Construct the full SignTool.exe path
+ string constructedFinalPath = Path.Combine(latestSigntoolPath, targetArchitecture, "signtool.exe");
+
+ // If it checks out, assign it to the output variable
+ if (Verify(constructedFinalPath))
+ {
+ signToolPath = constructedFinalPath;
+ Logger.LogMessage($"Successfully detected the SignTool.exe on the system: {constructedFinalPath}", LogTypeIntel.Information);
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Logger.LogMessage($"Failed to detect SignTool.exe path automatically: {ex.Message}", LogTypeIntel.Error);
+ }
+
+ }
+
+ // If Sign tool path was provided by parameter, use it
+ else
+ {
+ Logger.LogMessage("SignTool.exe path was provided by parameter", LogTypeIntel.Information);
+
+ if (Verify(filePath))
+ {
+ Logger.LogMessage("The provided SignTool.exe is valid", LogTypeIntel.Information);
+ signToolPath = filePath;
+ }
+ else
+ {
+ Logger.LogMessage("The provided SignTool.exe is not valid", LogTypeIntel.Information);
+ }
+ }
+
+ // Download the SignTool.exe if it's still null
+ signToolPath ??= Download();
+
+ Logger.LogMessage($"Setting the SignTool path in the common user configurations to: {signToolPath}", LogTypeIntel.Information);
+
+ return signToolPath;
+ }
+}
diff --git a/Harden-Windows-Security Module/Main files/Resources/Media/AppControlManagerMenuButtonIcon.png b/Harden-Windows-Security Module/Main files/Resources/Media/AppControlManagerMenuButtonIcon.png
new file mode 100644
index 000000000..35d6d38e5
Binary files /dev/null and b/Harden-Windows-Security Module/Main files/Resources/Media/AppControlManagerMenuButtonIcon.png differ
diff --git a/Harden-Windows-Security Module/Main files/Resources/Media/GitHubIcon.png b/Harden-Windows-Security Module/Main files/Resources/Media/GitHubIcon.png
new file mode 100644
index 000000000..abe7e2136
Binary files /dev/null and b/Harden-Windows-Security Module/Main files/Resources/Media/GitHubIcon.png differ
diff --git a/Harden-Windows-Security Module/Main files/Resources/Media/InstallAppControlManagerIcon.png b/Harden-Windows-Security Module/Main files/Resources/Media/InstallAppControlManagerIcon.png
new file mode 100644
index 000000000..5aaa2b2cc
Binary files /dev/null and b/Harden-Windows-Security Module/Main files/Resources/Media/InstallAppControlManagerIcon.png differ
diff --git a/Harden-Windows-Security Module/Main files/Resources/Media/YouTubeIcon.png b/Harden-Windows-Security Module/Main files/Resources/Media/YouTubeIcon.png
new file mode 100644
index 000000000..0d4a8af67
Binary files /dev/null and b/Harden-Windows-Security Module/Main files/Resources/Media/YouTubeIcon.png differ
diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/AppControlManager.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/AppControlManager.xaml
new file mode 100644
index 000000000..5ec5de92c
--- /dev/null
+++ b/Harden-Windows-Security Module/Main files/Resources/XAML/AppControlManager.xaml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/Main.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/Main.xaml
index b076fe670..85277d4ba 100644
--- a/Harden-Windows-Security Module/Main files/Resources/XAML/Main.xaml
+++ b/Harden-Windows-Security Module/Main files/Resources/XAML/Main.xaml
@@ -98,6 +98,13 @@
+
+
+
+
+
+
+
diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/Protect.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/Protect.xaml
index 3ed53043b..f87b6b3a9 100644
--- a/Harden-Windows-Security Module/Main files/Resources/XAML/Protect.xaml
+++ b/Harden-Windows-Security Module/Main files/Resources/XAML/Protect.xaml
@@ -209,7 +209,7 @@
-
+
diff --git a/Harden-Windows-Security Module/Main files/Resources/XAML/ResourceDictionaries/Text.xaml b/Harden-Windows-Security Module/Main files/Resources/XAML/ResourceDictionaries/Text.xaml
index 27d3fbcf9..0f6f79da7 100644
--- a/Harden-Windows-Security Module/Main files/Resources/XAML/ResourceDictionaries/Text.xaml
+++ b/Harden-Windows-Security Module/Main files/Resources/XAML/ResourceDictionaries/Text.xaml
@@ -14,6 +14,6 @@
+ Value="75,0,0,0" />
\ No newline at end of file