Skip to content

Commit

Permalink
Implemented Apps and Windows Features Removal (#506)
Browse files Browse the repository at this point in the history
New Feature: Introduced the ability to remove built-in apps using the Harden Windows Security module. This functionality is available on a dedicated page. The list of removable apps is stored in a JSON file, providing flexibility and extensibility.

New Feature: Added a new page for managing Optional Windows Features. While the Harden Windows Security module already includes an Optional Features category in the hardening measures section, this new page allows for granular control, enabling you to fine-tune which features to enable or disable. It also includes additional optional features that can be removed.

Compliance Checking Enhancement: Added support for VBScript compliance checks.

Code Improvements: Implemented several code enhancements and optimizations.

UI Enhancements: Updated the button styles on the ASR Rules and Unprotect pages. The new design replaces the previous animated buttons with play icons, offering a cleaner and more modern look.

This PR also completes the first half of this feature request: [Suggestion]: Offer Removal of telemetry and pre-installed apps from the OS in Harden Windows Security Module #479
  • Loading branch information
HotCakeX authored Jan 5, 2025
1 parent 3ac0f72 commit 26cc92c
Show file tree
Hide file tree
Showing 76 changed files with 1,413 additions and 1,068 deletions.
3 changes: 3 additions & 0 deletions Harden-Windows-Security Module/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1039,3 +1039,6 @@ dotnet_diagnostic.CA1002.severity = error

# SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
dotnet_diagnostic.SYSLIB1054.severity = silent

# CA1060: Move P/Invokes to NativeMethods class
dotnet_diagnostic.CA1060.severity = error
10 changes: 10 additions & 0 deletions Harden-Windows-Security Module/Harden Windows Security.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
</Content>
</ItemGroup>

<ItemGroup>
<Page Remove="Main files\Resources\XAML\OptionalFeatures.xaml" />
</ItemGroup>

<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
Expand Down Expand Up @@ -87,6 +91,12 @@
</Compile>
</ItemGroup>

<ItemGroup>
<Content Update="Main files\Resources\XAML\OptionalFeatures.xaml">
<Generator>MSBuild:Compile</Generator>
</Content>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ internal static Dictionary<string, List<Dictionary<string, object>>> Get()
private static async Task<Dictionary<string, List<Dictionary<string, object>>>> GetAsync()
{
// Set the location of the CSV file containing the MDM list
string path = GlobalVars.path ?? throw new InvalidOperationException("GlobalVars.path is null");
string csvFilePath = Path.Combine(path, "Resources", "MDMResultClasses.csv");
string csvFilePath = Path.Combine(GlobalVars.path, "Resources", "MDMResultClasses.csv");

// Create a dictionary where keys are the class names and values are lists of dictionaries
Dictionary<string, List<Dictionary<string, object>>> results = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal static void Invoke()
Logger.LogMessage("Collecting Intune applied policy details from the System", LogTypeIntel.Information);

// Path to the PowerShell script
string scriptPath = Path.Combine(GlobalVars.path!, "Shared", "SYSTEMInfoGathering.ps1");
string scriptPath = Path.Combine(GlobalVars.path, "Shared", "SYSTEMInfoGathering.ps1");

// Load the PowerShell script into a string
string script = File.ReadAllText(scriptPath);
Expand Down
63 changes: 9 additions & 54 deletions Harden-Windows-Security Module/Main files/C#/GUI/ASRRules/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media.Imaging;

namespace HardenWindowsSecurity;

Expand All @@ -27,11 +26,6 @@ private void ASRRulesView(object obj)
return;
}

if (GlobalVars.path is null)
{
throw new InvalidOperationException("GlobalVars.path cannot be null.");
}

// 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())
Expand All @@ -54,35 +48,8 @@ private void ASRRulesView(object obj)

#region finding elements

// Finding the Execute Button Grid
Grid? ExecuteButtonGrid = GUIASRRules.ParentGrid.FindName("ExecuteButtonGrid") as Grid ?? throw new InvalidOperationException("ExecuteButtonGrid is null in the ASRRules View");

// Finding the Execute Button
if (ExecuteButtonGrid.FindName("ExecuteButton") is not ToggleButton ExecuteButton)
{
throw new InvalidOperationException("Couldn't find the ExecuteButton in ASRRules view");
}

// Apply the template to make sure it's available
_ = ExecuteButton.ApplyTemplate();

// Access the image within the Execute Button's template
if (ExecuteButton.Template.FindName("RefreshIconImage", ExecuteButton) is not Image RefreshIconImage)
{
throw new InvalidOperationException("RefreshIconImage could not be found in the ASRRules view");
}

// Update the image source for the Refresh button
// Load the Refresh icon image into memory and set it as the source
BitmapImage RefreshIconBitmapImage = new();
RefreshIconBitmapImage.BeginInit();
RefreshIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "ExecuteButton.png"));
RefreshIconBitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
RefreshIconBitmapImage.EndInit();

