SEBWIN-615: Implemented session integrity verification.
This commit is contained in:
parent
04bebdffb2
commit
1c42434b9a
15 changed files with 264 additions and 29 deletions
|
@ -76,6 +76,8 @@ namespace SafeExamBrowser.Client.UnitTests
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
var valid = true;
|
||||||
|
|
||||||
appConfig = new AppConfig();
|
appConfig = new AppConfig();
|
||||||
actionCenter = new Mock<IActionCenter>();
|
actionCenter = new Mock<IActionCenter>();
|
||||||
applicationMonitor = new Mock<IApplicationMonitor>();
|
applicationMonitor = new Mock<IApplicationMonitor>();
|
||||||
|
@ -101,6 +103,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
||||||
text = new Mock<IText>();
|
text = new Mock<IText>();
|
||||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||||
|
|
||||||
|
integrityModule.Setup(m => m.TryVerifySessionIntegrity(It.IsAny<string>(), It.IsAny<string>(), out valid)).Returns(true);
|
||||||
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
|
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
|
||||||
runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true));
|
runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true));
|
||||||
uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object);
|
uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object);
|
||||||
|
@ -113,7 +116,6 @@ namespace SafeExamBrowser.Client.UnitTests
|
||||||
explorerShell.Object,
|
explorerShell.Object,
|
||||||
fileSystemDialog.Object,
|
fileSystemDialog.Object,
|
||||||
hashAlgorithm.Object,
|
hashAlgorithm.Object,
|
||||||
integrityModule.Object,
|
|
||||||
logger.Object,
|
logger.Object,
|
||||||
messageBox.Object,
|
messageBox.Object,
|
||||||
operationSequence.Object,
|
operationSequence.Object,
|
||||||
|
@ -128,6 +130,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
||||||
context.AppConfig = appConfig;
|
context.AppConfig = appConfig;
|
||||||
context.Browser = browser.Object;
|
context.Browser = browser.Object;
|
||||||
context.ClientHost = clientHost.Object;
|
context.ClientHost = clientHost.Object;
|
||||||
|
context.IntegrityModule = integrityModule.Object;
|
||||||
context.Server = server.Object;
|
context.Server = server.Object;
|
||||||
context.SessionId = sessionId;
|
context.SessionId = sessionId;
|
||||||
context.Settings = settings;
|
context.Settings = settings;
|
||||||
|
@ -1062,6 +1065,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
||||||
context.AppConfig = null;
|
context.AppConfig = null;
|
||||||
context.Browser = null;
|
context.Browser = null;
|
||||||
context.ClientHost = null;
|
context.ClientHost = null;
|
||||||
|
context.IntegrityModule = null;
|
||||||
context.Server = null;
|
context.Server = null;
|
||||||
context.Settings = null;
|
context.Settings = null;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ using SafeExamBrowser.Applications.Contracts;
|
||||||
using SafeExamBrowser.Browser.Contracts;
|
using SafeExamBrowser.Browser.Contracts;
|
||||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
|
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||||
using SafeExamBrowser.Server.Contracts;
|
using SafeExamBrowser.Server.Contracts;
|
||||||
using SafeExamBrowser.Settings;
|
using SafeExamBrowser.Settings;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
|
@ -48,6 +49,11 @@ namespace SafeExamBrowser.Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IClientHost ClientHost { get; set; }
|
internal IClientHost ClientHost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The integrity module.
|
||||||
|
/// </summary>
|
||||||
|
internal IIntegrityModule IntegrityModule { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The server proxy to be used if the current session mode is <see cref="SessionMode.Server"/>.
|
/// The server proxy to be used if the current session mode is <see cref="SessionMode.Server"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -12,6 +12,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using SafeExamBrowser.Applications.Contracts;
|
using SafeExamBrowser.Applications.Contracts;
|
||||||
using SafeExamBrowser.Browser.Contracts;
|
using SafeExamBrowser.Browser.Contracts;
|
||||||
using SafeExamBrowser.Browser.Contracts.Events;
|
using SafeExamBrowser.Browser.Contracts.Events;
|
||||||
|
@ -51,7 +52,6 @@ namespace SafeExamBrowser.Client
|
||||||
private readonly IExplorerShell explorerShell;
|
private readonly IExplorerShell explorerShell;
|
||||||
private readonly IFileSystemDialog fileSystemDialog;
|
private readonly IFileSystemDialog fileSystemDialog;
|
||||||
private readonly IHashAlgorithm hashAlgorithm;
|
private readonly IHashAlgorithm hashAlgorithm;
|
||||||
private readonly IIntegrityModule integrityModule;
|
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
private readonly IMessageBox messageBox;
|
private readonly IMessageBox messageBox;
|
||||||
private readonly IOperationSequence operations;
|
private readonly IOperationSequence operations;
|
||||||
|
@ -65,6 +65,7 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private IBrowserApplication Browser => context.Browser;
|
private IBrowserApplication Browser => context.Browser;
|
||||||
private IClientHost ClientHost => context.ClientHost;
|
private IClientHost ClientHost => context.ClientHost;
|
||||||
|
private IIntegrityModule IntegrityModule => context.IntegrityModule;
|
||||||
private IServerProxy Server => context.Server;
|
private IServerProxy Server => context.Server;
|
||||||
private AppSettings Settings => context.Settings;
|
private AppSettings Settings => context.Settings;
|
||||||
|
|
||||||
|
@ -79,7 +80,6 @@ namespace SafeExamBrowser.Client
|
||||||
IExplorerShell explorerShell,
|
IExplorerShell explorerShell,
|
||||||
IFileSystemDialog fileSystemDialog,
|
IFileSystemDialog fileSystemDialog,
|
||||||
IHashAlgorithm hashAlgorithm,
|
IHashAlgorithm hashAlgorithm,
|
||||||
IIntegrityModule integrityModule,
|
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IMessageBox messageBox,
|
IMessageBox messageBox,
|
||||||
IOperationSequence operations,
|
IOperationSequence operations,
|
||||||
|
@ -98,7 +98,6 @@ namespace SafeExamBrowser.Client
|
||||||
this.explorerShell = explorerShell;
|
this.explorerShell = explorerShell;
|
||||||
this.fileSystemDialog = fileSystemDialog;
|
this.fileSystemDialog = fileSystemDialog;
|
||||||
this.hashAlgorithm = hashAlgorithm;
|
this.hashAlgorithm = hashAlgorithm;
|
||||||
this.integrityModule = integrityModule;
|
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.messageBox = messageBox;
|
this.messageBox = messageBox;
|
||||||
this.operations = operations;
|
this.operations = operations;
|
||||||
|
@ -137,6 +136,8 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
logger.Info("Application successfully initialized.");
|
logger.Info("Application successfully initialized.");
|
||||||
logger.Log(string.Empty);
|
logger.Log(string.Empty);
|
||||||
|
|
||||||
|
VerifySessionIntegrity();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -165,6 +166,7 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
CloseShell();
|
CloseShell();
|
||||||
DeregisterEvents();
|
DeregisterEvents();
|
||||||
|
UpdateSessionIntegrity();
|
||||||
|
|
||||||
var success = operations.TryRevert() == OperationResult.Success;
|
var success = operations.TryRevert() == OperationResult.Success;
|
||||||
|
|
||||||
|
@ -330,7 +332,7 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
logger.Info($"Attempting to verify application integrity...");
|
logger.Info($"Attempting to verify application integrity...");
|
||||||
|
|
||||||
if (integrityModule.TryVerifyCodeSignature(out var isValid))
|
if (IntegrityModule.TryVerifyCodeSignature(out var isValid))
|
||||||
{
|
{
|
||||||
if (isValid)
|
if (isValid)
|
||||||
{
|
{
|
||||||
|
@ -339,7 +341,7 @@ namespace SafeExamBrowser.Client
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Warn("Application integrity is compromised!");
|
logger.Warn("Application integrity is compromised!");
|
||||||
ShowLockScreen(text.Get(TextKey.LockScreen_IntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
ShowLockScreen(text.Get(TextKey.LockScreen_ApplicationIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -351,6 +353,47 @@ namespace SafeExamBrowser.Client
|
||||||
timer.Start();
|
timer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void VerifySessionIntegrity()
|
||||||
|
{
|
||||||
|
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||||
|
|
||||||
|
if (hasQuitPassword)
|
||||||
|
{
|
||||||
|
logger.Info($"Attempting to verify session integrity...");
|
||||||
|
|
||||||
|
if (IntegrityModule.TryVerifySessionIntegrity(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl, out var isValid))
|
||||||
|
{
|
||||||
|
if (isValid)
|
||||||
|
{
|
||||||
|
logger.Info("Session integrity successfully verified.");
|
||||||
|
IntegrityModule.CacheSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Session integrity is compromised!");
|
||||||
|
Task.Delay(1000).ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
ShowLockScreen(text.Get(TextKey.LockScreen_SessionIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to verify session integrity!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSessionIntegrity()
|
||||||
|
{
|
||||||
|
var hasQuitPassword = !string.IsNullOrEmpty(Settings?.Security.QuitPasswordHash);
|
||||||
|
|
||||||
|
if (hasQuitPassword)
|
||||||
|
{
|
||||||
|
IntegrityModule?.ClearSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplicationMonitor_ExplorerStarted()
|
private void ApplicationMonitor_ExplorerStarted()
|
||||||
{
|
{
|
||||||
logger.Info("Trying to terminate Windows explorer...");
|
logger.Info("Trying to terminate Windows explorer...");
|
||||||
|
|
|
@ -19,7 +19,6 @@ using SafeExamBrowser.Communication.Contracts;
|
||||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
|
||||||
using SafeExamBrowser.Configuration.Cryptography;
|
using SafeExamBrowser.Configuration.Cryptography;
|
||||||
using SafeExamBrowser.Configuration.Integrity;
|
using SafeExamBrowser.Configuration.Integrity;
|
||||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||||
|
@ -71,7 +70,6 @@ namespace SafeExamBrowser.Client
|
||||||
private UserInterfaceMode uiMode;
|
private UserInterfaceMode uiMode;
|
||||||
|
|
||||||
private IActionCenter actionCenter;
|
private IActionCenter actionCenter;
|
||||||
private IIntegrityModule integrityModule;
|
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IMessageBox messageBox;
|
private IMessageBox messageBox;
|
||||||
private INativeMethods nativeMethods;
|
private INativeMethods nativeMethods;
|
||||||
|
@ -98,7 +96,6 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
actionCenter = uiFactory.CreateActionCenter();
|
actionCenter = uiFactory.CreateActionCenter();
|
||||||
context = new ClientContext();
|
context = new ClientContext();
|
||||||
integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule)));
|
|
||||||
messageBox = BuildMessageBox();
|
messageBox = BuildMessageBox();
|
||||||
nativeMethods = new NativeMethods();
|
nativeMethods = new NativeMethods();
|
||||||
networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods);
|
networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods);
|
||||||
|
@ -125,6 +122,7 @@ namespace SafeExamBrowser.Client
|
||||||
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
|
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
|
||||||
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
|
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
|
||||||
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
|
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
|
||||||
|
operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
|
||||||
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
|
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
|
||||||
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
|
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
|
||||||
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
|
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||||
|
@ -148,7 +146,6 @@ namespace SafeExamBrowser.Client
|
||||||
explorerShell,
|
explorerShell,
|
||||||
fileSystemDialog,
|
fileSystemDialog,
|
||||||
hashAlgorithm,
|
hashAlgorithm,
|
||||||
integrityModule,
|
|
||||||
logger,
|
logger,
|
||||||
messageBox,
|
messageBox,
|
||||||
sequence,
|
sequence,
|
||||||
|
@ -223,7 +220,7 @@ namespace SafeExamBrowser.Client
|
||||||
private IOperation BuildBrowserOperation()
|
private IOperation BuildBrowserOperation()
|
||||||
{
|
{
|
||||||
var fileSystemDialog = BuildFileSystemDialog();
|
var fileSystemDialog = BuildFileSystemDialog();
|
||||||
var keyGenerator = new KeyGenerator(context.AppConfig, integrityModule, ModuleLogger(nameof(KeyGenerator)), context.Settings);
|
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)), context.Settings);
|
||||||
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
||||||
var browser = new BrowserApplication(
|
var browser = new BrowserApplication(
|
||||||
context.AppConfig,
|
context.AppConfig,
|
||||||
|
@ -256,6 +253,11 @@ namespace SafeExamBrowser.Client
|
||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BuildIntegrityModule()
|
||||||
|
{
|
||||||
|
context.IntegrityModule = new IntegrityModule(context.AppConfig, ModuleLogger(nameof(IntegrityModule)));
|
||||||
|
}
|
||||||
|
|
||||||
private IOperation BuildKeyboardInterceptorOperation()
|
private IOperation BuildKeyboardInterceptorOperation()
|
||||||
{
|
{
|
||||||
var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);
|
var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);
|
||||||
|
|
|
@ -186,6 +186,11 @@ namespace SafeExamBrowser.Configuration.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ServiceLogFilePath { get; set; }
|
public string ServiceLogFilePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The file path under which the session cache is to be stored.
|
||||||
|
/// </summary>
|
||||||
|
public string SessionCacheFilePath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The directory to be used for temporary application data.
|
/// The directory to be used for temporary application data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -13,6 +13,16 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IIntegrityModule
|
public interface IIntegrityModule
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Caches the specified session for later integrity verification.
|
||||||
|
/// </summary>
|
||||||
|
void CacheSession(string configurationKey, string startUrl);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the specified session from the integrity verification cache.
|
||||||
|
/// </summary>
|
||||||
|
void ClearSession(string configurationKey, string startUrl);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to calculate the browser exam key.
|
/// Attempts to calculate the browser exam key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,5 +32,10 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity
|
||||||
/// Attempts to verify the code signature.
|
/// Attempts to verify the code signature.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryVerifyCodeSignature(out bool isValid);
|
bool TryVerifyCodeSignature(out bool isValid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to verify the integrity for the specified session.
|
||||||
|
/// </summary>
|
||||||
|
bool TryVerifySessionIntegrity(string configurationKey, string startUrl, out bool isValid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
var appDataLocalFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser));
|
var appDataLocalFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser));
|
||||||
var appDataRoamingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser));
|
var appDataRoamingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser));
|
||||||
var programDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
|
var programDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
|
||||||
|
var temporaryFolder = Path.Combine(appDataLocalFolder, "Temp");
|
||||||
var startTime = DateTime.Now;
|
var startTime = DateTime.Now;
|
||||||
var logFolder = Path.Combine(appDataLocalFolder, "Logs");
|
var logFolder = Path.Combine(appDataLocalFolder, "Logs");
|
||||||
var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
|
var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
|
||||||
|
@ -74,7 +75,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
appConfig.ServiceAddress = $"{AppConfig.BASE_ADDRESS}/service";
|
appConfig.ServiceAddress = $"{AppConfig.BASE_ADDRESS}/service";
|
||||||
appConfig.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}";
|
appConfig.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}";
|
||||||
appConfig.ServiceLogFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Service.log");
|
appConfig.ServiceLogFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Service.log");
|
||||||
appConfig.TemporaryDirectory = Path.Combine(appDataLocalFolder, "Temp");
|
appConfig.SessionCacheFilePath = Path.Combine(temporaryFolder, "cache.bin");
|
||||||
|
appConfig.TemporaryDirectory = temporaryFolder;
|
||||||
|
|
||||||
return appConfig;
|
return appConfig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
|
||||||
|
@ -15,20 +20,60 @@ namespace SafeExamBrowser.Configuration.Integrity
|
||||||
{
|
{
|
||||||
public class IntegrityModule : IIntegrityModule
|
public class IntegrityModule : IIntegrityModule
|
||||||
{
|
{
|
||||||
const string DLL_NAME =
|
private const string DLL_NAME =
|
||||||
#if X86
|
#if X86
|
||||||
"seb_x86.dll";
|
"seb_x86.dll";
|
||||||
#else
|
#else
|
||||||
"seb_x64.dll";
|
"seb_x64.dll";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private static readonly byte[] SESSION_DATA_IV =
|
||||||
|
{
|
||||||
|
0x12, 0x07, 0x14, 0x02, 0x03, 0x10, 0x14, 0x18,
|
||||||
|
0x11, 0x01, 0x04, 0x15, 0x06, 0x16, 0x05, 0x12
|
||||||
|
};
|
||||||
|
private static readonly byte[] SESSION_DATA_KEY =
|
||||||
|
{
|
||||||
|
0x01, 0x04, 0x07, 0x08, 0x09, 0x10, 0x13, 0x06,
|
||||||
|
0x11, 0x14, 0x15, 0x16, 0x05, 0x03, 0x13, 0x06,
|
||||||
|
0x01, 0x04, 0x02, 0x03, 0x14, 0x15, 0x07, 0x08,
|
||||||
|
0x11, 0x12, 0x16, 0x05, 0x09, 0x10, 0x12, 0x02
|
||||||
|
};
|
||||||
|
private static readonly string SESSION_DATA_SEPARATOR = "<@|--separator--|@>";
|
||||||
|
|
||||||
|
private readonly AppConfig appConfig;
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
public IntegrityModule(ILogger logger)
|
public IntegrityModule(AppConfig appConfig, ILogger logger)
|
||||||
{
|
{
|
||||||
|
this.appConfig = appConfig;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CacheSession(string configurationKey, string startUrl)
|
||||||
|
{
|
||||||
|
if (TryReadSessionCache(out var sessions) && TryWriteSessionCache(sessions.Append((configurationKey, startUrl))))
|
||||||
|
{
|
||||||
|
logger.Debug("Successfully cached session.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to cache session!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearSession(string configurationKey, string startUrl)
|
||||||
|
{
|
||||||
|
if (TryReadSessionCache(out var sessions) && TryWriteSessionCache(sessions.Where(s => s.configurationKey != configurationKey && s.startUrl != startUrl)))
|
||||||
|
{
|
||||||
|
logger.Debug("Successfully cleared session.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to clear session!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryCalculateBrowserExamKey(string configurationKey, string salt, out string browserExamKey)
|
public bool TryCalculateBrowserExamKey(string configurationKey, string salt, out string browserExamKey)
|
||||||
{
|
{
|
||||||
browserExamKey = default;
|
browserExamKey = default;
|
||||||
|
@ -72,6 +117,103 @@ namespace SafeExamBrowser.Configuration.Integrity
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryVerifySessionIntegrity(string configurationKey, string startUrl, out bool isValid)
|
||||||
|
{
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
isValid = false;
|
||||||
|
|
||||||
|
if (TryReadSessionCache(out var sessions))
|
||||||
|
{
|
||||||
|
isValid = sessions.All(s => s.configurationKey != configurationKey && s.startUrl != startUrl);
|
||||||
|
success = true;
|
||||||
|
logger.Debug($"Successfully verified session integrity, session is {(isValid ? "valid." : "compromised!")}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to verify session integrity!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryReadSessionCache(out IList<(string configurationKey, string startUrl)> sessions)
|
||||||
|
{
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
sessions = new List<(string configurationKey, string startUrl)>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(appConfig.SessionCacheFilePath))
|
||||||
|
{
|
||||||
|
using (var file = new FileStream(appConfig.SessionCacheFilePath, FileMode.Open))
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
using (var stream = new CryptoStream(file, aes.CreateDecryptor(SESSION_DATA_KEY, SESSION_DATA_IV), CryptoStreamMode.Read))
|
||||||
|
using (var reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
var line = reader.ReadLine();
|
||||||
|
|
||||||
|
if (line != default)
|
||||||
|
{
|
||||||
|
var session = line.Split(new string[] { SESSION_DATA_SEPARATOR }, StringSplitOptions.None);
|
||||||
|
var configurationKey = session[0];
|
||||||
|
var startUrl = session[1];
|
||||||
|
|
||||||
|
sessions.Add((configurationKey, startUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to read session cache!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryWriteSessionCache(IEnumerable<(string configurationKey, string startUrl)> sessions)
|
||||||
|
{
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (sessions.Any())
|
||||||
|
{
|
||||||
|
using (var file = new FileStream(appConfig.SessionCacheFilePath, FileMode.Create))
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = SESSION_DATA_KEY;
|
||||||
|
aes.IV = SESSION_DATA_IV;
|
||||||
|
|
||||||
|
using (var stream = new CryptoStream(file, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
||||||
|
using (var writer = new StreamWriter(stream))
|
||||||
|
{
|
||||||
|
foreach (var (configurationKey, startUrl) in sessions)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{configurationKey}{SESSION_DATA_SEPARATOR}{startUrl}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
File.Delete(appConfig.SessionCacheFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to write session cache!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
|
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
|
||||||
[return: MarshalAs(UnmanagedType.BStr)]
|
[return: MarshalAs(UnmanagedType.BStr)]
|
||||||
private static extern string CalculateBrowserExamKey(string configurationKey, string salt);
|
private static extern string CalculateBrowserExamKey(string configurationKey, string salt);
|
||||||
|
|
|
@ -60,13 +60,14 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
FileSystemDialog_Select,
|
FileSystemDialog_Select,
|
||||||
FileSystemDialog_Title,
|
FileSystemDialog_Title,
|
||||||
FolderDialog_ApplicationLocation,
|
FolderDialog_ApplicationLocation,
|
||||||
|
LockScreen_ApplicationIntegrityMessage,
|
||||||
LockScreen_ApplicationsAllowOption,
|
LockScreen_ApplicationsAllowOption,
|
||||||
LockScreen_ApplicationsMessage,
|
LockScreen_ApplicationsMessage,
|
||||||
LockScreen_ApplicationsTerminateOption,
|
LockScreen_ApplicationsTerminateOption,
|
||||||
LockScreen_DisplayConfigurationContinueOption,
|
LockScreen_DisplayConfigurationContinueOption,
|
||||||
LockScreen_DisplayConfigurationTerminateOption,
|
LockScreen_DisplayConfigurationTerminateOption,
|
||||||
LockScreen_DisplayConfigurationMessage,
|
LockScreen_DisplayConfigurationMessage,
|
||||||
LockScreen_IntegrityMessage,
|
LockScreen_SessionIntegrityMessage,
|
||||||
LockScreen_Title,
|
LockScreen_Title,
|
||||||
LockScreen_UnlockButton,
|
LockScreen_UnlockButton,
|
||||||
LockScreen_UserSessionContinueOption,
|
LockScreen_UserSessionContinueOption,
|
||||||
|
|
|
@ -138,6 +138,9 @@
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt.
|
Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt.
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="LockScreen_ApplicationIntegrityMessage">
|
||||||
|
Sie verwenden eine inoffizielle SEB-Version! Bitte stellen Sie sicher, dass Sie einen offiziellen Safe Exam Browser verwenden. Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren.
|
||||||
|
</Entry>
|
||||||
<Entry key="LockScreen_ApplicationsAllowOption">
|
<Entry key="LockScreen_ApplicationsAllowOption">
|
||||||
Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung!
|
Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung!
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -156,8 +159,8 @@
|
||||||
<Entry key="LockScreen_DisplayConfigurationMessage">
|
<Entry key="LockScreen_DisplayConfigurationMessage">
|
||||||
Eine verbotene Bildschirm-Konfiguration wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
|
Eine verbotene Bildschirm-Konfiguration wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_IntegrityMessage">
|
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||||
Sie verwenden eine inoffizielle SEB-Version! Bitte stellen Sie sicher, dass Sie einen offiziellen Safe Exam Browser verwenden. Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren.
|
Die letzte Sitzung mit der momentan aktiven Konfiguration oder Start-URL wurde nicht ordnungsgemäss beendet! Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Title">
|
<Entry key="LockScreen_Title">
|
||||||
SEB GESPERRT
|
SEB GESPERRT
|
||||||
|
|
|
@ -138,6 +138,9 @@
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
|
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="LockScreen_ApplicationIntegrityMessage">
|
||||||
|
You are using an unofficial SEB version! Please make sure to use an official Safe Exam Browser. In order to unlock SEB, please enter the correct unlock password.
|
||||||
|
</Entry>
|
||||||
<Entry key="LockScreen_ApplicationsAllowOption">
|
<Entry key="LockScreen_ApplicationsAllowOption">
|
||||||
Temporarily allow the blacklisted applications. This applies only to the currently running instances and session!
|
Temporarily allow the blacklisted applications. This applies only to the currently running instances and session!
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -156,8 +159,8 @@
|
||||||
<Entry key="LockScreen_DisplayConfigurationMessage">
|
<Entry key="LockScreen_DisplayConfigurationMessage">
|
||||||
A prohibited display configuration has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password.
|
A prohibited display configuration has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_IntegrityMessage">
|
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||||
You are using an unofficial SEB version! Please make sure to use an official Safe Exam Browser. In order to unlock SEB, please enter the correct unlock password.
|
The last session with the currently active configuration or start URL was not terminated properly! Please enter the correct password to unlock SEB.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Title">
|
<Entry key="LockScreen_Title">
|
||||||
SEB LOCKED
|
SEB LOCKED
|
||||||
|
|
|
@ -138,6 +138,9 @@
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
L'application "%%NAME%%" n'a pas pu être trouvée sur le système! Veuillez localiser le dossier contenant l'exécutable principal "%%EXECUTABLE%%".
|
L'application "%%NAME%%" n'a pas pu être trouvée sur le système! Veuillez localiser le dossier contenant l'exécutable principal "%%EXECUTABLE%%".
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="LockScreen_ApplicationIntegrityMessage">
|
||||||
|
Vous utilisez une version non officielle de SEB! Assurez-vous d'utiliser un Safe Exam Browser officiel. Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct.
|
||||||
|
</Entry>
|
||||||
<Entry key="LockScreen_ApplicationsAllowOption">
|
<Entry key="LockScreen_ApplicationsAllowOption">
|
||||||
Autoriser temporairement les applications figurant sur la liste noire. Cela ne s'applique qu'aux instances et à la session en cours!
|
Autoriser temporairement les applications figurant sur la liste noire. Cela ne s'applique qu'aux instances et à la session en cours!
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -156,8 +159,8 @@
|
||||||
<Entry key="LockScreen_DisplayConfigurationMessage">
|
<Entry key="LockScreen_DisplayConfigurationMessage">
|
||||||
Une configuration d'affichage interdite a été détectée. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct.
|
Une configuration d'affichage interdite a été détectée. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_IntegrityMessage">
|
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||||
Vous utilisez une version non officielle de SEB! Assurez-vous d'utiliser un Safe Exam Browser officiel. Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct.
|
La dernière session avec la configuration ou l'URL de démarrage actuellement active n'a pas été terminée correctement! Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Title">
|
<Entry key="LockScreen_Title">
|
||||||
SEB VEROUILLE
|
SEB VEROUILLE
|
||||||
|
|
|
@ -138,6 +138,9 @@
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
L'applicazione "%%NAME%%" non è stata trovata nel sistema! Individua la cartella contenente l'eseguibile principale "%%EXECUTABLE%%".
|
L'applicazione "%%NAME%%" non è stata trovata nel sistema! Individua la cartella contenente l'eseguibile principale "%%EXECUTABLE%%".
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="LockScreen_ApplicationIntegrityMessage">
|
||||||
|
Stai usando una versione SEB non ufficiale! Assicurati di utilizzare un Safe Exam Browser ufficiale. Per sbloccare SEB, inserisci la password di sblocco corretta.
|
||||||
|
</Entry>
|
||||||
<Entry key="LockScreen_ApplicationsAllowOption">
|
<Entry key="LockScreen_ApplicationsAllowOption">
|
||||||
Consenti temporaneamente le applicazioni nella lista nera. Ciò si applica solo alle istanze e alla sessione attualmente in esecuzione!
|
Consenti temporaneamente le applicazioni nella lista nera. Ciò si applica solo alle istanze e alla sessione attualmente in esecuzione!
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -156,8 +159,8 @@
|
||||||
<Entry key="LockScreen_DisplayConfigurationMessage">
|
<Entry key="LockScreen_DisplayConfigurationMessage">
|
||||||
È stata rilevata una configurazione di visualizzazione vietata. Per sbloccare SEB, seleziona una delle opzioni disponibili e inserisci la password di sblocco corretta.
|
È stata rilevata una configurazione di visualizzazione vietata. Per sbloccare SEB, seleziona una delle opzioni disponibili e inserisci la password di sblocco corretta.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_IntegrityMessage">
|
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||||
Stai usando una versione SEB non ufficiale! Assicurati di utilizzare un Safe Exam Browser ufficiale. Per sbloccare SEB, inserisci la password di sblocco corretta.
|
L'ultima sessione con la configurazione o l'URL di avvio attualmente attiva non è stata terminata correttamente! Per sbloccare SEB, inserisci la password di sblocco corretta.
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Title">
|
<Entry key="LockScreen_Title">
|
||||||
SEB BLOCCATO
|
SEB BLOCCATO
|
||||||
|
|
|
@ -123,13 +123,16 @@
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
无法在系统中找到应用程序 "%%NAME%%" 。请查找包含主可执行文件的文件夹"%%EXECUTABLE%%"。
|
无法在系统中找到应用程序 "%%NAME%%" 。请查找包含主可执行文件的文件夹"%%EXECUTABLE%%"。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_AllowOption">
|
<Entry key="LockScreen_ApplicationIntegrityMessage">
|
||||||
|
您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB,请输入正确的解锁密码。
|
||||||
|
</Entry>
|
||||||
|
<Entry key="LockScreen_ApplicationsAllowOption">
|
||||||
暂时允许黑名单上的应用程序。这仅适用于当前正在运行的实例和会话。
|
暂时允许黑名单上的应用程序。这仅适用于当前正在运行的实例和会话。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Message">
|
<Entry key="LockScreen_ApplicationsMessage">
|
||||||
下面列出的黑名单应用程序已经启动,无法自动终止。请选择一个可用的选项并输入正确的密码来解锁防作弊考试专用浏览器。
|
下面列出的黑名单应用程序已经启动,无法自动终止。请选择一个可用的选项并输入正确的密码来解锁防作弊考试专用浏览器。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_TerminateOption">
|
<Entry key="LockScreen_ApplicationsTerminateOption">
|
||||||
强制关闭防作弊考试专用浏览器。警告:将无法保存数据或执行任何进一步的操作,关闭操作将立即启动。
|
强制关闭防作弊考试专用浏览器。警告:将无法保存数据或执行任何进一步的操作,关闭操作将立即启动。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_DisplayConfigurationContinueOption">
|
<Entry key="LockScreen_DisplayConfigurationContinueOption">
|
||||||
|
@ -141,8 +144,8 @@
|
||||||
<Entry key="LockScreen_DisplayConfigurationMessage">
|
<Entry key="LockScreen_DisplayConfigurationMessage">
|
||||||
检测到禁止的显示配置。 要解锁 SEB,请选择可用选项之一并输入正确的解锁密码。
|
检测到禁止的显示配置。 要解锁 SEB,请选择可用选项之一并输入正确的解锁密码。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_IntegrityMessage">
|
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||||
您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB,请输入正确的解锁密码。
|
当前激活的配置或启动URL的最后一个会话没有被正确终止! 请输入正确的密码以解锁SEB。
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="LockScreen_Title">
|
<Entry key="LockScreen_Title">
|
||||||
防作弊考试专用浏览器已锁定
|
防作弊考试专用浏览器已锁定
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
|
var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
|
||||||
|
|
||||||
var args = Environment.GetCommandLineArgs();
|
var args = Environment.GetCommandLineArgs();
|
||||||
var integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule)));
|
var integrityModule = new IntegrityModule(appConfig, ModuleLogger(nameof(IntegrityModule)));
|
||||||
var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
|
var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
|
||||||
var desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor)));
|
var desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor)));
|
||||||
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||||
|
|
Loading…
Reference in a new issue