from Scripts.datasets import pci_data from Scripts import utils from bs4 import BeautifulSoup class AIDA64: def __init__(self): self.aida64_download_url = "https://www.aida64.com/downloads" self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"} self.encodings = ['utf-8', 'latin-1', 'ISO-8859-1'] self.utils = utils.Utils() def try_open(self, file_path): for encoding in self.encodings: try: with open(file_path, 'r', encoding=encoding) as file: return file.read() except UnicodeDecodeError: continue raise UnicodeDecodeError(f"Unable to decode file {file_path} with given encodings") def hardware_id(self, hardware_id): if "VEN" in hardware_id: return { "Bus Type": hardware_id.split("\\")[0], "Device ID": "{}-{}".format(hardware_id.split("VEN_")[-1].split("&")[0], hardware_id.split("DEV_")[-1].split("&")[0]) } if "VID" in hardware_id: return { "Bus Type": hardware_id.split("\\")[0], "Device ID": "{}-{}".format(hardware_id.split("VID_")[-1].split("&")[0], hardware_id.split("PID_")[-1].split("&")[0]), "Revision": hardware_id.split("REV_")[-1].split("&")[0] } return { "Bus Type": hardware_id.split("\\")[0], "Device": hardware_id.split("\\")[-1] } def motherboard(self, motherboard_props, dmi): motherboard_info = {} # Extract motherboard name and chipset from provided properties motherboard_info["Motherboard Name"] = motherboard_props.get("Motherboard Name", "Unknown").split("(")[0].strip() motherboard_info["Motherboard Chipset"] = motherboard_props.get("Motherboard Chipset", "Unknown").split(", ")[0] # If motherboard name is still unknown, attempt to derive it from DMI information if "Unknown" in motherboard_info.get("Motherboard Name"): motherboard_info["Motherboard Name"] = "Unknown" merged_report = dmi.get("System", {}).copy() merged_report.update(dmi.get("Motherboard", {})) for device_key, device_value in merged_report.items(): motherboard_name = "{} {}".format(device_value.get("Manufacturer", "O.E.M."), device_value.get("Product", "O.E.M.")) if "O.E.M." not in motherboard_name and "System Product Name" not in motherboard_name and len(motherboard_name) > len(motherboard_info.get("Motherboard Name")): motherboard_info["Motherboard Name"] = motherboard_name # Extract platform type from chassis information motherboard_info["Platform"] = dmi.get("Chassis", {}).get("Chassis Properties", {}).get("Chassis Type", "Unknown") if any(word in motherboard_info["Platform"].lower() for word in ["convertible", "notebook", "laptop", "docking station"]): motherboard_info["Platform"] = "Laptop" elif any(word in motherboard_info["Platform"].lower() for word in ["desktop", "mini pc"]): motherboard_info["Platform"] = "Desktop" elif "NUC" in motherboard_info["Motherboard Name"]: motherboard_info["Platform"] = "NUC" # Count and format CPU configuration motherboard_info["CPU Configuration"] = str(len(dmi.get("Processors", {}))).zfill(2) return motherboard_info def cpu(self, cpu_table): cpu_info = {} # Extract CPU Manufacturer cpu_info["CPU Manufacturer"] = cpu_table.get("CPU Manufacturer", {}).get("Company Name", "Unknown") # Extract Processor Name cpu_props = cpu_table.get("CPU Properties", {}) cpu_info["Processor Name"] = cpu_props.get("CPU Type", "Unknown").split(",")[0] # Determine CPU Manufacturer if still unknown if "Intel" in cpu_info["CPU Manufacturer"]: cpu_info["CPU Manufacturer"] = "Intel" elif "Advanced Micro Devices" in cpu_info["CPU Manufacturer"]: cpu_info["CPU Manufacturer"] = "AMD" elif cpu_info["CPU Manufacturer"] == "Unknown": processor_name = cpu_table.get("Multi CPU", {}).get("CPU #1", "Unknown").split("with")[0].split(",")[0] if "Intel" in processor_name: cpu_info["CPU Manufacturer"] = "Intel" elif "AMD" in processor_name: cpu_info["CPU Manufacturer"] = "AMD" cpu_info["Processor Name"] += cpu_info["CPU Manufacturer"] + processor_name.split(cpu_info["CPU Manufacturer"])[-1] # Extract CPU Codename cpu_info["CPU Codename"] = cpu_props.get("CPU Alias", "Unknown") # Extract CPU Cores count core_indices = [] for key in list(cpu_table.get("CPU Utilization", {}).keys()): core_index = key.split("#")[2].split(" ")[0] if "CPU #1" in key and core_index not in core_indices: core_indices.append(core_index) cpu_info["CPU Cores"] = str(len(core_indices)).zfill(2) # Extract Instruction Set cpu_info["Instruction Set"] = cpu_props.get("Instruction Set", "Unknown") return cpu_info def memory(self, memory_arrays, memory_devices): return { "Memory Arrays": [ memory_array.get("Memory Array Properties", {}) for function, memory_array in memory_arrays.items() ], "Memory Devices": [ memory_device.get("Memory Device Properties", {}) for device_locator, memory_device in memory_devices.items() ] } def storage(self, ata_controllers, storage_controllers, ata_devices): storage_info = { "Storage Controllers": {}, "Disk Drives": {} } storage_controllers.update(ata_controllers) for controller_name, controller_props in storage_controllers.items(): bus_type = controller_props.get("Bus Type", "Unknown") if "PCI" in bus_type or "VID" in bus_type: pci_device = controller_props.get("PCI Device") if " SD " in pci_device or "MMC" in pci_device: continue storage_info["Storage Controllers"][controller_name] = { "Bus Type": controller_props.get("Bus Type", "Unknown"), "Device ID": controller_props.get("Device ID", "Unknown"), "Device Description": controller_props.get("PCI Device", "Unknown") } device_categories = ["NVMe Device Properties", "SSD Physical Info", "ATA Device Properties", "Disk Device Physical Info", "Device Properties"] for device_name, device_props in ata_devices.items(): disk_props = {} relevant_categories = [key for key in list(device_props.keys()) if key in device_categories] for category in relevant_categories: category_props = device_props[category] disk_props["Controller"] = category_props.get("Controller Type", "Unknown") disk_props["Interface"] = category_props.get("Interface", "Unknown") if category_props.get("Device Type", None): disk_props["Interface"] = category_props.get("Device Type") storage_info["Disk Drives"][device_name] = disk_props return storage_info def monitor(self, monitor): return { monitor_data["Monitor Properties"].get("Monitor Name", monitor_name): { "Monitor Type": monitor_data["Monitor Properties"].get("Monitor Type", "Unknown"), "Maximum Resolution": monitor_data["Monitor Properties"].get("Maximum Resolution", list(monitor_data.get("Supported Video Modes", {}).keys())[-1].split("_")[0] if "Supported Video Modes" in monitor_data else "Unknown") } for monitor_name, monitor_data in monitor.items() if "Monitor Properties" in monitor_data } def acpi(self, acpi_tables): return [ { "ACPI Signature": acpi_table["ACPI Table Properties"].get("ACPI Signature", "Unknown"), "Table Description": acpi_table["ACPI Table Properties"].get("Table Description", "Unknown"), "Table Length": acpi_table["ACPI Table Properties"].get("Table Length", "Unknown"), "OEM Table ID": acpi_table["ACPI Table Properties"].get("OEM Table ID", "Unknown") } for table_key, acpi_table in acpi_tables.items() if "ACPI Table Properties" in acpi_table ] def audio(self, windows_devices): audio_devices_info = {} audio_device_ids = [] for device_name, device_props in windows_devices.get("Audio inputs and outputs", {}).items(): driver_description = device_props.get("Driver Description", "Unknown") if " (" not in driver_description: continue audio_endpoint = driver_description.split(" (")[0] audio_controller_name = driver_description.split(" (")[-1].strip(" )").split("- ")[-1] audio_controller_props = self.utils.search_dict_iter(windows_devices, audio_controller_name) bus_type = audio_controller_props.get("Bus Type", "") if audio_controller_name not in audio_devices_info and (bus_type.endswith("AUDIO") or bus_type.endswith("USB") or bus_type.endswith("ACP")): audio_devices_info[audio_controller_name] = { "Bus Type": bus_type, "{} ID".format("USB" if bus_type.endswith("USB") else "Codec"): audio_controller_props.get("Device ID", "Unknown"), "Audio Endpoints": [audio_endpoint] } audio_device_ids.append(audio_controller_props.get("Device ID", "Unknown")) elif audio_controller_name in audio_devices_info: audio_devices_info[audio_controller_name]["Audio Endpoints"].append(audio_endpoint) detected_audio_devices = self.utils.search_dict_iter(windows_devices, "AUDIO", equal=False) windows_audio_devices = self.utils.search_dict_iter(windows_devices, detected_audio_devices) for device_name, device_props in windows_audio_devices.items(): bus_type = device_props.get("Bus Type", "") if bus_type.endswith("AUDIO") or bus_type.endswith("USB"): if device_props.get("Device ID", "Unknown") not in audio_device_ids: occurrences = self.count_keys(audio_devices_info, device_name) unique_device_name = f"{device_name}_#{occurrences}" if occurrences > 0 else device_name audio_devices_info[unique_device_name] = { "Bus Type": bus_type, "{} ID".format("USB" if bus_type.endswith("USB") else "Codec"): device_props.get("Device ID", "Unknown") } return audio_devices_info def gpu(self, gpu_data, vulkan_data, windows_devices): gpu_info_dict = {} for adapter_name, adapter_props in windows_devices.get("Display adapters", windows_devices.get("Display adaptors", {})).items(): device_id = adapter_props.get("Device ID") if not device_id: continue manufacturer = "Intel" if device_id.startswith("8086") else "AMD" if device_id.startswith("1002") else "NVIDIA" if device_id.startswith("10DE") else "Unknown" props_in_vulkan = self.utils.search_dict_iter(vulkan_data, device_id, equal=False) gpu_props = self.utils.search_dict_iter(gpu_data, device_id, equal=False) bus_type = gpu_props.get("Bus Type", "Unknown") device_type = props_in_vulkan.get("Device Type", "Discrete GPU" if "PCI Express" in bus_type else "Integrated GPU" if "Integrated" in bus_type else "Unknown") gpu_codename = gpu_props.get("GPU Code Name", props_in_vulkan.get("Device Code Name", "Unknown")) gpu_info_dict[gpu_props.get("Video Adapter", adapter_name)] = { "Manufacturer": manufacturer, "GPU Codename": gpu_codename, "Device ID": device_id, "Device Type": device_type, "Memory Size": gpu_props.get("Memory Size", "Unknown") } if not gpu_info_dict: if gpu_data: for gpu_name, gpu_props in gpu_data.items(): gpu_props = gpu_props.get("Graphics Processor Properties", {}) device_id = gpu_props.get("PCI Device", "Unknown").split(" / ")[0] manufacturer = "Intel" if device_id.startswith("8086") else "AMD" if device_id.startswith("1002") else "NVIDIA" if device_id.startswith("10DE") else "Unknown" bus_type = gpu_props.get("Bus Type", "Unknown") device_type = "Discrete GPU" if "PCI Express" in bus_type else "Integrated GPU" if "Integrated" in bus_type else "Unknown" gpu_codename = gpu_props.get("GPU Code Name", "Unknown") gpu_info_dict[gpu_props.get("Video Adapter", gpu_name.split(": ")[-1])] = { "Manufacturer": manufacturer, "GPU Codename": gpu_codename, "Device ID": device_id, "Device Type": device_type, "Memory Size": gpu_props.get("Memory Size", "Unknown") } return self.utils.sort_dict_by_key(gpu_info_dict, "Device Type") def input(self, human_interface_devices, keyboards, pointing_devices, usb_devices): input_devices_info = {} combined_devices = human_interface_devices.copy() combined_devices.update(keyboards) combined_devices.update(pointing_devices) for device_name, device_props in combined_devices.items(): bus_type = device_props.get("Bus Type", "Unknown") device_id = device_props.get("Device ID", "Unknown") if "ACPI" in bus_type or "ROOT" in bus_type or "USB" in bus_type: if "USB" in bus_type: device_name = self.utils.search_dict_iter(usb_devices, device_props["Device ID"]).get("Device Description", device_name) input_devices_info[device_name] = { "Bus Type": bus_type, "Device ID": device_id } return input_devices_info def usb(self, usb_controllers, usb_devices, windows_devices): usb_info = { "USB Controllers": { controller_name: { "Bus Type": controller_props.get("Bus Type", "Unknown"), "Device ID": controller_props.get("Device ID", "Unknown") } for controller_name, controller_props in usb_controllers.items() if controller_props.get("Bus Type", "Unknown").startswith("PCI") }, "USB Devices": {} } for device_name, device_data in usb_devices.items(): device_props = device_data.get("Device Properties", {}) manufacturer = device_props.get("Manufacturer", None) product = device_props.get("Product", None) device_description = f"{manufacturer} {product}" if manufacturer and product else product if product else None if not device_description: device_id = device_props.get("Device ID", None) revision = device_props.get("Revision", None)[:-1] if device_props.get("Revision") else None hardware_id = f'USB\\VID_{device_id[:4]}&PID_{device_id[5:]}&REV_{revision}' if device_id and revision else None if hardware_id: device_description = self.utils.search_dict_iter(windows_devices, hardware_id + "&MI_00").get("Driver Description", None) if not device_description: device_description = self.utils.search_dict_iter(windows_devices, hardware_id).get("Driver Description", device_name) occurrences = self.count_keys(usb_info["USB Devices"], device_description) device_description = f"{device_description}_#{occurrences}" if occurrences > 0 else device_description if "Hub" not in device_description and "Billboard" not in device_description and not "0000-0000" in device_props.get("Device ID"): usb_info["USB Devices"][device_description] = { "Device Description": device_description.split("_#")[0], "Device Class": device_props.get("Device Class"), "Device ID": device_props.get("Device ID"), "Revision": device_props.get("Revision") } return usb_info def network(self, windows_devices, pci_devices): network_info = {} for device_name, device_props in pci_devices.items(): device_class = device_props.get("Device Properties", {}).get("Device Class", "Unknown")[6:-1] if self.utils.contains_any(["Network", "Ethernet", "WiFi", "Wi-Fi", "Wireless"], device_class + device_name): device_id = device_props.get("Device Properties").get("Device ID") network_adapter = self.utils.search_dict_iter(windows_devices, device_id) network_adapters = self.utils.search_dict_iter(windows_devices, network_adapter) for adapter_name, adapter_props in network_adapters.items(): device_description = adapter_name + adapter_props.get("PCI Device", "Unknown") connection_name = "WiFi" if "WiFi" in device_description or "Wi-Fi" in device_description or "Wireless" in device_description else \ "Ethernet" if "Network" in device_description or "Ethernet" in device_description else None bus_type = adapter_props.get("Bus Type", "Unknown") if (bus_type.startswith("PCI") or bus_type.startswith("USB")) and connection_name: device_key = adapter_props.get("PCI Device") if not " - " in adapter_props.get("PCI Device", " - ") else adapter_name network_info[device_key.split(" [")[0]] = { "Connection Name": connection_name, "Bus Type": bus_type, "Device ID": adapter_props.get("Device ID", "Unknown") } return self.utils.sort_dict_by_key(network_info, "Connection Name") def sd_controller(self, pci_devices, usb_devices, hardware): combined_devices = pci_devices.copy() combined_devices.update(usb_devices) for device_name, device_data in combined_devices.items(): device_props = device_data.get("Device Properties", device_data) device_class = device_props.get("Device Class", "Unknown") if self.utils.contains_any(["SD Host Controller", "Card Reader", "SDHC", "SDXC", "SDUC", " SD ", "MMC"], f"{device_name} {device_class}"): hardware["SD Controller"] = { "Device Description": device_name, "Bus Type": "PCI" if device_props.get("Bus Type", "Unknown").startswith("PCI") else "USB", "Device ID": device_props.get("Device ID", "Unknown") } return hardware def intel_mei(self, cpu_codename, pci_devices, hardware): if "Sandy Bridge" in cpu_codename or "Ivy Bridge" in cpu_codename: intel_mei_data = self.utils.search_dict_iter(pci_devices, "HECI", equal=False) if not intel_mei_data: intel_mei_data = self.utils.search_dict_iter(pci_devices, "Management Engine Interface", equal=False) if intel_mei_data: intel_mei_props = intel_mei_data.get("Device Properties", {}) hardware["Intel MEI"] = { "Bus Type": intel_mei_props.get("Bus Type", "Unknown"), "Device ID": intel_mei_props.get("Device ID", "Unknown") } return hardware def bluetooth(self, bluetooth, usb_devices, hardware): bluetooth_info = {} for device_name, device_props in bluetooth.items(): bus_type = device_props.get("Bus Type", "Unknown") if bus_type.startswith("USB"): bluetooth_info[device_name] = { "Device ID": device_props.get("Device ID"), "Revision": device_props.get("Revision") } if not bluetooth_info: for usb_device_name, usb_device_props in usb_devices.items(): device_class = usb_device_props.get("Device Class", {}) device_id = usb_device_props.get("Device ID", {}) if "bluetooth" in (usb_device_name + device_class).lower() or device_id in pci_data.BluetoothIDs: bluetooth_info[usb_device_name] = usb_device_props if bluetooth_info: hardware["Bluetooth"] = bluetooth_info return hardware def biometric(self, biometric_devices, usb_devices, hardware): biometric_info = { device_name: { "Bus Type": device_props.get("Bus Type", "Unknown"), "Device ID": device_props.get("Device ID", "Unknown") } for device_name, device_props in biometric_devices.items() } if not biometric_info: for device_name, device_data in usb_devices.items(): if "fingerprint" in device_name.lower(): device_props = device_data.get("Device Properties", {}) biometric_info[device_name] = { "Bus Type": device_props.get("Bus Type", "Unknown"), "Device ID": device_props.get("Device ID", "Unknown") } if biometric_info: hardware["Biometric"] = biometric_info return hardware def parse_dmi(self, dmi_data): parsed_dmi = {} for full_key, item_value in dmi_data.items(): occurrence_suffix = '' category_name = None if '_#' in full_key: suffix_idx = full_key.index('_#') occurrence_suffix = full_key[suffix_idx:] full_key = full_key[:suffix_idx] if ' / ' in full_key: category_idx = full_key.index(' / ') category_name = full_key[:category_idx] device_name = full_key[category_idx + 3:] if not category_name: parsed_dmi[f"{full_key}{occurrence_suffix}"] = item_value else: if category_name not in parsed_dmi: parsed_dmi[category_name] = {} parsed_dmi[category_name][f"{device_name}{occurrence_suffix}"] = item_value return parsed_dmi def parse_windows_devices(self, windows_devices): parsed_windows_devices = {} for full_key, item_value in windows_devices.items(): device_props = item_value.get("Device Properties", {}) # Update device properties with hardware ID if available if "Hardware ID" in device_props: device_props.update(self.hardware_id(device_props.get("Hardware ID"))) # Extract category name from the full key category_name = full_key.split(" / ")[0] # Initialize category dictionary if not already present if category_name not in parsed_windows_devices: parsed_windows_devices[category_name] = {} # Extract device name from device properties device_name = device_props.get("Driver Description") # Count occurrences of device name within category occurrences = self.count_keys(parsed_windows_devices[category_name], device_name) device_name = f"{device_name}_#{occurrences}" if occurrences > 0 else device_name # Add device to category dictionary parsed_windows_devices[category_name][device_name] = device_props return parsed_windows_devices def count_keys(self, dictionary, target_key): return sum(1 for key in dictionary if target_key in key) def html_to_dict(self, html_content): soup = BeautifulSoup(html_content, "html.parser") tables = soup.find_all('table') if not tables: return {} root = {} table_names = [ "Summary", "DMI", "CPU", "GPU", "Vulkan", "ATA", "Windows Devices", "PCI Devices", "USB Devices" ] table = None for table_content in tables: # Find the table header to identify the table pt_element = table_content.find("td", class_="pt") if pt_element: table = pt_element.text.strip() elif table in table_names: root[table] = {} stack = [(root[table], -1)] # Stack holds (current_dict, current_level) lines = str(table_content).strip().splitlines() for line in lines: if line.startswith(''): # Remove tag line = line.replace('', '') # Calculate the current level based on the number of tags level = (len(line) - len(line.lstrip(''))) // 3 - 1 if level < 1: continue # Remove all tags from the left while line.startswith(""): line = line[line.find(">") + 1:] if not line.startswith('', line[:idx], '', line[idx:]) else: continue soup_line = BeautifulSoup(line, "html.parser") td_elements = soup_line.find_all('td') key = td_elements[0].text.strip() value = None if len(td_elements) < 2 else td_elements[-1].text.strip() # Clean the key key = key.rstrip(":").strip("[]").strip() # Pop from stack to find the correct parent dictionary while stack and stack[-1][1] >= level: stack.pop() # Add the new key-value pair current_dict = stack[-1][0] occurrences = self.count_keys(current_dict, key) key = f"{key}_#{occurrences}" if occurrences > 0 else key if value is None: new_dict = {} current_dict[key] = new_dict stack.append((new_dict, level)) else: if '' not in line: current_dict[key] = value else: if not current_dict.items(): current_dict[key] = [] current_dict[value] = [] else: current_dict[list(current_dict.keys())[0]].append(key) current_dict[list(current_dict.keys())[1]].append(value) if len(table_names) - len(root) > 1: raise Exception("Your AIDA64 report is missing some information. Please revise it according to the provided guidelines") return root def dump(self, report_path): html_content = self.try_open(report_path) report_dict = self.html_to_dict(html_content) dmi = self.parse_dmi(report_dict.get("DMI", {})) windows_devices = self.parse_windows_devices(report_dict.get("Windows Devices", {})) hardware = {} hardware["Motherboard"] = self.motherboard(report_dict.get("Summary", {}).get("Motherboard", {}), dmi) hardware["CPU"] = self.cpu(report_dict.get("CPU", {})) hardware["GPU"] = self.gpu(report_dict.get("GPU", {}), report_dict.get("Vulkan", {}), windows_devices) hardware["Network"] = self.network(windows_devices, report_dict.get("PCI Devices", {})) hardware["Storage"] = self.storage(windows_devices.get("IDE ATA/ATAPI controllers", {}), windows_devices.get("Storage controllers", {}), report_dict.get("ATA", {})) hardware["Audio"] = self.audio(windows_devices) hardware["USB"] = self.usb(windows_devices.get("Universal Serial Bus controllers", {}), report_dict.get("USB Devices", {}), windows_devices) hardware["Input"] = self.input(windows_devices.get("Human Interface Devices", {}), windows_devices.get("Keyboards", {}), windows_devices.get("Mice and other pointing devices", {}), hardware["USB"].get("USB Devices", {})) hardware = self.biometric(windows_devices.get("Biometric devices", {}), hardware["USB"].get("USB Devices", {}), hardware) hardware = self.bluetooth(windows_devices.get("Bluetooth", {}), hardware["USB"].get("USB Devices", {}), hardware) hardware = self.sd_controller(report_dict.get("PCI Devices", {}), hardware["USB"].get("USB Devices", {}), hardware) hardware = self.intel_mei(hardware["CPU"].get("CPU Codename"), report_dict.get("PCI Devices", {}), hardware) return hardware