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 sessionContext = new SessionContext(); | ||||
| 			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 sessionOperations = new Queue<IRepeatableOperation>(); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
|  */ | ||||
| 
 | ||||
| using SafeExamBrowser.SystemComponents.Contracts.Registry.Events; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| 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. | ||||
| 		/// </summary> | ||||
| 		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.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Security.Cryptography; | ||||
| using System.Timers; | ||||
| using Microsoft.Win32; | ||||
| using SafeExamBrowser.Logging.Contracts; | ||||
| using SafeExamBrowser.SystemComponents.Contracts.Registry; | ||||
| using SafeExamBrowser.SystemComponents.Contracts.Registry.Events; | ||||
|  | @ -86,6 +89,38 @@ namespace SafeExamBrowser.SystemComponents.Registry | |||
| 			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) | ||||
| 		{ | ||||
| 			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 SafeExamBrowser.Logging.Contracts; | ||||
| using SafeExamBrowser.SystemComponents.Contracts; | ||||
| using SafeExamBrowser.SystemComponents.Contracts.Registry; | ||||
| using Microsoft.Win32; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System; | ||||
| 
 | ||||
| namespace SafeExamBrowser.SystemComponents | ||||
| { | ||||
|  | @ -31,11 +35,13 @@ namespace SafeExamBrowser.SystemComponents | |||
| 		private static readonly string VIRTUALBOX_MAC_PREFIX = "080027"; | ||||
| 
 | ||||
| 		private readonly ILogger logger; | ||||
| 		private readonly IRegistry registry; | ||||
| 		private readonly ISystemInfo systemInfo; | ||||
| 
 | ||||
| 		public VirtualMachineDetector(ILogger logger, ISystemInfo systemInfo) | ||||
| 		public VirtualMachineDetector(ILogger logger, IRegistry registry, ISystemInfo systemInfo) | ||||
| 		{ | ||||
| 			this.logger = logger; | ||||
| 			this.registry = registry;  | ||||
| 			this.systemInfo = systemInfo; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -97,45 +103,70 @@ namespace SafeExamBrowser.SystemComponents | |||
| 		{ | ||||
| 			var isVirtualMachine = false; | ||||
| 
 | ||||
| 			// check historic hardware profiles | ||||
| 			var hardwareConfKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SYSTEM\\HardwareConfig"); | ||||
| 			if (hardwareConfKey != null) | ||||
| 			{ | ||||
| 				foreach (string configId in hardwareConfKey.GetSubKeyNames()) | ||||
| 				{ | ||||
| 					var configKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey($"SYSTEM\\HardwareConfig\\{configId}"); | ||||
| 			/**  | ||||
| 			 * check historic hardware profiles | ||||
| 			 *  | ||||
| 			 * HKLM\SYSTEM\HardwareConfig\{configId=uuid}\ComputerIds | ||||
| 			 *	- {computerId=uuid}: {computerSummary=hardwareInfo} | ||||
| 			 *	 | ||||
| 			 */ | ||||
| 			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; | ||||
| 					} | ||||
| 
 | ||||
| 				// reconstruct the systemInfo.biosInfo string | ||||
| 					var biosInfo = (string) configKey.GetValue("BIOSVendor") + " " + (string) configKey.GetValue("BIOSVersion"); | ||||
| 					var manufacturer = (string) configKey.GetValue("SystemManufacturer"); | ||||
| 					var model = (string) configKey.GetValue("SystemProductName"); | ||||
| 				string biosInfo = $"{(string) biosVendor} {(string) biosVersion}"; | ||||
| 
 | ||||
| 					isVirtualMachine |= IsVirtualSystemInfo(biosInfo, manufacturer, model); | ||||
| 				isVirtualMachine |= IsVirtualSystemInfo(biosInfo, (string) systemManufacturer, (string) systemProductName); | ||||
| 
 | ||||
| 				// 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) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
| 
 | ||||
| 					foreach (var computerId in computerIdsKey.GetSubKeyNames()) | ||||
| 				foreach (var computerId in computerIds) | ||||
| 				{ | ||||
| 					logger.Info($"computerId: {computerId}"); | ||||
| 					// e.g. manufacturer&version&sku&... | ||||
| 						var computerSummary = (string) computerIdsKey.GetValue(computerId); | ||||
| 						isVirtualMachine |= IsVirtualSystemInfo(computerSummary, computerSummary, computerSummary); | ||||
| 					} | ||||
| 					object computerSummary; // = (string) computerIds.GetValue(computerId); | ||||
| 
 | ||||
| 					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 | ||||
| 			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"); | ||||
| 
 | ||||
| 			if (deviceCacheKey != null && currHostname != null) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Notselwyn
						Notselwyn