SEBWIN-615: Implemented session integrity verification.

This commit is contained in:
Damian Büchel 2022-11-24 14:50:25 +01:00
parent 04bebdffb2
commit 1c42434b9a
15 changed files with 264 additions and 29 deletions

View file

@ -76,6 +76,8 @@ namespace SafeExamBrowser.Client.UnitTests
[TestInitialize]
public void Initialize()
{
var valid = true;
appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>();
applicationMonitor = new Mock<IApplicationMonitor>();
@ -101,6 +103,7 @@ namespace SafeExamBrowser.Client.UnitTests
text = new Mock<IText>();
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);
runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true));
uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object);
@ -113,7 +116,6 @@ namespace SafeExamBrowser.Client.UnitTests
explorerShell.Object,
fileSystemDialog.Object,
hashAlgorithm.Object,
integrityModule.Object,
logger.Object,
messageBox.Object,
operationSequence.Object,
@ -128,6 +130,7 @@ namespace SafeExamBrowser.Client.UnitTests
context.AppConfig = appConfig;
context.Browser = browser.Object;
context.ClientHost = clientHost.Object;
context.IntegrityModule = integrityModule.Object;
context.Server = server.Object;
context.SessionId = sessionId;
context.Settings = settings;
@ -1062,6 +1065,7 @@ namespace SafeExamBrowser.Client.UnitTests
context.AppConfig = null;
context.Browser = null;
context.ClientHost = null;
context.IntegrityModule = null;
context.Server = null;
context.Settings = null;

View file

@ -12,6 +12,7 @@ using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -48,6 +49,11 @@ namespace SafeExamBrowser.Client
/// </summary>
internal IClientHost ClientHost { get; set; }
/// <summary>
/// The integrity module.
/// </summary>
internal IIntegrityModule IntegrityModule { get; set; }
/// <summary>
/// The server proxy to be used if the current session mode is <see cref="SessionMode.Server"/>.
/// </summary>

View file