RefreshIconImage.Source = RefreshIconBitmapImage;

Button RetrieveASRStatusButton = GUIASRRules.ParentGrid.FindName("RetrieveASRStatus") as Button ?? throw new InvalidOperationException("RetrieveASRStatus could not be found in the ASRRules view");
Button ApplyASRRulesButton = GUIASRRules.ParentGrid.FindName("ApplyASRRulesButton") as Button ?? throw new InvalidOperationException("ApplyASRRulesButton could not be found in the ASRRules view");

#endregion

Expand Down Expand Up @@ -121,11 +88,6 @@ void ProcessListViewItems(ListView listView)
string GetASRRuleConfig(string ASRRuleName, byte ComboBoxIndex)
{

if (GlobalVars.path is null)
{
throw new InvalidOperationException("GlobalVars.path is null.");
}

if (AttackSurfaceReductionIntel.ASRRulesCorrelation is null)
{
throw new InvalidOperationException("ASRRulesCorrelation is null");
Expand Down Expand Up @@ -169,12 +131,12 @@ string GetASRRuleConfig(string ASRRuleName, byte ComboBoxIndex)
}


// Register the ExecuteButton as an element that will be enabled/disabled based on current activity
ActivityTracker.RegisterUIElement(ExecuteButton);
// Register the ApplyASRRulesButton as an element that will be enabled/disabled based on current activity
ActivityTracker.RegisterUIElement(ApplyASRRulesButton);


// Set up the Click event handler for the ExecuteButton button
ExecuteButton.Click += async (sender, e) =>
// Set up the Click event handler for the ApplyASRRulesButton button
ApplyASRRulesButton.Click += async (sender, e) =>
{
// Only continue if there is no activity other places
if (!ActivityTracker.IsActive)
Expand All @@ -183,7 +145,7 @@ string GetASRRuleConfig(string ASRRuleName, byte ComboBoxIndex)
ActivityTracker.IsActive = true;

// Set text blocks to empty while new data is being generated
System.Windows.Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(() =>
{
// Get the ListViews
if (GUIASRRules.ParentGrid.FindName("ASRRuleSet1") is not ListView ASRRuleSet1 || GUIASRRules.ParentGrid.FindName("ASRRuleSet2") is not ListView ASRRuleSet2)
Expand Down Expand Up @@ -259,12 +221,6 @@ await System.Threading.Tasks.Task.Run(() =>

});

// Update the UI Elements at the end of the run
await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
{
ExecuteButton.IsChecked = false; // Uncheck the ExecuteButton button to start the reverse animation
});

// mark as activity completed
ActivityTracker.IsActive = false;

Expand All @@ -286,7 +242,7 @@ await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
// Dictionary of ComboBoxes, key is ComboBox name and value is ComboBox element itself
Dictionary<string, ComboBox> ComboBoxList = [];

System.Windows.Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(() =>
{
// Get the ListViews
if (GUIASRRules.ParentGrid.FindName("ASRRuleSet1") is not ListView ASRRuleSet1 ||
Expand Down Expand Up @@ -369,7 +325,7 @@ await System.Threading.Tasks.Task.Run(() =>
_ = ComboBoxList.TryGetValue(ComboBoxName!, out ComboBox? currentMatchingComboBox);

// Use the GUI dispatcher to set the ComboBox selected index to the currently applied ASR rule's action
System.Windows.Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(() =>
{
// Make the connection between ASR rule applied action and the ComboBox Item Indexes
int selectedIndex = Convert.ToInt32(action, CultureInfo.InvariantCulture) switch
Expand All @@ -395,7 +351,6 @@ await System.Threading.Tasks.Task.Run(() =>
};



// Cache the view before setting it as the CurrentView
_viewCache["ASRRulesView"] = GUIASRRules.View;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ private void BitLockerView(object obj)
return;
}

if (GlobalVars.path is null)
{
throw new InvalidOperationException("GlobalVars.path cannot be null.");
}

// 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())
Expand Down Expand Up @@ -208,7 +203,7 @@ await Task.Run(() =>
// Add image to the BackupButtonIcon
BitmapImage BackupButtonIconBitmapImage = new();
BackupButtonIconBitmapImage.BeginInit();
BackupButtonIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "ExportIconBlack.png"));
BackupButtonIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "ExportIconBlack.png"));
BackupButtonIconBitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
BackupButtonIconBitmapImage.EndInit();
BackupButtonIcon.Source = BackupButtonIconBitmapImage;
Expand Down Expand Up @@ -265,7 +260,7 @@ await Task.Run(() =>
// Add the same Refresh image to multiple sources
BitmapImage RefreshButtonIcon1BitmapImage = new();
RefreshButtonIcon1BitmapImage.BeginInit();
RefreshButtonIcon1BitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "RefreshButtonIcon.png"));
RefreshButtonIcon1BitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "RefreshButtonIcon.png"));
RefreshButtonIcon1BitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
RefreshButtonIcon1BitmapImage.EndInit();
RefreshButtonIcon1.Source = RefreshButtonIcon1BitmapImage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected void OnPropertyChanged(string propertyName)
private static BitmapImage LoadImage(string fileName)
{
// Construct the full path to the image file
string imagePath = Path.Combine(GlobalVars.path!, "Resources", "Media", fileName);
string imagePath = Path.Combine(GlobalVars.path, "Resources", "Media", fileName);
// Return the loaded image as a BitmapImage
return new BitmapImage(new Uri(imagePath, UriKind.Absolute));
}
Expand Down
44 changes: 25 additions & 19 deletions Harden-Windows-Security Module/Main files/C#/GUI/Confirm/View.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;

#nullable disable
Expand All @@ -25,7 +31,7 @@ public partial class NavigationVM : ViewModelBase
private ICollectionView _SecOpsCollectionView;

// Collection of SecOp objects
private System.Collections.ObjectModel.ObservableCollection<SecOp> __SecOpses;
private ObservableCollection<SecOp> __SecOpses;

// Method to handle the "Confirm" view, including loading and modifying it
private void ConfirmView(object obj)
Expand Down Expand Up @@ -61,7 +67,7 @@ private void ConfirmView(object obj)
string xamlContent = File.ReadAllText(xamlPath);

// Parse the XAML content to create a UserControl object
GUIConfirmSystemCompliance.View = (UserControl)System.Windows.Markup.XamlReader.Parse(xamlContent);
GUIConfirmSystemCompliance.View = (UserControl)XamlReader.Parse(xamlContent);

// Set the DataContext for the Confirm view
GUIConfirmSystemCompliance.View.DataContext = new ConfirmVM();
Expand Down Expand Up @@ -131,7 +137,7 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl
__SecOpses = [];

// Create a collection view based on the security options collection
_SecOpsCollectionView = System.Windows.Data.CollectionViewSource.GetDefaultView(__SecOpses);
_SecOpsCollectionView = CollectionViewSource.GetDefaultView(__SecOpses);

// Set the ItemSource of the DataGrid in the Confirm view to the collection view
if (GUIConfirmSystemCompliance.SecOpsDataGrid is not null)
Expand Down Expand Up @@ -173,7 +179,7 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl
// Load the Refresh icon image into memory and set it as the source
BitmapImage RefreshIconBitmapImage = new();
RefreshIconBitmapImage.BeginInit();
RefreshIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "ExecuteButton.png"));
RefreshIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "ExecuteButton.png"));
RefreshIconBitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
RefreshIconBitmapImage.EndInit();

