feat: extended IRegistry interface (no breaking changes). VM detection is broken regardless
This commit is contained in:
parent
e4e0f7c16b
commit
7fc31f6e90
4 changed files with 208 additions and 32 deletions
|
@ -83,7 +83,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)), Interlocutor.Runtime);
|
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)), Interlocutor.Runtime);
|
||||||
var sessionContext = new SessionContext();
|
var sessionContext = new SessionContext();
|
||||||
var splashScreen = uiFactory.CreateSplashScreen(appConfig);
|
var splashScreen = uiFactory.CreateSplashScreen(appConfig);
|
||||||
var vmDetector = new VirtualMachineDetector(ModuleLogger(nameof(VirtualMachineDetector)), systemInfo);
|
var vmDetector = new VirtualMachineDetector(ModuleLogger(nameof(VirtualMachineDetector)), registry, systemInfo);
|
||||||
|
|
||||||
var bootstrapOperations = new Queue<IOperation>();
|
var bootstrapOperations = new Queue<IOperation>();
|
||||||
var sessionOperations = new Queue<IRepeatableOperation>();
|
var sessionOperations = new Queue<IRepeatableOperation>();
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
|
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SafeExamBrowser.SystemComponents.Contracts.Registry
|
namespace SafeExamBrowser.SystemComponents.Contracts.Registry
|
||||||
{
|
{
|
||||||
|
@ -34,5 +35,15 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Registry
|
||||||
/// Attempts to read the value of the given name under the specified registry key.
|
/// Attempts to read the value of the given name under the specified registry key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryRead(string key, string name, out object value);
|
bool TryRead(string key, string name, out object value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to read the value names of the given registry key.
|
||||||
|
/// </summary>
|
||||||
|
bool TryGetNames(string key, out IEnumerable<string> names);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to read the subkey names of the given registry key.
|
||||||
|
/// </summary>
|
||||||
|
bool TryGetSubKeys(string key, out IEnumerable<string> subKeys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using Microsoft.Win32;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
|
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
|
||||||
|
@ -86,6 +89,38 @@ namespace SafeExamBrowser.SystemComponents.Registry
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetNames(string key, out IEnumerable<string> names)
|
||||||
|
{
|
||||||
|
names = null;
|
||||||
|
|
||||||
|
RegistryKey keyObj;
|
||||||
|
if (!TryOpenKey(key, out keyObj))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (keyObj)
|
||||||
|
{
|
||||||
|
names = keyObj.GetValueNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetSubKeys(string key, out IEnumerable<string> subKeys)
|
||||||
|
{
|
||||||
|
subKeys = null;
|
||||||
|
|
||||||
|
RegistryKey keyObj;
|
||||||
|
if (!TryOpenKey(key, out keyObj))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (keyObj)
|
||||||
|
{
|
||||||
|
subKeys = keyObj.GetSubKeyNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
foreach (var item in values)
|
foreach (var item in values)
|
||||||
|
@ -104,5 +139,104 @@ namespace SafeExamBrowser.SystemComponents.Registry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a keyName and returns the basekey for it.
|
||||||
|
/// It will also store the subkey name in the out parameter.
|
||||||
|
/// If the keyName is not valid, we will return false.
|
||||||
|
/// Does not raise Exceptions.
|
||||||
|
/// Supports shortcuts.
|
||||||
|
/// </summary>
|
||||||
|
// yoinked (and partially modified to follow SEB conventions) private Win32 function: https://stackoverflow.com/a/58547945
|
||||||
|
private bool GetBaseKeyFromKeyName(string keyName, out RegistryKey hiveKey, out string subKeyName)
|
||||||
|
{
|
||||||
|
hiveKey = null;
|
||||||
|
subKeyName = null;
|
||||||
|
|
||||||
|
string basekeyName;
|
||||||
|
int i = keyName.IndexOf('\\');
|
||||||
|
if (i != -1)
|
||||||
|
{
|
||||||
|
basekeyName = keyName.Substring(0, i).ToUpper(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add shortcuts as well to be implicit
|
||||||
|
switch (basekeyName)
|
||||||
|
{
|
||||||
|
case "HKEY_CURRENT_USER":
|
||||||
|
case "HKCU":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_LOCAL_MACHINE":
|
||||||
|
case "HKLM":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_CLASSES_ROOT":
|
||||||
|
case "HKCR":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_USERS":
|
||||||
|
case "HKU":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_PERFORMANCE_DATA":
|
||||||
|
case "HKPD":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.PerformanceData, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_CURRENT_CONFIG":
|
||||||
|
case "HKCC":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
case "HKEY_DYN_DATA":
|
||||||
|
case "HKDD":
|
||||||
|
hiveKey = RegistryKey.OpenBaseKey(RegistryHive.DynData, RegistryView.Registry64);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// output is already set to null at the start
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == -1 || i == keyName.Length)
|
||||||
|
{
|
||||||
|
subKeyName = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to open a key and outputs a RegistryKey object. Does not raise Exceptions, but returns false/true.
|
||||||
|
/// </summary>
|
||||||
|
private bool TryOpenKey(string key, out RegistryKey keyObj)
|
||||||
|
{
|
||||||
|
keyObj = null;
|
||||||
|
|
||||||
|
string subHiveKey;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RegistryKey hiveObj;
|
||||||
|
if (!GetBaseKeyFromKeyName(key, out hiveObj, out subHiveKey))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
keyObj = hiveObj.OpenSubKey(subHiveKey);
|
||||||
|
if (keyObj == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to open registry key '{key}'!", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ using System.Linq;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts;
|
using SafeExamBrowser.SystemComponents.Contracts;
|
||||||
|
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace SafeExamBrowser.SystemComponents
|
namespace SafeExamBrowser.SystemComponents
|
||||||
{
|
{
|
||||||
|
@ -31,11 +35,13 @@ namespace SafeExamBrowser.SystemComponents
|
||||||
private static readonly string VIRTUALBOX_MAC_PREFIX = "080027";
|
private static readonly string VIRTUALBOX_MAC_PREFIX = "080027";
|
||||||
|
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
private readonly IRegistry registry;
|
||||||
private readonly ISystemInfo systemInfo;
|
private readonly ISystemInfo systemInfo;
|
||||||
|
|
||||||
public VirtualMachineDetector(ILogger logger, ISystemInfo systemInfo)
|
public VirtualMachineDetector(ILogger logger, IRegistry registry, ISystemInfo systemInfo)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.registry = registry;
|
||||||
this.systemInfo = systemInfo;
|
this.systemInfo = systemInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,45 +103,70 @@ namespace SafeExamBrowser.SystemComponents
|
||||||
{
|
{
|
||||||
var isVirtualMachine = false;
|
var isVirtualMachine = false;
|
||||||
|
|
||||||
// check historic hardware profiles
|
/**
|
||||||
var hardwareConfKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SYSTEM\\HardwareConfig");
|
* check historic hardware profiles
|
||||||
if (hardwareConfKey != null)
|
*
|
||||||
|
* HKLM\SYSTEM\HardwareConfig\{configId=uuid}\ComputerIds
|
||||||
|
* - {computerId=uuid}: {computerSummary=hardwareInfo}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
IEnumerable<string> hardwareConfigSubkeys;
|
||||||
|
if (!registry.TryGetSubKeys("HKLM\\SYSTEM\\HardwareConfig", out hardwareConfigSubkeys))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (string configId in hardwareConfigSubkeys)
|
||||||
{
|
{
|
||||||
foreach (string configId in hardwareConfKey.GetSubKeyNames())
|
logger.Info($"scanning configId: {configId}");
|
||||||
|
var configKey = $"HKEY_LOCAL_MACHINE\\SYSTEM\\HardwareConfig\\{configId}";
|
||||||
|
|
||||||
|
object biosVendor;
|
||||||
|
object biosVersion;
|
||||||
|
object systemManufacturer;
|
||||||
|
object systemProductName;
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
success &= registry.TryRead(configKey, "BIOSVendor", out biosVendor);
|
||||||
|
success &= registry.TryRead(configKey, "BIOSVersion", out biosVersion);
|
||||||
|
success &= registry.TryRead(configKey, "SystemManufacturer", out systemManufacturer);
|
||||||
|
success &= registry.TryRead(configKey, "SystemProductName", out systemProductName);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// reconstruct the systemInfo.biosInfo string
|
||||||
|
string biosInfo = $"{(string) biosVendor} {(string) biosVersion}";
|
||||||
|
|
||||||
|
isVirtualMachine |= IsVirtualSystemInfo(biosInfo, (string) systemManufacturer, (string) systemProductName);
|
||||||
|
|
||||||
|
// hardware information of profile throughout installation etc.
|
||||||
|
IEnumerable<string> computerIds;
|
||||||
|
if (!registry.TryGetSubKeys($"HKLM\\SYSTEM\\HardwareConfig\\{configId}\\ComputerIds", out computerIds))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var computerId in computerIds)
|
||||||
{
|
{
|
||||||
var configKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($"SYSTEM\\HardwareConfig\\{configId}");
|
logger.Info($"computerId: {computerId}");
|
||||||
|
// e.g. manufacturer&version&sku&...
|
||||||
|
object computerSummary; // = (string) computerIds.GetValue(computerId);
|
||||||
|
|
||||||
if (configKey == null)
|
if (!registry.TryRead($"HKLM\\SYSTEM\\HardwareConfig\\{configId}\\ComputerIds", computerId, out computerSummary))
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// reconstruct the systemInfo.biosInfo string
|
isVirtualMachine |= IsVirtualSystemInfo((string) computerSummary, (string) systemManufacturer, (string) systemProductName);
|
||||||
var biosInfo = (string) configKey.GetValue("BIOSVendor") + " " + (string) configKey.GetValue("BIOSVersion");
|
|
||||||
var manufacturer = (string) configKey.GetValue("SystemManufacturer");
|
|
||||||
var model = (string) configKey.GetValue("SystemProductName");
|
|
||||||
|
|
||||||
isVirtualMachine |= IsVirtualSystemInfo(biosInfo, manufacturer, model);
|
|
||||||
|
|
||||||
// hardware information of profile throughout installation etc.
|
|
||||||
var computerIdsKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($"SYSTEM\\HardwareConfig\\{configId}\\ComputerIds");
|
|
||||||
|
|
||||||
if (computerIdsKey == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var computerId in computerIdsKey.GetSubKeyNames())
|
|
||||||
{
|
|
||||||
// e.g. manufacturer&version&sku&...
|
|
||||||
var computerSummary = (string) computerIdsKey.GetValue(computerId);
|
|
||||||
isVirtualMachine |= IsVirtualSystemInfo(computerSummary, computerSummary, computerSummary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check Windows timeline caches for current hardware config
|
// check Windows timeline caches for current hardware config
|
||||||
var deviceCacheKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\TaskFlow\\DeviceCache");
|
/*IEnumerable<string> deviceCacheSubkeys;
|
||||||
|
if (registry.TryGetSubKeys($"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\TaskFlow\\DeviceCache", out deviceCacheSubkeys)
|
||||||
|
{
|
||||||
|
foreach (string deviceCacheKey in deviceCacheSubkeys)
|
||||||
|
{
|
||||||
|
if (registry.TryRead($"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\TaskFlow\\DeviceCache"))*/
|
||||||
|
|
||||||
|
|
||||||
|
var deviceCacheKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey($"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\TaskFlow\\DeviceCache");
|
||||||
var currHostname = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
|
var currHostname = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
|
||||||
|
|
||||||
if (deviceCacheKey != null && currHostname != null)
|
if (deviceCacheKey != null && currHostname != null)
|
||||||
|
|
Loading…
Reference in a new issue