@ -12,6 +12,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events;
@ -51,7 +52,6 @@ namespace SafeExamBrowser.Client
private readonly IExplorerShell explorerShell;
private readonly IFileSystemDialog fileSystemDialog;
private readonly IHashAlgorithm hashAlgorithm;
private readonly IIntegrityModule integrityModule;
private readonly ILogger logger;
private readonly IMessageBox messageBox;
private readonly IOperationSequence operations;
@ -65,6 +65,7 @@ namespace SafeExamBrowser.Client
private IBrowserApplication Browser => context.Browser;
private IClientHost ClientHost => context.ClientHost;
private IIntegrityModule IntegrityModule => context.IntegrityModule;
private IServerProxy Server => context.Server;
private AppSettings Settings => context.Settings;
@ -79,7 +80,6 @@ namespace SafeExamBrowser.Client
IExplorerShell explorerShell,
IFileSystemDialog fileSystemDialog,
IHashAlgorithm hashAlgorithm,
IIntegrityModule integrityModule,
ILogger logger,
IMessageBox messageBox,
IOperationSequence operations,
@ -98,7 +98,6 @@ namespace SafeExamBrowser.Client
this.explorerShell = explorerShell;
this.fileSystemDialog = fileSystemDialog;
this.hashAlgorithm = hashAlgorithm;
this.integrityModule = integrityModule;
this.logger = logger;
this.messageBox = messageBox;
this.operations = operations;
@ -137,6 +136,8 @@ namespace SafeExamBrowser.Client
{
logger.Info("Application successfully initialized.");
logger.Log(string.Empty);
VerifySessionIntegrity();
}
else
{
@ -165,6 +166,7 @@ namespace SafeExamBrowser.Client
CloseShell();
DeregisterEvents();
UpdateSessionIntegrity();
var success = operations.TryRevert() == OperationResult.Success;
@ -330,7 +332,7 @@ namespace SafeExamBrowser.Client
{
logger.Info($"Attempting to verify application integrity...");
if (integrityModule.TryVerifyCodeSignature(out var isValid))
if (IntegrityModule.TryVerifyCodeSignature(out var isValid))
{
if (isValid)
{
@ -339,7 +341,7 @@ namespace SafeExamBrowser.Client
else
{
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
@ -351,6 +353,47 @@ namespace SafeExamBrowser.Client
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()
{
logger.Info("Trying to terminate Windows explorer...");

View file

@ -19,7 +19,6 @@ using SafeExamBrowser.Communication.Contracts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Communication.Hosts;
using SafeExamBrowser.Communication.Proxies;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Configuration.Integrity;
using SafeExamBrowser.Core.Contracts.OperationModel;
@ -71,7 +70,6 @@ namespace SafeExamBrowser.Client
private UserInterfaceMode uiMode;
private IActionCenter actionCenter;
private IIntegrityModule integrityModule;
private ILogger logger;
private IMessageBox messageBox;
private INativeMethods nativeMethods;
@ -98,7 +96,6 @@ namespace SafeExamBrowser.Client
actionCenter = uiFactory.CreateActionCenter();
context = new ClientContext();
integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule)));
messageBox = BuildMessageBox();
nativeMethods = new 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 ConfigurationOperation(context, logger, runtimeProxy));
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
@ -148,7 +146,6 @@ namespace SafeExamBrowser.Client
explorerShell,
fileSystemDialog,
hashAlgorithm,
integrityModule,
logger,
messageBox,
sequence,
@ -223,7 +220,7 @@ namespace SafeExamBrowser.Client
private IOperation BuildBrowserOperation()
{
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 browser = new BrowserApplication(
context.AppConfig,
@ -256,6 +253,11 @@ namespace SafeExamBrowser.Client
return operation;
}
private void BuildIntegrityModule()
{
context.IntegrityModule = new IntegrityModule(context.AppConfig, ModuleLogger(nameof(IntegrityModule)));
}
private IOperation BuildKeyboardInterceptorOperation()
{
var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);

View file

@ -186,6 +186,11 @@ namespace SafeExamBrowser.Configuration.Contracts
/// </summary>
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>
/// The directory to be used for temporary application data.
/// </summary>

View file

@ -13,6 +13,16 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity
/// </summary>
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>
/// Attempts to calculate the browser exam key.
/// </summary>
@ -22,5 +32,10 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity
/// Attempts to verify the code signature.
/// </summary>
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);
}
}

View file

@ -45,6 +45,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
var appDataLocalFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), 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 temporaryFolder = Path.Combine(appDataLocalFolder, "Temp");
var startTime = DateTime.Now;
var logFolder = Path.Combine(appDataLocalFolder, "Logs");
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.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}";
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;
}

View file

