serverRoom/backend-csharp/AmtScanner.Api/Services/AmtHardwareQueryService.cs

681 lines
26 KiB
C#

using AmtScanner.Api.Models;
using Intel.Management.Wsman;
using System.Security;
namespace AmtScanner.Api.Services;
public class AmtHardwareQueryService : IAmtHardwareQueryService
{
private readonly ILogger<AmtHardwareQueryService> _logger;
public AmtHardwareQueryService(ILogger<AmtHardwareQueryService> logger)
{
_logger = logger;
}
public async Task<HardwareInfo> QueryHardwareInfoAsync(
string ipAddress,
string username,
string password,
List<int> 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}"
};
}
}