From 026d1fbfd8e9af53e64aed31fe26c2b611e2e151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Wed, 4 Oct 2023 14:48:08 +0200 Subject: [PATCH] SEBWIN-732: Implemented random desktop functionality. --- SafeExamBrowser.Client/CompositionRoot.cs | 1 + SafeExamBrowser.Runtime/CompositionRoot.cs | 2 + .../Operations/KioskModeOperation.cs | 49 ++++++++++--------- .../IDesktopFactory.cs | 6 +++ .../{ => Desktops}/Desktop.cs | 4 +- .../{ => Desktops}/DesktopFactory.cs | 46 +++++++++++++++-- .../{ => Desktops}/DesktopMonitor.cs | 2 +- .../Desktops/ObfuscatedDesktop.cs | 29 +++++++++++ .../{ => Processes}/Process.cs | 2 +- .../{ => Processes}/ProcessFactory.cs | 2 +- .../SafeExamBrowser.WindowsApi.csproj | 11 +++-- 11 files changed, 116 insertions(+), 38 deletions(-) rename SafeExamBrowser.WindowsApi/{ => Desktops}/Desktop.cs (92%) rename SafeExamBrowser.WindowsApi/{ => Desktops}/DesktopFactory.cs (65%) rename SafeExamBrowser.WindowsApi/{ => Desktops}/DesktopMonitor.cs (97%) create mode 100644 SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs rename SafeExamBrowser.WindowsApi/{ => Processes}/Process.cs (98%) rename SafeExamBrowser.WindowsApi/{ => Processes}/ProcessFactory.cs (99%) diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 23042243..29c0ea09 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -54,6 +54,7 @@ using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Shared.Activators; using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi.Contracts; +using SafeExamBrowser.WindowsApi.Processes; using Desktop = SafeExamBrowser.UserInterface.Desktop; using Mobile = SafeExamBrowser.UserInterface.Mobile; diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 1df5a697..503a6999 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -35,6 +35,8 @@ using SafeExamBrowser.SystemComponents.Contracts; using SafeExamBrowser.SystemComponents.Registry; using SafeExamBrowser.UserInterface.Desktop; using SafeExamBrowser.WindowsApi; +using SafeExamBrowser.WindowsApi.Desktops; +using SafeExamBrowser.WindowsApi.Processes; namespace SafeExamBrowser.Runtime { diff --git a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs index 8e4d096c..09b47cc5 100644 --- a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs @@ -17,14 +17,15 @@ namespace SafeExamBrowser.Runtime.Operations { internal class KioskModeOperation : SessionOperation { - private IDesktop newDesktop; - private IDesktop originalDesktop; - private IDesktopFactory desktopFactory; - private IDesktopMonitor desktopMonitor; - private IExplorerShell explorerShell; + private readonly IDesktopFactory desktopFactory; + private readonly IDesktopMonitor desktopMonitor; + private readonly IExplorerShell explorerShell; + private readonly ILogger logger; + private readonly IProcessFactory processFactory; + private KioskMode? activeMode; - private ILogger logger; - private IProcessFactory processFactory; + private IDesktop customDesktop; + private IDesktop originalDesktop; public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event StatusChangedEventHandler StatusChanged; @@ -54,7 +55,7 @@ namespace SafeExamBrowser.Runtime.Operations switch (Context.Next.Settings.Security.KioskMode) { case KioskMode.CreateNewDesktop: - CreateNewDesktop(); + CreateCustomDesktop(); break; case KioskMode.DisableExplorerShell: TerminateExplorerShell(); @@ -80,7 +81,7 @@ namespace SafeExamBrowser.Runtime.Operations switch (activeMode) { case KioskMode.CreateNewDesktop: - CloseNewDesktop(); + CloseCustomDesktop(); break; case KioskMode.DisableExplorerShell: RestartExplorerShell(); @@ -92,7 +93,7 @@ namespace SafeExamBrowser.Runtime.Operations switch (newMode) { case KioskMode.CreateNewDesktop: - CreateNewDesktop(); + CreateCustomDesktop(); break; case KioskMode.DisableExplorerShell: TerminateExplorerShell(); @@ -111,7 +112,7 @@ namespace SafeExamBrowser.Runtime.Operations switch (activeMode) { case KioskMode.CreateNewDesktop: - CloseNewDesktop(); + CloseCustomDesktop(); break; case KioskMode.DisableExplorerShell: RestartExplorerShell(); @@ -121,26 +122,26 @@ namespace SafeExamBrowser.Runtime.Operations return OperationResult.Success; } - private void CreateNewDesktop() + private void CreateCustomDesktop() { originalDesktop = desktopFactory.GetCurrent(); logger.Info($"Current desktop is {originalDesktop}."); - newDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser)); - logger.Info($"Created new desktop {newDesktop}."); + customDesktop = desktopFactory.CreateRandom(); + logger.Info($"Created custom desktop {customDesktop}."); - newDesktop.Activate(); - processFactory.StartupDesktop = newDesktop; - logger.Info("Successfully activated new desktop."); + customDesktop.Activate(); + processFactory.StartupDesktop = customDesktop; + logger.Info("Successfully activated custom desktop."); - desktopMonitor.Start(newDesktop); + desktopMonitor.Start(customDesktop); } - private void CloseNewDesktop() + private void CloseCustomDesktop() { desktopMonitor.Stop(); - if (originalDesktop != null) + if (originalDesktop != default) { originalDesktop.Activate(); processFactory.StartupDesktop = originalDesktop; @@ -151,14 +152,14 @@ namespace SafeExamBrowser.Runtime.Operations logger.Warn($"No original desktop found to activate!"); } - if (newDesktop != null) + if (customDesktop != default) { - newDesktop.Close(); - logger.Info($"Closed new desktop {newDesktop}."); + customDesktop.Close(); + logger.Info($"Closed custom desktop {customDesktop}."); } else { - logger.Warn($"No new desktop found to close!"); + logger.Warn($"No custom desktop found to close!"); } } diff --git a/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs b/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs index 4b8d31ce..23e8d9fa 100644 --- a/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs @@ -19,6 +19,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts /// If the desktop could not be created. IDesktop CreateNew(string name); + /// + /// Creates a new desktop with a random name. + /// + /// If the desktop could not be created. + IDesktop CreateRandom(); + /// /// Retrieves the currently active desktop. /// diff --git a/SafeExamBrowser.WindowsApi/Desktop.cs b/SafeExamBrowser.WindowsApi/Desktops/Desktop.cs similarity index 92% rename from SafeExamBrowser.WindowsApi/Desktop.cs rename to SafeExamBrowser.WindowsApi/Desktops/Desktop.cs index 38d34b6e..2d54990a 100644 --- a/SafeExamBrowser.WindowsApi/Desktop.cs +++ b/SafeExamBrowser.WindowsApi/Desktops/Desktop.cs @@ -11,9 +11,9 @@ using System.ComponentModel; using System.Runtime.InteropServices; using SafeExamBrowser.WindowsApi.Contracts; -namespace SafeExamBrowser.WindowsApi +namespace SafeExamBrowser.WindowsApi.Desktops { - public class Desktop : IDesktop + internal class Desktop : IDesktop { public IntPtr Handle { get; private set; } public string Name { get; private set; } diff --git a/SafeExamBrowser.WindowsApi/DesktopFactory.cs b/SafeExamBrowser.WindowsApi/Desktops/DesktopFactory.cs similarity index 65% rename from SafeExamBrowser.WindowsApi/DesktopFactory.cs rename to SafeExamBrowser.WindowsApi/Desktops/DesktopFactory.cs index 440f5dc8..02f2238c 100644 --- a/SafeExamBrowser.WindowsApi/DesktopFactory.cs +++ b/SafeExamBrowser.WindowsApi/Desktops/DesktopFactory.cs @@ -10,18 +10,20 @@ using System; using System.ComponentModel; using System.Runtime.InteropServices; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Constants; +using SafeExamBrowser.WindowsApi.Contracts; -namespace SafeExamBrowser.WindowsApi +namespace SafeExamBrowser.WindowsApi.Desktops { public class DesktopFactory : IDesktopFactory { - private ILogger logger; + private readonly ILogger logger; + private readonly Random random; public DesktopFactory(ILogger logger) { this.logger = logger; + this.random = new Random(); } public IDesktop CreateNew(string name) @@ -44,11 +46,34 @@ namespace SafeExamBrowser.WindowsApi return desktop; } + public IDesktop CreateRandom() + { + logger.Debug($"Attempting to create random desktop..."); + + var name = GenerateRandomDesktopName(); + var handle = User32.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, (uint) AccessMask.GENERIC_ALL, IntPtr.Zero); + + if (handle == IntPtr.Zero) + { + logger.Error($"Failed to create random desktop '{name}'!"); + + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + var obfuscatedHandle = new IntPtr(random.Next(100, 10000)); + var obfuscatedName = GenerateRandomDesktopName(); + var desktop = new ObfuscatedDesktop(handle, name, obfuscatedHandle, obfuscatedName); + + logger.Debug($"Successfully created random desktop {desktop}."); + + return desktop; + } + public IDesktop GetCurrent() { var threadId = Kernel32.GetCurrentThreadId(); var handle = User32.GetThreadDesktop(threadId); - var name = String.Empty; + var name = string.Empty; var nameLength = 0; if (handle == IntPtr.Zero) @@ -81,5 +106,18 @@ namespace SafeExamBrowser.WindowsApi return desktop; } + + private string GenerateRandomDesktopName() + { + var length = random.Next(5, 20); + var name = new char[length]; + + for (var letter = 0; letter < length; letter++) + { + name[letter] = (char) (random.Next(2) == 0 && letter != 0 ? random.Next('a', 'z' + 1) : random.Next('A', 'Z' + 1)); + } + + return new string(name); + } } } diff --git a/SafeExamBrowser.WindowsApi/DesktopMonitor.cs b/SafeExamBrowser.WindowsApi/Desktops/DesktopMonitor.cs similarity index 97% rename from SafeExamBrowser.WindowsApi/DesktopMonitor.cs rename to SafeExamBrowser.WindowsApi/Desktops/DesktopMonitor.cs index d633c69c..0bade340 100644 --- a/SafeExamBrowser.WindowsApi/DesktopMonitor.cs +++ b/SafeExamBrowser.WindowsApi/Desktops/DesktopMonitor.cs @@ -13,7 +13,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Contracts; -namespace SafeExamBrowser.WindowsApi +namespace SafeExamBrowser.WindowsApi.Desktops { public class DesktopMonitor : IDesktopMonitor { diff --git a/SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs b/SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs new file mode 100644 index 00000000..3ea57ba2 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/Desktops/ObfuscatedDesktop.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; + +namespace SafeExamBrowser.WindowsApi.Desktops +{ + internal class ObfuscatedDesktop : Desktop + { + private readonly IntPtr? obfuscatedHandle; + private readonly string obfuscatedName; + + public ObfuscatedDesktop(IntPtr handle, string name, IntPtr? obfuscatedHandle, string obfuscatedName) : base(handle, name) + { + this.obfuscatedHandle = obfuscatedHandle; + this.obfuscatedName = obfuscatedName; + } + + public override string ToString() + { + return $"'{obfuscatedName}' [{obfuscatedHandle}]"; + } + } +} diff --git a/SafeExamBrowser.WindowsApi/Process.cs b/SafeExamBrowser.WindowsApi/Processes/Process.cs similarity index 98% rename from SafeExamBrowser.WindowsApi/Process.cs rename to SafeExamBrowser.WindowsApi/Processes/Process.cs index 541d10c9..72d62965 100644 --- a/SafeExamBrowser.WindowsApi/Process.cs +++ b/SafeExamBrowser.WindowsApi/Processes/Process.cs @@ -12,7 +12,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts.Events; -namespace SafeExamBrowser.WindowsApi +namespace SafeExamBrowser.WindowsApi.Processes { internal class Process : IProcess { diff --git a/SafeExamBrowser.WindowsApi/ProcessFactory.cs b/SafeExamBrowser.WindowsApi/Processes/ProcessFactory.cs similarity index 99% rename from SafeExamBrowser.WindowsApi/ProcessFactory.cs rename to SafeExamBrowser.WindowsApi/Processes/ProcessFactory.cs index a68cd507..6e528f7b 100644 --- a/SafeExamBrowser.WindowsApi/ProcessFactory.cs +++ b/SafeExamBrowser.WindowsApi/Processes/ProcessFactory.cs @@ -20,7 +20,7 @@ using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Types; -namespace SafeExamBrowser.WindowsApi +namespace SafeExamBrowser.WindowsApi.Processes { public class ProcessFactory : IProcessFactory { diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj index 34f6f1e1..070c515c 100644 --- a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj +++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj @@ -63,14 +63,15 @@ - - - + + + + - - + +