@ -7,7 +7,12 @@
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.Logging.Contracts;
@ -15,20 +20,60 @@ namespace SafeExamBrowser.Configuration.Integrity
{
public class IntegrityModule : IIntegrityModule
{
const string DLL_NAME =
private const string DLL_NAME =
#if X86
"seb_x86.dll";
#else
"seb_x64.dll";
#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;
public IntegrityModule(ILogger logger)
public IntegrityModule(AppConfig appConfig, ILogger logger)
{
this.appConfig = appConfig;
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)
{
browserExamKey = default;
@ -72,6 +117,103 @@ namespace SafeExamBrowser.Configuration.Integrity
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)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string CalculateBrowserExamKey(string configurationKey, string salt);

View file

@ -60,13 +60,14 @@ namespace SafeExamBrowser.I18n.Contracts
FileSystemDialog_Select,
FileSystemDialog_Title,
FolderDialog_ApplicationLocation,
LockScreen_ApplicationIntegrityMessage,
LockScreen_ApplicationsAllowOption,
LockScreen_ApplicationsMessage,
LockScreen_ApplicationsTerminateOption,
LockScreen_DisplayConfigurationContinueOption,
LockScreen_DisplayConfigurationTerminateOption,
LockScreen_DisplayConfigurationMessage,
LockScreen_IntegrityMessage,
LockScreen_SessionIntegrityMessage,
LockScreen_Title,
LockScreen_UnlockButton,
LockScreen_UserSessionContinueOption,

View file

@ -138,6 +138,9 @@
<Entry key="FolderDialog_ApplicationLocation">
Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt.
</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">
Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung!
</Entry>
@ -156,8 +159,8 @@
<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.
</Entry>
<Entry key="LockScreen_IntegrityMessage">
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 key="LockScreen_SessionIntegrityMessage">
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 key="LockScreen_Title">
SEB GESPERRT

View file

@ -138,6 +138,9 @@
<Entry key="FolderDialog_ApplicationLocation">
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
</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">
Temporarily allow the blacklisted applications. This applies only to the currently running instances and session!
</Entry>
@ -156,8 +159,8 @@
<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.
</Entry>
<Entry key="LockScreen_IntegrityMessage">
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 key="LockScreen_SessionIntegrityMessage">
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 key="LockScreen_Title">
SEB LOCKED

View file

@ -138,6 +138,9 @@
<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%%".
</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">
Autoriser temporairement les applications figurant sur la liste noire. Cela ne s'applique qu'aux instances et à la session en cours!
</Entry>
@ -156,8 +159,8 @@
<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.
</Entry>
<Entry key="LockScreen_IntegrityMessage">
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 key="LockScreen_SessionIntegrityMessage">
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 key="LockScreen_Title">
SEB VEROUILLE

View file

@ -138,6 +138,9 @@
<Entry key="FolderDialog_ApplicationLocation">
L'applicazione "%%NAME%%" non è stata trovata nel sistema! Individua la cartella contenente l'eseguibile principale "%%EXECUTABLE%%".
</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">
Consenti temporaneamente le applicazioni nella lista nera. Ciò si applica solo alle istanze e alla sessione attualmente in esecuzione!
</Entry>
@ -156,8 +159,8 @@
<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.
</Entry>
<Entry key="LockScreen_IntegrityMessage">
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 key="LockScreen_SessionIntegrityMessage">
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 key="LockScreen_Title">
SEB BLOCCATO

View file

@ -123,13 +123,16 @@
<Entry key="FolderDialog_ApplicationLocation">
无法在系统中找到应用程序 "%%NAME%%" 。请查找包含主可执行文件的文件夹"%%EXECUTABLE%%"。
</Entry>
<Entry key="LockScreen_AllowOption">
<Entry key="LockScreen_ApplicationIntegrityMessage">
您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB请输入正确的解锁密码。
</Entry>
<Entry key="LockScreen_ApplicationsAllowOption">
暂时允许黑名单上的应用程序。这仅适用于当前正在运行的实例和会话。
</Entry>
<Entry key="LockScreen_Message">
<Entry key="LockScreen_ApplicationsMessage">
下面列出的黑名单应用程序已经启动,无法自动终止。请选择一个可用的选项并输入正确的密码来解锁防作弊考试专用浏览器。
</Entry>
<Entry key="LockScreen_TerminateOption">
<Entry key="LockScreen_ApplicationsTerminateOption">
强制关闭防作弊考试专用浏览器。警告:将无法保存数据或执行任何进一步的操作,关闭操作将立即启动。
</Entry>
<Entry key="LockScreen_DisplayConfigurationContinueOption">
@ -141,8 +144,8 @@
<Entry key="LockScreen_DisplayConfigurationMessage">
检测到禁止的显示配置。 要解锁 SEB请选择可用选项之一并输入正确的解锁密码。
</Entry>
<Entry key="LockScreen_IntegrityMessage">
您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB请输入正确的解锁密码
<Entry key="LockScreen_SessionIntegrityMessage">
当前激活的配置或启动URL的最后一个会话没有被正确终止! 请输入正确的密码以解锁SEB
</Entry>
<Entry key="LockScreen_Title">
防作弊考试专用浏览器已锁定

View file

@ -64,7 +64,7 @@ namespace SafeExamBrowser.Runtime
var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
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 desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor)));
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);