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 | ||||||
| 				foreach (string configId in hardwareConfKey.GetSubKeyNames()) | 			 *	- {computerId=uuid}: {computerSummary=hardwareInfo} | ||||||
| 				{ | 			 *	 | ||||||
| 					var configKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($"SYSTEM\\HardwareConfig\\{configId}"); | 			 */ | ||||||
|  | 			IEnumerable<string> hardwareConfigSubkeys; | ||||||
|  | 			if (!registry.TryGetSubKeys("HKLM\\SYSTEM\\HardwareConfig", out hardwareConfigSubkeys)) | ||||||
|  | 				return false; | ||||||
| 
 | 
 | ||||||
| 					if (configKey == null) | 			foreach (string configId in hardwareConfigSubkeys) | ||||||
| 			{ | 			{ | ||||||
|  | 				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; | 					continue; | ||||||
| 					} |  | ||||||
| 
 | 
 | ||||||
| 				// reconstruct the systemInfo.biosInfo string | 				// reconstruct the systemInfo.biosInfo string | ||||||
| 					var biosInfo = (string) configKey.GetValue("BIOSVendor") + " " + (string) configKey.GetValue("BIOSVersion"); | 				string biosInfo = $"{(string) biosVendor} {(string) biosVersion}"; | ||||||
| 					var manufacturer = (string) configKey.GetValue("SystemManufacturer"); |  | ||||||
| 					var model = (string) configKey.GetValue("SystemProductName"); |  | ||||||
| 
 | 
 | ||||||
| 					isVirtualMachine |= IsVirtualSystemInfo(biosInfo, manufacturer, model); | 				isVirtualMachine |= IsVirtualSystemInfo(biosInfo, (string) systemManufacturer, (string) systemProductName); | ||||||
| 
 | 
 | ||||||
| 				// hardware information of profile throughout installation etc.  | 				// hardware information of profile throughout installation etc.  | ||||||
| 					var computerIdsKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($"SYSTEM\\HardwareConfig\\{configId}\\ComputerIds"); | 				IEnumerable<string> computerIds; | ||||||
|  | 				if (!registry.TryGetSubKeys($"HKLM\\SYSTEM\\HardwareConfig\\{configId}\\ComputerIds", out computerIds)) | ||||||
|  | 					return false; | ||||||
| 
 | 
 | ||||||
| 					if (computerIdsKey == null) | 				foreach (var computerId in computerIds) | ||||||
| 					{ |  | ||||||
| 						continue; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					foreach (var computerId in computerIdsKey.GetSubKeyNames()) |  | ||||||
| 				{ | 				{ | ||||||
|  | 					logger.Info($"computerId: {computerId}"); | ||||||
| 					// e.g. manufacturer&version&sku&... | 					// e.g. manufacturer&version&sku&... | ||||||
| 						var computerSummary = (string) computerIdsKey.GetValue(computerId); | 					object computerSummary; // = (string) computerIds.GetValue(computerId); | ||||||
| 						isVirtualMachine |= IsVirtualSystemInfo(computerSummary, computerSummary, computerSummary); | 
 | ||||||
| 					} | 					if (!registry.TryRead($"HKLM\\SYSTEM\\HardwareConfig\\{configId}\\ComputerIds", computerId, out computerSummary)) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
|  | 					isVirtualMachine |= IsVirtualSystemInfo((string) computerSummary, (string) systemManufacturer, (string) systemProductName); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// 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…
	
	Add table
		
		Reference in a new issue
	
	 Notselwyn
						Notselwyn