SEBWIN-732: Implemented random desktop functionality.

This commit is contained in:
Damian Büchel 2023-10-04 14:48:08 +02:00
parent 3711555f70
commit 026d1fbfd8
11 changed files with 116 additions and 38 deletions

View file

@ -54,6 +54,7 @@ using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Activators; using SafeExamBrowser.UserInterface.Shared.Activators;
using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Processes;
using Desktop = SafeExamBrowser.UserInterface.Desktop; using Desktop = SafeExamBrowser.UserInterface.Desktop;
using Mobile = SafeExamBrowser.UserInterface.Mobile; using Mobile = SafeExamBrowser.UserInterface.Mobile;

View file

@ -35,6 +35,8 @@ using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.SystemComponents.Registry; using SafeExamBrowser.SystemComponents.Registry;
using SafeExamBrowser.UserInterface.Desktop; using SafeExamBrowser.UserInterface.Desktop;
using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Desktops;
using SafeExamBrowser.WindowsApi.Processes;
namespace SafeExamBrowser.Runtime namespace SafeExamBrowser.Runtime
{ {

View file

@ -17,14 +17,15 @@ namespace SafeExamBrowser.Runtime.Operations
{ {
internal class KioskModeOperation : SessionOperation internal class KioskModeOperation : SessionOperation
{ {
private IDesktop newDesktop; private readonly IDesktopFactory desktopFactory;
private IDesktop originalDesktop; private readonly IDesktopMonitor desktopMonitor;
private IDesktopFactory desktopFactory; private readonly IExplorerShell explorerShell;
private IDesktopMonitor desktopMonitor; private readonly ILogger logger;
private IExplorerShell explorerShell; private readonly IProcessFactory processFactory;
private KioskMode? activeMode; private KioskMode? activeMode;
private ILogger logger; private IDesktop customDesktop;
private IProcessFactory processFactory; private IDesktop originalDesktop;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public override event StatusChangedEventHandler StatusChanged; public override event StatusChangedEventHandler StatusChanged;
@ -54,7 +55,7 @@ namespace SafeExamBrowser.Runtime.Operations
switch (Context.Next.Settings.Security.KioskMode) switch (Context.Next.Settings.Security.KioskMode)
{ {
case KioskMode.CreateNewDesktop: case KioskMode.CreateNewDesktop:
CreateNewDesktop(); CreateCustomDesktop();
break; break;
case KioskMode.DisableExplorerShell: case KioskMode.DisableExplorerShell:
TerminateExplorerShell(); TerminateExplorerShell();
@ -80,7 +81,7 @@ namespace SafeExamBrowser.Runtime.Operations
switch (activeMode) switch (activeMode)
{ {
case KioskMode.CreateNewDesktop: case KioskMode.CreateNewDesktop:
CloseNewDesktop(); CloseCustomDesktop();
break; break;
case KioskMode.DisableExplorerShell: case KioskMode.DisableExplorerShell:
RestartExplorerShell(); RestartExplorerShell();
@ -92,7 +93,7 @@ namespace SafeExamBrowser.Runtime.Operations
switch (newMode) switch (newMode)
{ {
case KioskMode.CreateNewDesktop: case KioskMode.CreateNewDesktop:
CreateNewDesktop(); CreateCustomDesktop();
break; break;
case KioskMode.DisableExplorerShell: case KioskMode.DisableExplorerShell:
TerminateExplorerShell(); TerminateExplorerShell();
@ -111,7 +112,7 @@ namespace SafeExamBrowser.Runtime.Operations
switch (activeMode) switch (activeMode)
{ {
case KioskMode.CreateNewDesktop: case KioskMode.CreateNewDesktop:
CloseNewDesktop(); CloseCustomDesktop();
break; break;
case KioskMode.DisableExplorerShell: case KioskMode.DisableExplorerShell:
RestartExplorerShell(); RestartExplorerShell();
@ -121,26 +122,26 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Success; return OperationResult.Success;
} }
private void CreateNewDesktop() private void CreateCustomDesktop()
{ {
originalDesktop = desktopFactory.GetCurrent(); originalDesktop = desktopFactory.GetCurrent();
logger.Info($"Current desktop is {originalDesktop}."); logger.Info($"Current desktop is {originalDesktop}.");
newDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser)); customDesktop = desktopFactory.CreateRandom();
logger.Info($"Created new desktop {newDesktop}."); logger.Info($"Created custom desktop {customDesktop}.");
newDesktop.Activate(); customDesktop.Activate();
processFactory.StartupDesktop = newDesktop; processFactory.StartupDesktop = customDesktop;
logger.Info("Successfully activated new desktop."); logger.Info("Successfully activated custom desktop.");
desktopMonitor.Start(newDesktop); desktopMonitor.Start(customDesktop);
} }
private void CloseNewDesktop() private void CloseCustomDesktop()
{ {
desktopMonitor.Stop(); desktopMonitor.Stop();
if (originalDesktop != null) if (originalDesktop != default)
{ {
originalDesktop.Activate(); originalDesktop.Activate();
processFactory.StartupDesktop = originalDesktop; processFactory.StartupDesktop = originalDesktop;
@ -151,14 +152,14 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Warn($"No original desktop found to activate!"); logger.Warn($"No original desktop found to activate!");
} }
if (newDesktop != null) if (customDesktop != default)
{ {
newDesktop.Close(); customDesktop.Close();
logger.Info($"Closed new desktop {newDesktop}."); logger.Info($"Closed custom desktop {customDesktop}.");
} }
else else
{ {
logger.Warn($"No new desktop found to close!"); logger.Warn($"No custom desktop found to close!");
} }
} }

View file

@ -19,6 +19,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts
/// <exception cref="System.ComponentModel.Win32Exception">If the desktop could not be created.</exception> /// <exception cref="System.ComponentModel.Win32Exception">If the desktop could not be created.</exception>
IDesktop CreateNew(string name); IDesktop CreateNew(string name);
/// <summary>
/// Creates a new desktop with a random name.
/// </summary>
/// <exception cref="System.ComponentModel.Win32Exception">If the desktop could not be created.</exception>
IDesktop CreateRandom();
/// <summary> /// <summary>
/// Retrieves the currently active desktop. /// Retrieves the currently active desktop.
/// </summary> /// </summary>

View file

@ -11,9 +11,9 @@ using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SafeExamBrowser.WindowsApi.Contracts; 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 IntPtr Handle { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }

View file

@ -10,18 +10,20 @@ using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.WindowsApi namespace SafeExamBrowser.WindowsApi.Desktops
{ {
public class DesktopFactory : IDesktopFactory public class DesktopFactory : IDesktopFactory
{ {
private ILogger logger; private readonly ILogger logger;
private readonly Random random;
public DesktopFactory(ILogger logger) public DesktopFactory(ILogger logger)
{ {
this.logger = logger; this.logger = logger;
this.random = new Random();
} }
public IDesktop CreateNew(string name) public IDesktop CreateNew(string name)
@ -44,11 +46,34 @@ namespace SafeExamBrowser.WindowsApi
return desktop; 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() public IDesktop GetCurrent()
{ {
var threadId = Kernel32.GetCurrentThreadId(); var threadId = Kernel32.GetCurrentThreadId();
var handle = User32.GetThreadDesktop(threadId); var handle = User32.GetThreadDesktop(threadId);
var name = String.Empty; var name = string.Empty;
var nameLength = 0; var nameLength = 0;
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
@ -81,5 +106,18 @@ namespace SafeExamBrowser.WindowsApi
return desktop; 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);
}
} }
} }

View file

@ -13,7 +13,7 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.WindowsApi namespace SafeExamBrowser.WindowsApi.Desktops
{ {
public class DesktopMonitor : IDesktopMonitor public class DesktopMonitor : IDesktopMonitor
{ {

View file

@ -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}]";
}
}
}

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events; using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.WindowsApi namespace SafeExamBrowser.WindowsApi.Processes
{ {
internal class Process : IProcess internal class Process : IProcess
{ {

View file

@ -20,7 +20,7 @@ using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi namespace SafeExamBrowser.WindowsApi.Processes
{ {
public class ProcessFactory : IProcessFactory public class ProcessFactory : IProcessFactory
{ {

View file

@ -63,14 +63,15 @@
<Compile Include="Delegates\EnumWindowsDelegate.cs" /> <Compile Include="Delegates\EnumWindowsDelegate.cs" />
<Compile Include="Delegates\EventDelegate.cs" /> <Compile Include="Delegates\EventDelegate.cs" />
<Compile Include="Delegates\HookDelegate.cs" /> <Compile Include="Delegates\HookDelegate.cs" />
<Compile Include="Desktop.cs" /> <Compile Include="Desktops\Desktop.cs" />
<Compile Include="DesktopFactory.cs" /> <Compile Include="Desktops\DesktopFactory.cs" />
<Compile Include="DesktopMonitor.cs" /> <Compile Include="Desktops\DesktopMonitor.cs" />
<Compile Include="Desktops\ObfuscatedDesktop.cs" />
<Compile Include="ExplorerShell.cs" /> <Compile Include="ExplorerShell.cs" />
<Compile Include="Hooks\MouseHook.cs" /> <Compile Include="Hooks\MouseHook.cs" />
<Compile Include="Hooks\SystemHook.cs" /> <Compile Include="Hooks\SystemHook.cs" />
<Compile Include="Process.cs" /> <Compile Include="Processes\Process.cs" />
<Compile Include="ProcessFactory.cs" /> <Compile Include="Processes\ProcessFactory.cs" />
<Compile Include="Constants\AccessMask.cs" /> <Compile Include="Constants\AccessMask.cs" />
<Compile Include="Types\Bounds.cs" /> <Compile Include="Types\Bounds.cs" />
<Compile Include="Types\EXECUTION_STATE.cs" /> <Compile Include="Types\EXECUTION_STATE.cs" />