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 @@
-
-
-
+
+
+
+
-
-
+
+