Skip to content

Commit

Permalink
AppControl Manager v.1.9.2.0 (#617)
Browse files Browse the repository at this point in the history
The User Configurations directory name which has been WDACConfig in the Program Files directory is now changed to AppControl Manager to better reflect the product name. All of the files and directories in the previous directory will be automatically moved to the new directory upon application start. All of the paths in the User Configurations JSON file will be updated to reflect the new directory. The old directory will be removed at the end. This process only happens 1 time, is very fast and requires no user interaction.

WDACConfig was the name of a now deprecated PowerShell module and the directory name was a remnant of that which has been kept unchanged for the sake of interoperability that is no longer the case.
Added a new feature to the Deploy App Control Policies page that lets you select one or more XML files and convert them to CIP files without deploying them.

Added a new feature to the Deploy App Control Policies page that lets you create signed CIP files without deploying them. It will essentially create signed CIP files for you that you can then use to manually deploy them to any other system.

Bumped version to 1.9.2.0.

Improved logging across the app when an error occurs.

Fixed an issue with Allow New Apps' sub-pages where data wouldn't be populated if you were in one of the pages with ListViews and you'd have to switch to the main page and then back to view the data. This was a regression in 1.9.1.0.

Improved the column sorting and naming in pages with ListViews that show data from event logs. They now show more relevant data.

Improved the XML deserialization logic.

Adjusted the locations of all the Guide buttons in each page, they now all appear in the same place, improving consistency.

Fixed an issue where if there was an error while a content dialog was open, the error message wouldn't be displayed. Now content dialogs are tracked globally and if an error occurs while one of them is open, they will be closed for the error message to be displayed in a dedicated content dialog.
  • Loading branch information
HotCakeX authored Feb 23, 2025
1 parent d256435 commit ad43ab0
Show file tree
Hide file tree
Showing 34 changed files with 659 additions and 218 deletions.
162 changes: 159 additions & 3 deletions AppControl Manager/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading;
using System.Threading.Tasks;
using AppControlManager.AppSettings;
using AppControlManager.Main;
using AppControlManager.Others;
using CommunityToolkit.WinUI;
using Microsoft.UI;
Expand Down Expand Up @@ -37,6 +38,10 @@ public partial class App : Application
private static Mutex? _mutex;
private const string MutexName = "AppControlManagerRunning";

// To track the currently open Content Dialog across the app. Every piece of code that tries to display a content dialog, whether custom or generic, must assign it first
// to this variable before using ShowAsync() method to display it.
internal static ContentDialog? CurrentlyOpenContentDialog;

/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
Expand All @@ -45,15 +50,16 @@ public App()
{
this.InitializeComponent();

// to handle unhandled exceptions
this.UnhandledException += App_UnhandledException;

Logger.Write($"App Startup, .NET runtime version: {Environment.Version}");

// Give beautiful outline to the UI elements when using the tab key and keyboard for navigation
// https://learn.microsoft.com/en-us/windows/apps/design/style/reveal-focus
this.FocusVisualKind = FocusVisualKind.Reveal;

// to handle unhandled exceptions
this.UnhandledException += App_UnhandledException;

MoveUserConfigDirectory();

#region

Expand Down Expand Up @@ -177,6 +183,15 @@ private async Task ShowErrorDialogAsync(Exception ex)
await m_window.DispatcherQueue.EnqueueAsync(async () =>
{

// Since only 1 content dialog can be displayed at a time, we close any currently active ones before showing the error
if (CurrentlyOpenContentDialog is ContentDialog dialog)
{
dialog.Hide();

// Remove it after hiding it
CurrentlyOpenContentDialog = null;
}

ContentDialog errorDialog = new()
{
Title = "An error occurred",
Expand All @@ -198,4 +213,145 @@ await m_window.DispatcherQueue.EnqueueAsync(async () =>
}
}
}

// Path to the old user config directory
private static readonly string OldUserConfigDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WDACConfig");

/// <summary>
/// This method will move everything from the old user config dir to the new one and deletes the old one at the end
/// This will be removed in few months once all users have installed the new app version and use the new location.
/// The old location was called "WDACConfig" because it was the location of the module i had created. I maintained the
/// same location to provide interoperability for both the module and the new app but the module is now deprecated so
/// it's time to change the user config location name to an appropriate one.
/// </summary>
private static void MoveUserConfigDirectory()
{
// Ensure the new user config directory exists
if (!Directory.Exists(GlobalVars.UserConfigDir))
{
_ = Directory.CreateDirectory(GlobalVars.UserConfigDir);
}

// Check if the old user config directory exists
if (Directory.Exists(OldUserConfigDir))
{

Logger.Write(@"Moving the user config directory to the new location at 'Program Files\AppControl Manager");

// Step 1: Recreate the directory structure

// Get all subdirectories (recursively) from the old config directory
string[] directories = Directory.GetDirectories(OldUserConfigDir, "*", SearchOption.AllDirectories);

foreach (string oldDir in directories)
{
// Calculate the relative path of the old directory compared to the root of the old config
string relativePath = Path.GetRelativePath(OldUserConfigDir, oldDir);

// Combine the new config directory with the relative path to get the new directory path
string newDir = Path.Combine(GlobalVars.UserConfigDir, relativePath);

// Create the new directory if it does not exist
if (!Directory.Exists(newDir))
{
_ = Directory.CreateDirectory(newDir);
}
}

// Step 2: Move all files while preserving their relative positions

// Get all files (recursively) from the old config directory
string[] files = Directory.GetFiles(OldUserConfigDir, "*", SearchOption.AllDirectories);

foreach (string filePath in files)
{
// Calculate the file's relative path from the old config directory
string relativeFilePath = Path.GetRelativePath(OldUserConfigDir, filePath);

// Combine with the new config directory to get the target file path
string destFilePath = Path.Combine(GlobalVars.UserConfigDir, relativeFilePath);

// Ensure that the destination subdirectory exists (double-check)
string? destSubDir = Path.GetDirectoryName(destFilePath);

if (!string.IsNullOrEmpty(destSubDir) && !Directory.Exists(destSubDir))
{
_ = Directory.CreateDirectory(destSubDir);
}

// Move the file to the new directory
try
{
File.Move(filePath, destFilePath, overwrite: true);
}
catch (Exception ex)
{
Logger.Write(ErrorWriter.FormatException(ex));
}
}

// Step 3: Delete the old user config directory
Directory.Delete(OldUserConfigDir, recursive: true);

// Step 4: Get all of the user configurations from the JSON file
UserConfiguration config = UserConfiguration.Get();

string? newSignToolCustomPath = null;
string? newCertificatePath = null;
string? newUnsignedPolicyPath = null;
string? newSignedPolicyPath = null;

if (!string.IsNullOrEmpty(config.SignToolCustomPath) && config.SignToolCustomPath.Contains(OldUserConfigDir))
{
newSignToolCustomPath = config.SignToolCustomPath.Replace(OldUserConfigDir, GlobalVars.UserConfigDir);

if (!File.Exists(newSignToolCustomPath))
{
newSignToolCustomPath = null;
}
}
if (!string.IsNullOrEmpty(config.CertificatePath) && config.CertificatePath.Contains(OldUserConfigDir))
{
newCertificatePath = config.CertificatePath.Replace(OldUserConfigDir, GlobalVars.UserConfigDir);

if (!File.Exists(newCertificatePath))
{
newCertificatePath = null;
}
}
if (!string.IsNullOrEmpty(config.UnsignedPolicyPath) && config.UnsignedPolicyPath.Contains(OldUserConfigDir))
{
newUnsignedPolicyPath = config.UnsignedPolicyPath.Replace(OldUserConfigDir, GlobalVars.UserConfigDir);

if (!File.Exists(newUnsignedPolicyPath))
{
newUnsignedPolicyPath = null;
}
}
if (!string.IsNullOrEmpty(config.SignedPolicyPath) && config.SignedPolicyPath.Contains(OldUserConfigDir))
{
newSignedPolicyPath = config.SignedPolicyPath.Replace(OldUserConfigDir, GlobalVars.UserConfigDir);

if (!File.Exists(newSignedPolicyPath))
{
newSignedPolicyPath = null;
}
}

try
{
// Replace the "WDACConfig" with "AppControl Manager" in user configurations JSON file
_ = UserConfiguration.Set(
SignedPolicyPath: newSignedPolicyPath,
UnsignedPolicyPath: newUnsignedPolicyPath,
SignToolCustomPath: newSignToolCustomPath,
CertificatePath: newCertificatePath
);
}
catch (Exception ex)
{
Logger.Write(ErrorWriter.FormatException(ex));
}
}
}
}
19 changes: 1 addition & 18 deletions AppControl Manager/AppControl Manager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<PublishAot>True</PublishAot>
<OptimizationPreference>Speed</OptimizationPreference>
<ErrorReport>send</ErrorReport>
<FileVersion>1.9.1.0</FileVersion>
<FileVersion>1.9.2.0</FileVersion>
<AssemblyVersion>$(FileVersion)</AssemblyVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down Expand Up @@ -118,15 +118,6 @@
<XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
</PropertyGroup>

<!--
This section is no longer necessary since this generator is not Native AOT compatible and serialization/deserialization logic has been manually implemented with static code
ARM64 doesn't support source generated XML de(serialization)
<ItemGroup Condition="'$(RuntimeIdentifier)' != 'win-arm64'">
<DotNetCliToolReference Include="Microsoft.XmlSerializer.Generator" Version="9.0.1" />
</ItemGroup>
-->

<!--
<ItemGroup>
<TrimmerRootDescriptor Include="MyRoots.xml" />
Expand Down Expand Up @@ -173,14 +164,6 @@
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250129-preview2" />

<!--
No longer needed - manual static code has been implemented
ARM64 doesn't support source generated XML de(serialization)
<PackageReference Include="Microsoft.XmlSerializer.Generator" Version="9.0.2" Condition="'$(RuntimeIdentifier)' != 'win-arm64'" />
-->

<PackageReference Include="System.Diagnostics.EventLog" Version="9.0.2" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.2" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion AppControl Manager/Main/AppControlSimulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ [.. GetCertificateDetails.Get([.. FileSignatureResults])], // Get all of the de
// If user chose to output the results to CSV file
if (csvOutput)
{
ExportToCsv(FinalSimulationResults, @$"C:\Program Files\WDACConfig\AppControl Simulation output {DateTime.Now:yyyy-MM-dd HH-mm-ss}.csv");
ExportToCsv(FinalSimulationResults, Path.Combine(GlobalVars.UserConfigDir, @$"AppControl Simulation output {DateTime.Now:yyyy-MM-dd HH-mm-ss}.csv"));
}

return FinalSimulationResults;
Expand Down
4 changes: 3 additions & 1 deletion AppControl Manager/Main/BasePolicyCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ internal sealed class DriverBlockListInfo
}
catch (Exception ex)
{
Logger.Write($"An error occurred while retrieving additional information related to the Microsoft recommended driver block rules: {ex.Message}");
Logger.Write($"An error occurred while retrieving additional information related to the Microsoft recommended driver block rules.");

Logger.Write(ErrorWriter.FormatException(ex));

// Return null in case of an error
return null;
Expand Down
8 changes: 4 additions & 4 deletions AppControl Manager/Main/UserConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,19 +217,19 @@ private static UserConfiguration ReadUserConfiguration()
{
try
{
// Create the WDACConfig folder in Program Files if it doesn't exist
// Create the AppControl Manager folder in Program Files if it doesn't exist
if (!Directory.Exists(GlobalVars.UserConfigDir))
{
_ = Directory.CreateDirectory(GlobalVars.UserConfigDir);
Logger.Write("The WDACConfig folder in Program Files has been created because it did not exist.");
Logger.Write("The AppControl Manager folder in Program Files has been created because it did not exist.");
}

// Create User configuration folder in the WDACConfig folder if it doesn't already exist
// Create User configuration folder in the AppControl Manager folder if it doesn't already exist
string UserConfigDir = Path.Combine(GlobalVars.UserConfigDir, "UserConfigurations");
if (!Directory.Exists(UserConfigDir))
{
_ = Directory.CreateDirectory(UserConfigDir);
Logger.Write("The WDACConfig folder in Program Files has been created because it did not exist.");
Logger.Write("The AppControl Manager folder in Program Files has been created because it did not exist.");
}

// Read the JSON file
Expand Down
56 changes: 27 additions & 29 deletions AppControl Manager/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ internal sealed class PageTitleMap
},
[typeof(Pages.GitHubDocumentation)] = new PageTitleMap
{
Titles = ["GitHub Documentation"],
Titles = [GlobalVars.Rizz.GetString("GitHubDocsNavItem/Content")],
Pages = [typeof(Pages.GitHubDocumentation)]
},
[typeof(Pages.MicrosoftDocumentation)] = new PageTitleMap
{
Titles = ["Microsoft Documentation"],
Titles = [GlobalVars.Rizz.GetString("MSFTDocsNavItem/Content")],
Pages = [typeof(Pages.MicrosoftDocumentation)]
},
[typeof(Pages.GetSecurePolicySettings)] = new PageTitleMap
Expand All @@ -90,7 +90,7 @@ internal sealed class PageTitleMap
},
[typeof(Pages.Settings)] = new PageTitleMap
{
Titles = ["Settings"],
Titles = [GlobalVars.Rizz.GetString("SettingsNavItem/Content")],
Pages = [typeof(Pages.Settings)]
},
[typeof(Pages.SystemInformation)] = new PageTitleMap
Expand All @@ -105,7 +105,7 @@ internal sealed class PageTitleMap
},
[typeof(Pages.Logs)] = new PageTitleMap
{
Titles = ["Logs"],
Titles = [GlobalVars.Rizz.GetString("LogsNavItem/Content")],
Pages = [typeof(Pages.Logs)]
},
[typeof(Pages.Simulation)] = new PageTitleMap
Expand All @@ -115,12 +115,12 @@ internal sealed class PageTitleMap
},
[typeof(Pages.UpdatePage)] = new PageTitleMap
{
Titles = [GlobalVars.Rizz.GetString("Update"), "Custom MSIXBundle Path"],
Titles = [GlobalVars.Rizz.GetString("UpdateNavItem/Content"), "Custom MSIXBundle Path"],
Pages = [typeof(Pages.UpdatePage), typeof(Pages.UpdatePageCustomMSIXPath)]
},
[typeof(Pages.UpdatePageCustomMSIXPath)] = new PageTitleMap // sub-page
{
Titles = [GlobalVars.Rizz.GetString("Update"), "Custom MSIXBundle Path"],
Titles = [GlobalVars.Rizz.GetString("UpdateNavItem/Content"), "Custom MSIXBundle Path"],
Pages = [typeof(Pages.UpdatePage), typeof(Pages.UpdatePageCustomMSIXPath)]
},
[typeof(Pages.DeploymentPage)] = new PageTitleMap
Expand Down Expand Up @@ -212,27 +212,27 @@ private void BreadcrumbBar_ItemClicked(BreadcrumbBar sender, BreadcrumbBarItemCl
// Sub-pages should only be added if they don't rely on/access the the instance of any page that might not be initialized
private static readonly Dictionary<string, Type> NavigationPageToItemContentMap = new()
{
{ "Create Policy", typeof(Pages.CreatePolicy) },
{ "Get Code Integrity Hashes", typeof(Pages.GetCIHashes) },
{ "GitHub Documentation", typeof(Pages.GitHubDocumentation) },
{ "Microsoft Documentation", typeof(Pages.MicrosoftDocumentation) },
{ "Get Secure Policy Settings", typeof(Pages.GetSecurePolicySettings) },
{ "Settings", typeof(Pages.Settings) },
{ "System Information", typeof(Pages.SystemInformation) },
{ "Configure Policy Rule Options", typeof(Pages.ConfigurePolicyRuleOptions) },
{ "Logs", typeof(Pages.Logs) },
{ "Simulation", typeof(Pages.Simulation) },
{ "Update", typeof(Pages.UpdatePage) },
{ "Deploy App Control Policy", typeof(Pages.DeploymentPage) },
{ "Create policy from Event Logs", typeof(Pages.EventLogsPolicyCreation) },
{ "MDE Advanced Hunting", typeof(Pages.MDEAHPolicyCreation) },
{ "Allow New Apps", typeof(Pages.AllowNewApps) },
{ "Build New Certificate", typeof(Pages.BuildNewCertificate) },
{ "Create Supplemental Policy", typeof(Pages.CreateSupplementalPolicy) },
{ "Merge App Control Policies", typeof(Pages.MergePolicies) },
{ "Create Deny Policy", typeof(Pages.CreateDenyPolicy) },
{ "Validate Policies", typeof(Pages.ValidatePolicy) },
{ "View File Certificates", typeof(Pages.ViewFileCertificates) }
{ GlobalVars.Rizz.GetString("CreatePolicyNavItem/Content"), typeof(Pages.CreatePolicy) },
{ GlobalVars.Rizz.GetString("GetCodeIntegrityHashesNavItem/Content"), typeof(Pages.GetCIHashes) },
{ GlobalVars.Rizz.GetString("GitHubDocsNavItem/Content"), typeof(Pages.GitHubDocumentation) },
{ GlobalVars.Rizz.GetString("MSFTDocsNavItem/Content"), typeof(Pages.MicrosoftDocumentation) },
{ GlobalVars.Rizz.GetString("GetSecurePolicySettingsNavItem/Content"), typeof(Pages.GetSecurePolicySettings) },
{ GlobalVars.Rizz.GetString("SettingsNavItem/Content"), typeof(Pages.Settings) },
{ GlobalVars.Rizz.GetString("SystemInformationNavItem/Content"), typeof(Pages.SystemInformation) },
{ GlobalVars.Rizz.GetString("ConfigurePolicyRuleOptionsNavItem/Content"), typeof(Pages.ConfigurePolicyRuleOptions) },
{ GlobalVars.Rizz.GetString("LogsNavItem/Content"), typeof(Pages.Logs) },
{ GlobalVars.Rizz.GetString("SimulationNavItem/Content"), typeof(Pages.Simulation) },
{ GlobalVars.Rizz.GetString("UpdateNavItem/Content"), typeof(Pages.UpdatePage) },
{ GlobalVars.Rizz.GetString("DeploymentNavItem/Content"), typeof(Pages.DeploymentPage) },
{ GlobalVars.Rizz.GetString("CreatePolicyFromEventLogsNavItem/Content"), typeof(Pages.EventLogsPolicyCreation) },
{ GlobalVars.Rizz.GetString("CreatePolicyFromMDEAHNavItem/Content"), typeof(Pages.MDEAHPolicyCreation) },
{ GlobalVars.Rizz.GetString("AllowNewAppsNavItem/Content"), typeof(Pages.AllowNewApps) },
{ GlobalVars.Rizz.GetString("BuildNewCertificateNavItem/Content"), typeof(Pages.BuildNewCertificate) },
{ GlobalVars.Rizz.GetString("CreateSupplementalPolicyNavItem/Content"), typeof(Pages.CreateSupplementalPolicy) },
{ GlobalVars.Rizz.GetString("MergePoliciesNavItem/Content"), typeof(Pages.MergePolicies) },
{ GlobalVars.Rizz.GetString("CreateDenyPolicyNavItem/Content"), typeof(Pages.CreateDenyPolicy) },
{ GlobalVars.Rizz.GetString("ValidatePoliciesNavItem/Content"), typeof(Pages.ValidatePolicy) },
{ GlobalVars.Rizz.GetString("ViewFileCertificatesNavItem/Content"), typeof(Pages.ViewFileCertificates) }
};


Expand Down Expand Up @@ -1146,7 +1146,6 @@ private void OnBackgroundChanged(object? sender, BackgroundChangedEventArgs e)
private void OnAppThemeChanged(object? sender, AppThemeChangedEventArgs e)
{


// Get the current system color mode
// UISettings uiSettings = new();
// ElementTheme currentColorMode = uiSettings.GetColorValue(UIColorType.Background) == Colors.Black
Expand Down Expand Up @@ -1263,7 +1262,6 @@ private void OnAppThemeChanged(object? sender, AppThemeChangedEventArgs e)
};

}

}

break;
Expand Down
Loading

0 comments on commit ad43ab0

Please sign in to comment.