using AmtScanner.Api.Models; using Intel.Management.Wsman; using System.Security; namespace AmtScanner.Api.Services; public class AmtHardwareQueryService : IAmtHardwareQueryService { private readonly ILogger _logger; public AmtHardwareQueryService(ILogger logger) { _logger = logger; } public async Task QueryHardwareInfoAsync( string ipAddress, string username, string password, List openPorts) { var hardwareInfo = new HardwareInfo { CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, LastUpdated = DateTime.UtcNow }; try { // Determine protocol and port string protocol; int port; if (openPorts.Contains(16992)) { protocol = "http"; port = 16992; } else if (openPorts.Contains(16993)) { protocol = "https"; port = 16993; } else { throw new Exception("No suitable AMT port found"); } _logger.LogInformation("Querying hardware info from {Protocol}://{Ip}:{Port}", protocol, ipAddress, port); // Create secure password var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); // Create WS-Management connection var connection = new WsmanConnection(); connection.Address = $"{protocol}://{ipAddress}:{port}/wsman"; connection.SetCredentials(username, securePassword); // Accept self-signed certificates for HTTPS if (protocol == "https") { connection.Options.ServerCertificateValidationCallback = (certificate, sslPolicyErrors) => { if (certificate.Subject.Equals(certificate.Issuer)) { return true; } if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None) { return true; } return false; }; } await Task.Run(() => { // Query all hardware components sequentially (not in parallel) QuerySystemInfo(connection, hardwareInfo); QueryProcessorInfo(connection, hardwareInfo); QueryMemoryInfo(connection, hardwareInfo); QueryStorageInfo(connection, hardwareInfo); }); _logger.LogInformation("Successfully queried hardware info from {Ip}", ipAddress); } catch (Exception ex) { _logger.LogError(ex, "Error querying hardware info from {Ip}", ipAddress); throw; } return hardwareInfo; } private void QuerySystemInfo(IWsmanConnection connection, HardwareInfo hardwareInfo) { try { _logger.LogDebug("Querying system information"); var query = connection.ExecQuery("SELECT * FROM CIM_ComputerSystem WHERE Name='ManagedSystem'"); int count = 0; foreach (IWsmanItem item in query) { count++; _logger.LogDebug("Found CIM_ComputerSystem item {Count}", count); var manufacturer = item.Object.GetProperty("Manufacturer"); _logger.LogDebug("Manufacturer IsNull: {IsNull}, Value: {Value}", manufacturer.IsNull, manufacturer.IsNull ? "null" : manufacturer.ToString()); if (!manufacturer.IsNull) { hardwareInfo.SystemManufacturer = manufacturer.ToString(); } var model = item.Object.GetProperty("Model"); _logger.LogDebug("Model IsNull: {IsNull}, Value: {Value}", model.IsNull, model.IsNull ? "null" : model.ToString()); if (!model.IsNull) { hardwareInfo.SystemModel = model.ToString(); } // Try to get serial number (may not be available) try { var serialNumber = item.Object.GetProperty("SerialNumber"); _logger.LogDebug("SerialNumber IsNull: {IsNull}, Value: {Value}", serialNumber.IsNull, serialNumber.IsNull ? "null" : serialNumber.ToString()); if (!serialNumber.IsNull) { hardwareInfo.SystemSerialNumber = serialNumber.ToString(); } } catch (Exception ex) { _logger.LogDebug(ex, "Serial number property not available"); } break; // Only need first result } if (count == 0) { _logger.LogWarning("No CIM_ComputerSystem items found"); } _logger.LogDebug("System info: {Manufacturer} {Model}", hardwareInfo.SystemManufacturer, hardwareInfo.SystemModel); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to query system information"); } } private void QueryProcessorInfo(IWsmanConnection connection, HardwareInfo hardwareInfo) { try { _logger.LogDebug("Querying processor information"); var query = connection.ExecQuery("SELECT * FROM CIM_Realizes"); foreach (IWsmanItem item in query) { if (item.Object.GetProperty("Dependent").IsA("CIM_Processor")) { var cpuObj = item.Object.GetProperty("Dependent").Ref.Get(); var chipObj = item.Object.GetProperty("Antecedent").Ref.Get(); // Try to get CPU model from Chip Version (more detailed) try { var version = chipObj.GetProperty("Version"); if (!version.IsNull && !string.IsNullOrWhiteSpace(version.ToString())) { hardwareInfo.ProcessorModel = version.ToString().Trim(); } } catch { // Version not available, fall back to Family } // If no version, use processor family if (string.IsNullOrEmpty(hardwareInfo.ProcessorModel)) { var family = cpuObj.GetProperty("Family"); if (!family.IsNull) { hardwareInfo.ProcessorModel = MapProcessorFamily(family.ToString()); } } // Get clock speeds var maxClockSpeed = cpuObj.GetProperty("MaxClockSpeed"); if (!maxClockSpeed.IsNull) { hardwareInfo.ProcessorMaxClockSpeed = int.Parse(maxClockSpeed.ToString()); } var currentClockSpeed = cpuObj.GetProperty("CurrentClockSpeed"); if (!currentClockSpeed.IsNull) { hardwareInfo.ProcessorCurrentClockSpeed = int.Parse(currentClockSpeed.ToString()); } // Try to get core count (may not be available on all systems) try { var numberOfCores = cpuObj.GetProperty("NumberOfCores"); if (!numberOfCores.IsNull) { hardwareInfo.ProcessorCores = int.Parse(numberOfCores.ToString()); } } catch { // Core count not available } // Try to get thread count try { var numberOfLogicalProcessors = cpuObj.GetProperty("NumberOfLogicalProcessors"); if (!numberOfLogicalProcessors.IsNull) { hardwareInfo.ProcessorThreads = int.Parse(numberOfLogicalProcessors.ToString()); } } catch { // Thread count not available } break; // Only need first processor } } _logger.LogDebug("Processor info: {Model} @ {Speed}MHz", hardwareInfo.ProcessorModel, hardwareInfo.ProcessorMaxClockSpeed); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to query processor information"); } } private void QueryMemoryInfo(IWsmanConnection connection, HardwareInfo hardwareInfo) { try { _logger.LogDebug("Querying memory information"); var query = connection.ExecQuery("SELECT * FROM CIM_PhysicalMemory"); long totalMemory = 0; foreach (IWsmanItem item in query) { var module = new MemoryModule(); var tag = item.Object.GetProperty("Tag"); if (!tag.IsNull) { module.SlotLocation = tag.ToString(); } var bankLabel = item.Object.GetProperty("BankLabel"); if (!bankLabel.IsNull && string.IsNullOrEmpty(module.SlotLocation)) { module.SlotLocation = bankLabel.ToString(); } var capacity = item.Object.GetProperty("Capacity"); if (!capacity.IsNull) { module.CapacityBytes = long.Parse(capacity.ToString()); totalMemory += module.CapacityBytes.Value; } // Try ConfiguredMemoryClockSpeed first (this is in MHz, available in newer AMT versions) try { var configuredSpeed = item.Object.GetProperty("ConfiguredMemoryClockSpeed"); _logger.LogDebug("Memory ConfiguredMemoryClockSpeed: IsNull={IsNull}, Value={Value}", configuredSpeed.IsNull, configuredSpeed.IsNull ? "null" : configuredSpeed.ToString()); if (!configuredSpeed.IsNull) { var configuredSpeedValue = int.Parse(configuredSpeed.ToString()); if (configuredSpeedValue > 0) { module.SpeedMHz = configuredSpeedValue; } } } catch (Exception ex) { _logger.LogDebug(ex, "ConfiguredMemoryClockSpeed not available"); } // If ConfiguredMemoryClockSpeed not available, try MaxMemorySpeed (also in MHz) if (module.SpeedMHz == null || module.SpeedMHz == 0) { try { var maxSpeed = item.Object.GetProperty("MaxMemorySpeed"); _logger.LogDebug("Memory MaxMemorySpeed: IsNull={IsNull}, Value={Value}", maxSpeed.IsNull, maxSpeed.IsNull ? "null" : maxSpeed.ToString()); if (!maxSpeed.IsNull) { var maxSpeedValue = int.Parse(maxSpeed.ToString()); if (maxSpeedValue > 0) { module.SpeedMHz = maxSpeedValue; } } } catch (Exception ex) { _logger.LogDebug(ex, "MaxMemorySpeed not available"); } } // Speed property is in nanoseconds, convert to MHz if other properties not available // Note: Speed in nanoseconds can be converted to MHz: MHz = 1000 / nanoseconds if (module.SpeedMHz == null || module.SpeedMHz == 0) { var speed = item.Object.GetProperty("Speed"); _logger.LogDebug("Memory Speed (nanoseconds): IsNull={IsNull}, Value={Value}", speed.IsNull, speed.IsNull ? "null" : speed.ToString()); if (!speed.IsNull) { var speedNs = int.Parse(speed.ToString()); if (speedNs > 0) { // Convert nanoseconds to MHz: MHz = 1000 / ns // For example: 0.625 ns = 1600 MHz, but AMT typically returns 0 for this // If speed is a reasonable nanosecond value (like 1-100), convert it if (speedNs <= 100) { module.SpeedMHz = (int)(1000.0 / speedNs); } else { // Some older AMT versions might return MHz directly in Speed field module.SpeedMHz = speedNs; } } } } var memoryType = item.Object.GetProperty("MemoryType"); if (!memoryType.IsNull) { module.MemoryType = MapMemoryType(memoryType.ToString()); } // Try to get FormFactor try { var formFactor = item.Object.GetProperty("FormFactor"); if (!formFactor.IsNull) { var formFactorStr = MapFormFactor(formFactor.ToString()); _logger.LogDebug("Memory FormFactor: {FormFactor}", formFactorStr); // We could add FormFactor to MemoryModule model if needed } } catch (Exception ex) { _logger.LogDebug(ex, "FormFactor not available"); } var manufacturer = item.Object.GetProperty("Manufacturer"); if (!manufacturer.IsNull) { module.Manufacturer = manufacturer.ToString(); } var partNumber = item.Object.GetProperty("PartNumber"); if (!partNumber.IsNull) { module.PartNumber = partNumber.ToString(); } try { var serialNumber = item.Object.GetProperty("SerialNumber"); if (!serialNumber.IsNull) { module.SerialNumber = serialNumber.ToString(); } } catch { // Serial number not available } hardwareInfo.MemoryModules.Add(module); } hardwareInfo.TotalMemoryBytes = totalMemory; _logger.LogDebug("Memory info: {Count} modules, {TotalGB}GB total", hardwareInfo.MemoryModules.Count, totalMemory / 1024 / 1024 / 1024); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to query memory information"); } } private void QueryStorageInfo(IWsmanConnection connection, HardwareInfo hardwareInfo) { try { _logger.LogDebug("Querying storage information"); var query = connection.ExecQuery("SELECT * FROM CIM_MediaAccessDevice"); foreach (IWsmanItem item in query) { var device = new StorageDevice(); var deviceId = item.Object.GetProperty("DeviceID"); if (!deviceId.IsNull) { device.DeviceId = deviceId.ToString(); } // Try to get model from CIM_PhysicalPackage via CIM_Realizes association // This is how the SDK gets the model number try { var realizesQuery = connection.ExecQuery("SELECT * FROM CIM_Realizes"); foreach (IWsmanItem realizesItem in realizesQuery) { try { var dependent = realizesItem.Object.GetProperty("Dependent"); if (!dependent.IsNull && dependent.IsA("CIM_MediaAccessDevice")) { // Check if this is the same device var depObj = dependent.Ref.Get(); var depDeviceId = depObj.GetProperty("DeviceID"); if (!depDeviceId.IsNull && depDeviceId.ToString() == device.DeviceId) { var antecedent = realizesItem.Object.GetProperty("Antecedent"); if (!antecedent.IsNull && antecedent.IsA("CIM_PhysicalPackage")) { var packageObj = antecedent.Ref.Get(); var model = packageObj.GetProperty("Model"); if (!model.IsNull && !string.IsNullOrWhiteSpace(model.ToString())) { device.Model = model.ToString().Trim(); _logger.LogDebug("Storage Model from CIM_PhysicalPackage: {Model}", device.Model); } // Also try to get serial number from package try { var serialNumber = packageObj.GetProperty("SerialNumber"); if (!serialNumber.IsNull && !string.IsNullOrWhiteSpace(serialNumber.ToString())) { _logger.LogDebug("Storage SerialNumber from CIM_PhysicalPackage: {SerialNumber}", serialNumber.ToString()); } } catch { } break; } } } } catch (Exception ex) { _logger.LogDebug(ex, "Error processing CIM_Realizes item"); } } } catch (Exception ex) { _logger.LogDebug(ex, "Failed to query CIM_Realizes for storage model"); } // Fallback: try to get model from MediaAccessDevice properties if (string.IsNullOrEmpty(device.Model)) { var caption = item.Object.GetProperty("Caption"); if (!caption.IsNull && !string.IsNullOrWhiteSpace(caption.ToString())) { device.Model = caption.ToString().Trim(); } } // If Caption is empty, try Name if (string.IsNullOrEmpty(device.Model)) { try { var name = item.Object.GetProperty("Name"); if (!name.IsNull && !string.IsNullOrWhiteSpace(name.ToString())) { device.Model = name.ToString().Trim(); } } catch { } } // If still empty, try Description if (string.IsNullOrEmpty(device.Model)) { try { var description = item.Object.GetProperty("Description"); if (!description.IsNull && !string.IsNullOrWhiteSpace(description.ToString())) { device.Model = description.ToString().Trim(); } } catch { } } // Try different capacity properties try { var maxMediaSize = item.Object.GetProperty("MaxMediaSize"); if (!maxMediaSize.IsNull) { device.CapacityBytes = long.Parse(maxMediaSize.ToString()) * 1024; // Convert KB to bytes } } catch { try { var capacity = item.Object.GetProperty("Capacity"); if (!capacity.IsNull) { device.CapacityBytes = long.Parse(capacity.ToString()); } } catch { // Capacity not available } } // Try to get interface type try { var interfaceType = item.Object.GetProperty("InterfaceType"); if (!interfaceType.IsNull) { device.InterfaceType = interfaceType.ToString(); } } catch { // Interface type not available } hardwareInfo.StorageDevices.Add(device); } _logger.LogDebug("Storage info: {Count} devices", hardwareInfo.StorageDevices.Count); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to query storage information"); } } private string MapProcessorFamily(string familyCode) { // Map processor family codes to readable names // Based on DMTF SMBIOS Reference Specification return familyCode switch { "2" => "Unknown", "3" => "8086", "4" => "80286", "5" => "80386", "6" => "80486", "11" => "Pentium", "12" => "Pentium Pro", "13" => "Pentium II", "15" => "Celeron", "16" => "Pentium II Xeon", "17" => "Pentium III", "179" => "Intel Core i3", "180" => "Intel Core i5", "181" => "Intel Core i7", "183" => "Intel Core i9", "198" => "Intel Core", // Generic Intel Core processor "205" => "Intel Core i9 (12th Gen)", "206" => "Intel Core i7 (12th Gen)", "207" => "Intel Core i5 (12th Gen)", "208" => "Intel Core i3 (12th Gen)", _ => $"Processor Family {familyCode}" }; } private string MapMemoryType(string typeCode) { // Map memory type codes to readable names // Based on DMTF SMBIOS Reference Specification return typeCode switch { "0" => "Unknown", "1" => "Other", "2" => "DRAM", "3" => "Synchronous DRAM", "4" => "Cache DRAM", "5" => "EDO", "6" => "EDRAM", "7" => "VRAM", "8" => "SRAM", "9" => "RAM", "10" => "ROM", "11" => "Flash", "12" => "EEPROM", "13" => "FEPROM", "14" => "EPROM", "15" => "CDRAM", "16" => "3DRAM", "17" => "SDRAM", "18" => "SGRAM", "19" => "RDRAM", "20" => "DDR", "21" => "DDR2", "22" => "DDR2 FB-DIMM", "24" => "DDR3", "25" => "FBD2", "26" => "DDR4", "27" => "LPDDR", "28" => "LPDDR2", "29" => "LPDDR3", "30" => "LPDDR4", "31" => "Logical non-volatile device", "32" => "HBM (High Bandwidth Memory)", "33" => "HBM2 (High Bandwidth Memory Generation 2)", "34" => "DDR5", "35" => "LPDDR5", _ => $"Type {typeCode}" }; } private string MapFormFactor(string formFactorCode) { // Map form factor codes to readable names // Based on DMTF SMBIOS Reference Specification return formFactorCode switch { "0" => "Unknown", "1" => "Other", "2" => "Unknown", "3" => "SIMM", "4" => "SIP", "5" => "Chip", "6" => "DIP", "7" => "ZIP", "8" => "Proprietary Card", "9" => "DIMM", "10" => "TSOP", "11" => "Row of chips", "12" => "RIMM", "13" => "SODIMM", "14" => "SRIMM", "15" => "FB-DIMM", _ => $"FormFactor {formFactorCode}" }; } }