Expand Down Expand Up @@ -221,7 +227,7 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl

// Disable the Refresh button while processing
// Set text blocks to empty while new data is being generated
System.Windows.Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(() =>
{
// Finding the elements
TextBlock CompliantItemsTextBlock = (TextBlock)GUIConfirmSystemCompliance.View.FindName("CompliantItemsTextBlock");
Expand All @@ -243,7 +249,7 @@ void ApplyFilters(string filterText, bool includeCompliant, bool includeNonCompl
});

// Run the method asynchronously in a different thread
await System.Threading.Tasks.Task.Run(() =>
await Task.Run(() =>
{
// Get fresh data for compliance checking
Initializer.Initialize(null, true);
Expand Down Expand Up @@ -280,7 +286,7 @@ await System.Threading.Tasks.Task.Run(() =>
});

// After InvokeConfirmation is completed, update the security options collection
await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
await Application.Current.Dispatcher.InvokeAsync(() =>
{
LoadMembers(); // Load updated security options
RefreshButton.IsChecked = false; // Uncheck the Refresh button
Expand All @@ -307,33 +313,33 @@ await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
/// </summary>
/// <param name="category">Name of the category</param>
/// <returns>The color of the category to be used for display purposes on the DataGrid GUI</returns>
private static System.Windows.Media.Brush GetCategoryColor(string category)
private static Brush GetCategoryColor(string category)
{
// Determine the background color for each category
return category switch
{
// Light Pastel Sky Blue
"MicrosoftDefender" => new System.Windows.Media.BrushConverter().ConvertFromString("#B3E5FC") as System.Windows.Media.Brush,
"MicrosoftDefender" => new BrushConverter().ConvertFromString("#B3E5FC") as Brush,
// Light Pastel Coral
"AttackSurfaceReductionRules" => new System.Windows.Media.BrushConverter().ConvertFromString("#FFDAB9") as System.Windows.Media.Brush,
"AttackSurfaceReductionRules" => new BrushConverter().ConvertFromString("#FFDAB9") as Brush,
// Light Pastel Green
"BitLockerSettings" => new System.Windows.Media.BrushConverter().ConvertFromString("#C3FDB8") as System.Windows.Media.Brush,
"BitLockerSettings" => new BrushConverter().ConvertFromString("#C3FDB8") as Brush,
// Light Pastel Lemon
"TLSSecurity" => new System.Windows.Media.BrushConverter().ConvertFromString("#FFFACD") as System.Windows.Media.Brush,
"TLSSecurity" => new BrushConverter().ConvertFromString("#FFFACD") as Brush,
// Light Pastel Lavender
"LockScreen" => new System.Windows.Media.BrushConverter().ConvertFromString("#E6E6FA") as System.Windows.Media.Brush,
"LockScreen" => new BrushConverter().ConvertFromString("#E6E6FA") as Brush,
// Light Pastel Aqua
"UserAccountControl" => new System.Windows.Media.BrushConverter().ConvertFromString("#C1F0F6") as System.Windows.Media.Brush,
"UserAccountControl" => new BrushConverter().ConvertFromString("#C1F0F6") as Brush,
// Light Pastel Teal
"DeviceGuard" => new System.Windows.Media.BrushConverter().ConvertFromString("#B2DFDB") as System.Windows.Media.Brush,
"DeviceGuard" => new BrushConverter().ConvertFromString("#B2DFDB") as Brush,
// Light Pastel Pink
"WindowsFirewall" => new System.Windows.Media.BrushConverter().ConvertFromString("#F8BBD0") as System.Windows.Media.Brush,
"WindowsFirewall" => new BrushConverter().ConvertFromString("#F8BBD0") as Brush,
// Light Pastel Peach
"OptionalWindowsFeatures" => new System.Windows.Media.BrushConverter().ConvertFromString("#FFE4E1") as System.Windows.Media.Brush,
"OptionalWindowsFeatures" => new BrushConverter().ConvertFromString("#FFE4E1") as Brush,
// Light Pastel Mint
"WindowsNetworking" => new System.Windows.Media.BrushConverter().ConvertFromString("#F5FFFA") as System.Windows.Media.Brush,
"WindowsNetworking" => new BrushConverter().ConvertFromString("#F5FFFA") as Brush,
// Light Pastel Gray
_ => new System.Windows.Media.BrushConverter().ConvertFromString("#EDEDED") as System.Windows.Media.Brush,
_ => new BrushConverter().ConvertFromString("#EDEDED") as Brush,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ private void ExclusionsView(object obj)
return;
}

if (GlobalVars.path is null)
{
throw new InvalidOperationException("GlobalVars.path cannot be null.");
}

// 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())
Expand Down
9 changes: 2 additions & 7 deletions Harden-Windows-Security Module/Main files/C#/GUI/Log/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ private void LogsView(object obj)
return;
}

if (GlobalVars.path is null)
{
throw new InvalidOperationException("GlobalVars.path cannot be null.");
}

// Construct the file path for the Logs view XAML
string xamlPath = Path.Combine(GlobalVars.path, "Resources", "XAML", "Logs.xaml");

Expand All @@ -56,7 +51,7 @@ private void LogsView(object obj)
// Add image to the ExportLogsIcon
BitmapImage ExportLogsIconBitmapImage = new();
ExportLogsIconBitmapImage.BeginInit();
ExportLogsIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "ExportIconBlack.png"));
ExportLogsIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "ExportIconBlack.png"));
ExportLogsIconBitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
ExportLogsIconBitmapImage.EndInit();
ExportLogsIcon.Source = ExportLogsIconBitmapImage;
Expand All @@ -65,7 +60,7 @@ private void LogsView(object obj)
// Add image to the ClearLogsIcon
BitmapImage ClearLogsIconBitmapImage = new();
ClearLogsIconBitmapImage.BeginInit();
ClearLogsIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path!, "Resources", "Media", "ClearLogsIcon.png"));
ClearLogsIconBitmapImage.UriSource = new Uri(Path.Combine(GlobalVars.path, "Resources", "Media", "ClearLogsIcon.png"));
ClearLogsIconBitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load the image data into memory
ClearLogsIconBitmapImage.EndInit();
ClearLogsIcon.Source = ClearLogsIconBitmapImage;
Expand Down
Loading

0 comments on commit 26cc92c

Please sign in to comment.