Статья Способы получения информации об ОС и железе с помощью Python. Часть 01: ОС Windows и модуль winreg

Определение оборудования компьютера, на котором вы работаете, сведения об операционной системе и прочие параметры порою бывают очень нужны. Цели, в которых их можно использовать – самые разнообразные. От простого любопытства, до определения версии чипсета, чтобы подобрать под него необходимое железо для апгрейда. И мы все, за годы использования привыкли пользоваться программами сторонних производителей. Это и не хорошо, и не плохо. Каждый инструмент выполняет свою задачу в нужное время и в нужном месте. Но, для большего понимания того, как и откуда берется информация, в те же самые программы, можно написать свое собственное, небольшое консольное приложение на Python. Давайте рассмотрим несколько вариантов того, как и с помощью каких модулей это можно сделать. А также попробуем оценить быстродействие получившихся скриптов, количество выводимой информации.


000.jpg


А начнем мы написание наших скриптов с попытки получения информации из реестра операционной системы Windows.

Давайте чуть подробнее остановимся на том, что же это вообще такое. В современных операционных системах реестр используется для хранения настроек, конфигурационных данных, информации об установленных программах и еще множество самых разнообразных параметров и настроек, которые используются операционной системой для работы с железом, разнообразными настройками системы, запуска и управления службами и прочими компонентами.

Реестра был введен в 1992 году в ОС Windows 3.1 и продолжает использоваться во всех, более поздних версиях ОС. Нужно понимать, что реестр представляет собой иерархическую структуру, подобную дереву, которая содержит множество ветвей и ключей. Каждый ключ содержит данные в виде значений. В общем, это довольно важный компонент операционной системы, неправильное использование которого может привести к непредсказуемым последствиям или отказу работы системы. Но, в рамках данной статьи изменять реестр мы не собираемся. А только лишь будем считывать его значения.

Первым скриптом, который мы напишем для получения данных о параметрах ОС и ее оборудовании, это будет скрипт получающий данные из реестра.


Что потребуется?

По большему счету для работы данного скрипта в установке сторонних модулей нет необходимости. Но, если вы хотите сохранить полученные данные в не текстовый файл, то установить сторонний модуль все же придется. В данном скрипте я использую модуль python-docx, который позволит нам сохранить полученные данные в документ Microsoft Word. Поэтому, для его установки нужно написать в терминале команду:

pip install python-docx

Также, в работе данного скрипта я использовал модуль windows-tools, с помощью которого получал данные о ключе активации операционной системы. По большему счету можно было бы обойтись и без него, так как, если мы заглянем внутрь данного модуля, то увидим, что в своей работе он также, как и мы, использует библиотеку winreg, а также дополнительную функцию для дешифровки ключа активации. Для установки данного модуля пишем в терминале команду:

pip install windows-tools

После того, как будут установлены модули, давайте выполним импорт необходимых библиотек в скрипт.

Python:
import time
from datetime import datetime as dt, timedelta
from platform import node
from struct import unpack
from winreg import OpenKeyEx, QueryValueEx, HKEY_LOCAL_MACHINE, QueryInfoKey, EnumKey, KEY_READ

from docx import Document

from windows_tools import product_key


Получение версии операционной системы Windows

Давайте напишем, в рамках данного скрипта, первую функцию, с помощью которой получим версию, сборку, название и ключ активации из реестра. Назовем ее winreg_os() -> dict. Никаких параметров данная функция не принимает, а вот возвращает словарь с полученными значениями в случае, если они были получены.

Создадим пустой словарь win_info, в который будем складывать полученные данные. Для начала получим имя компьютера. Для этого откроем ветку реестра, в котором содержится данное значение: HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\ и прочитаем значение ключа ComputerName.

Затем проделаем то же самое с получением времени последнего выключения компьютера. Для этого открываем ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows и читаем значение ключа ShutdownTime. Так как значение данного ключа содержится в реестре в микросекундах, для начала конвертируем это значение в человекочитаемый формат, а уж только после этого добавим в словарь.

Python:
def winreg_os() -> dict:
    win_info = dict()
    if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
        win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
    if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
        shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
        shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
            strftime('%Y-%m-%d %H:%M:%S')

Все остальные значения ключей, которые нам нужно получить, находятся в одной ветке реестра. Поэтому, для начала открываем ее с помощью OpenKeyEx: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion.

Запускаем цикл, в котором будем итерироваться по списку названий ключей реестра. По очереди будем получать значение каждого ключа. Перед тем, как добавить значение в словарь, проверим, не является ли ключ датой установки, так как ее нужно конвертировать в человекочитаемый вид. И добавляем все значения которые получим в словарь.

Python:
    if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
        for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
                    "RegisteredOwner"]:
            try:
                if key == "InstallDate":
                    win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
                else:
                    win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
            except FileNotFoundError:
                continue

Двигаемся дальше. И следующее значение, которое нам будет нужно, это временная зона. Хотя, по большому счету оно особой роли ни для чего не играет, но для информации пригодится. Открываем ключ реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation и читаем значение ключа TimeZoneKeyName, после чего добавляем его в словарь.

Python:
    if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
        win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})

Ну и последняя операция, которая, можно сказать, будет некоей вишенкой на данном торте функции )). Получим ключ активации операционной системы. На самом деле, знать ключ активации достаточно важно. И хотя, Microsoft уже перешла на цифровые лицензии, которые подхватываются автоматически при установке ОС, кто знает, может вам понадобиться установить Windows на другое железо, в другой сети. Не думаю, что ключ активации подхватиться с такой же легкостью. Вот потому, знать его желательно.

Поэтому, получаем данное значение с помощью модуля windows_tools и его метода product_key. Данный метод позволяет получить ключ из реестра даже в том случае, если он вшит в BIOS компьютера, так как его значение при установке копируется в реестр. Поэтому, для начала пробуем получить стандартный ключ. Если не получиться, тогда ключ из BIOS. Ну и если все получено, добавляем данные в словарь.

Python:
    if pkey := product_key.get_windows_product_key_from_reg():
        win_info.update({"ActivateKey": pkey})
    elif pkey := product_key.get_windows_product_key_from_wmi():
        win_info.update({"ActivateKey": pkey})
    else:
        win_info.update({"ActivateKey": "No Key"})
    return win_info if win_info else False

После того, как будут собраны все необходимые данные, проверяем, содержит ли что-то словарь, который мы создавали в начале. Если да, возвращаем его из функции.

Python:
# Узнаем версию ОС
def winreg_os() -> dict:
    win_info = dict()
    if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
        win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
    if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
        shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
        shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
            strftime('%Y-%m-%d %H:%M:%S')
        win_info.update({'ShutdownTime': shutdown_time})
    if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
        for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
                    "RegisteredOwner"]:
            try:
                if key == "InstallDate":
                    win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
                else:
                    win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
            except FileNotFoundError:
                continue
    if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
        win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})
    if pkey := product_key.get_windows_product_key_from_reg():
        win_info.update({"ActivateKey": pkey})
    elif pkey := product_key.get_windows_product_key_from_wmi():
        win_info.update({"ActivateKey": pkey})
    else:
        win_info.update({"ActivateKey": "No Key"})
    return win_info if win_info else False


Получение информации о BIOS

Перейдем к следующей части нашего « ». После некоторого гугления или яндексения… даже не знаю, как правильно сказать… в общем, после поиска информации в интернете и ковыряния в реестре, я обнаружил, что информация о BIOS храниться в следующих ветках: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System; HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS. И в них уже содержаться все необходимые нам ключи, значение которых необходимо прочитать.

Создадим функцию bios_winreg() -> dict, которая будет возвращать словарь с полученными значениями. Для начала, определим словарь, куда будем эти значения складывать. Затем подключимся к первой ветке реестра. Из нее мы получим версию BIOS и добавим ее в словарь.

Python:
def bios_winreg() -> dict:
    md_dict = dict()
    if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
        md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})

Создаем цикл, который будет итерироваться по списку ключей, после чего выполняем подключение ко второй ветке реестра, и по очереди забираем значения из ключей, подставляемых с помощью цикла. Добавляем их также в созданный ранее словарь. Обработаем исключение, на всякий случай, если не будет найдено нужного ключа. В этом случае мы просто продолжаем цикл и перебираем ключи до конца.

Python:
    for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
        if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
            try:
                md_dict.update({key: QueryValueEx(bios, key)[0]})
            except FileNotFoundError:
                continue
        else:
            return False
    return md_dict if md_dict else False

После того, как все значения получены, возвращаем словарь из функции, если он не пуст.

Python:
# BIOS
def bios_winreg() -> dict:
    md_dict = dict()
    if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
        md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})
    for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
        if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
            try:
                md_dict.update({key: QueryValueEx(bios, key)[0]})
            except FileNotFoundError:
                continue
        else:
            return False
    return md_dict if md_dict else False


Получение информации о материнской плате

Следующая функция, которую мы создадим: motherboard_winreg() -> (dict, bool), будет получать из реестра информацию о материнской плате и возвращать словарь с полученными значениями. Информация о материнской плате содержится в ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation. Поэтому, для начала создаем словарь, в который будем складывать полученные значения, а после подключимся к этой ветке реестра. Заберем информацию о производителе и названии материнской платы.

Python:
# Материнская плата
def motherboard_winreg() -> (dict, bool):
    md_dict = dict()
    if mb_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\SystemInformation"):
        md_dict.update({'SystemManufacturer': QueryValueEx(mb_info, 'SystemManufacturer')[0]})
        md_dict.update({'SystemProductName': QueryValueEx(mb_info, 'SystemProductName')[0]})
        return md_dict if md_dict else False
    return False

После того, как вся нужная информация будет получена, вернем словарь со значениями, если он не пуст из функции.


Получение информации о процессоре

Теперь перейдем к получению информации о процессоре. Данная информация содержится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor. В данной ветке находятся ключи, каждый из которых содержит информацию о ядре процессора. Независимо от того, физическое оно или логическое. Для примера, вот скриншот, как это может выглядеть:

001.png

Таким образом, узнав количество данных веток, мы сможем понять, сколько ядер у нашего процессора. Поэтому, подключаемся к ветке и с помощью QueryInfoKey забираем информацию о подкатегориях. Добавляем в словарь значение о количестве ядер.

Python:
def cpu_winreg():
    proc_info = dict()
    loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
        if QueryInfoKey(h_apps)[0]:
            proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})

Если мы откроем каждую из веток, то увидим, что информация о каждом ядре, по сути, является дубликатом информации самого первого ядра. За исключением некоторых параметров, таких как идентификаторы. Но получать мы их не будем, а ограничимся только общей информацией. Таким образом, перебирать каждую ветку не имеет, в нашем случае, особого смысла. Достаточно получить информацию из одной. Поэтому, подключаемся к самой первой ветке имеющей индекс 0. После чего забираем оттуда нужные параметры, такие как название процессора, идентификатор, производитель и частоту.

Python:
            try:
                core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
                proc_info.update({
                    "ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
                    "Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
                    "VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
                    "~MHz": QueryValueEx(core, '~MHz')[0]
                })
            except FileNotFoundError:
                return False
    return proc_info if proc_info else False

После того, как нужные параметры получены и добавлены в словарь, возвращаем его из функции. Если он, конечно же, не пуст.

Python:
# CPU
def cpu_winreg():
    proc_info = dict()
    loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
        if QueryInfoKey(h_apps)[0]:
            proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})
            try:
                core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
                proc_info.update({
                    "ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
                    "Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
                    "VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
                    "~MHz": QueryValueEx(core, '~MHz')[0]
                })
            except FileNotFoundError:
                return False
    return proc_info if proc_info else False


Получение информации о GPU

С получением информации о GPU из реестра, немного неоднозначная ситуация. Дело в том, что в системе нет стандартной ветки, откуда можно было бы забрать все по умолчанию. При установке драйверов на видеокарту конкретного производителя в реестре создается отдельная ветка, в которой и содержится нужная информация. Но, так как у каждого производителя и ветка будет своей, протестировать получение данной информации из реестра у меня не представляется возможным. Так как в наличии просто нет необходимого оборудования. Тем не менее, мы получим из реестра, в данном случае, хотя бы модель.

Создадим функцию gpu_winreg(), которая будет получать данные о модели и возвращать словарь с полученным значением. Информация о модели GPU содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000. Подключаемся к данной ветке. Забираем значение ключа DriverDesc, добавляем его в словарь и возвращаем из функции.

Python:
# GPU
def gpu_winreg():
    loc = r'SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000'
    key = OpenKeyEx(HKEY_LOCAL_MACHINE, loc, 0, KEY_READ)
    value = {"Name": QueryValueEx(key, "DriverDesc")[0]}
    return value if value else False


Получение информации о HDD/SSD

Создадим функцию hdd_ssd_winreg(). С ее помощью мы будем получать данные о всех жестких дисках, которые установлены в компьютере. Информация об этом находится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi. Подключаемся к ней.

Python:
hdd_ssd_info = dict()
    loc = "HARDWARE\\DEVICEMAP\\Scsi"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:

Следующее, что нам понадобится, так как мы не знаем, сколько дисков установлено в системе, узнать, сколько подразделов содержится в данном разделе реестра. Так же, как мы делали, когда получали информацию о процессоре и количестве его ядер.

Вот как выглядит соответствующая ветка реестра:

002.png

Как вы видите, далеко не каждый раздел содержит информацию. Более того, количество разделов будет равно количеству портов для подключения на плате. А значит, информация о диске будет содержаться только в том разделе, соответствующем порту, к которому диск подключен. Следовательно, нам нужно перебрать каждый раздел и подраздел, и получить значения ключей из каждого из них. А также обработать исключение, когда мы будем натыкаться на раздел, в котором нет информации о подключенном диске.

Для начала, создаем цикл, в котором будем итерироваться по диапазону соответствующему количеству разделов. Ну, а далее, будем в каждом из циклов подключаться все глубже и глубже по дереву, пока не достигнем конечной точки. Откуда и будем забирать значения ключей.


Python:
# HDD, SSD
def hdd_ssd_winreg():
    hdd_ssd_info = dict()
    loc = "HARDWARE\\DEVICEMAP\\Scsi"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
        for idx in range(QueryInfoKey(h_apps)[0]):
            try:
                scsi_port = OpenKeyEx(h_apps, EnumKey(h_apps, idx))
                for ids in range(QueryInfoKey(scsi_port)[0]):
                    scsi_bus = OpenKeyEx(scsi_port, EnumKey(scsi_port, ids))
                    for idb in range(QueryInfoKey(scsi_bus)[0]):
                        target = OpenKeyEx(scsi_bus, EnumKey(scsi_bus, idb))
                        for idc in range(QueryInfoKey(target)[0]):
                            log_unit = OpenKeyEx(target, EnumKey(target, idc))
                            hdd_ssd_info.update({QueryValueEx(log_unit, 'SerialNumber')[0].strip(): {
                                    "Vendor": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[0].strip(),
                                    "Model": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[1].strip(),
                                    "SerialNumber": QueryValueEx(log_unit, 'SerialNumber')[0].strip()
                                }})
            except FileNotFoundError:
                continue
    return hdd_ssd_info if hdd_ssd_info else False

После того, как информация будет собрана, возвращаем ее из функции. Хочу еще обратить ваше внимание на то, что, так как ключи, содержащие значения неуникальны, их нужно поместить в качестве словаря в ключ, содержащий уникальное значение о диске. Или номер, или название. Это не имеет особого значения. Главное, чтобы создаваемый ключ больше не повторялся.


Получение информации о CD/DVD-ROM

Вы можете сказать, что получение данной информации не имеет большого значения, так как данные устройства постепенно выходят из обихода и их уже можно встретить только на старых машинах. Так-то оно так, вот только не совсем. Дело в том, что с помощью данной информации можно узнать, используется ли в системе USB-модем, так как у каждого современного модема есть раздел, который определяется системой как внешний привод. А соответственно, узнав название и модель, мы получим представление, какой модем используется.

Создадим функцию cdrom_winreg(), которая будет возвращать словарь с полученными значениями. Информация о приводах содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\cdrom\Enum. Уже, подключившись к данной ветке, мы будем получать из нее значения ключей.

Создадим словарь, куда будем складывать полученные значения. В данной ветке есть хороший ключ Count, получив значение которого мы узнаем, сколько приводов установлено в системе. Тем более, что проверив это значение и поняв, что оно равно нулю, мы можем вовсе не итерироваться в поисках приводов, а просто вернуть False.

Python:
def cdrom_winreg():
    disks = dict()
    loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
    cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
    count = QueryValueEx(cd_rom, 'Count')[0]
    if count > 0:

Теперь, если значение больше нуля, итерируемся по диапазону значений равному количеству приводов. Собираем значения, которые присваиваем в переменные. Сразу же обработаем возможные исключения, если полученный ключ, к примеру, не сможет быть обработан. И уже после этого добавляем полученные значения в словарь, который возвращаем из функции.

Python:
# CD-ROM
def cdrom_winreg():
    disks = dict()
    loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
    cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
    count = QueryValueEx(cd_rom, 'Count')[0]
    if count > 0:
        for num in range(count):
            try:
                ven = QueryValueEx(cd_rom, f'{num}')[0].split("&")[1].split("_")[1]
            except Exception:
                ven = None
            try:
                prod = " ".join(QueryValueEx(cd_rom, f'{num}')[0].split("&")[2].split("_")[1:])
            except Exception:
                prod = None
            try:
                rev = QueryValueEx(cd_rom, f'{num}')[0].split("&")[3].split("_")[1].split("\\")[0]
            except Exception:
                rev = None
            try:
                serial = QueryValueEx(cd_rom, f'{num}')[0].split("&")[6]
            except Exception:
                serial = None

            disks.update({num: {
                "Vendor": ven,
                "Product": prod,
                "Revision": rev,
                "SerialNumber": serial
            }})
        return disks if disks else False
    return False


Получаем информацию о сетевых интерфейсах

Для получения данной информации придется несколько порыскать по веткам реестра. Собрать значения в одном месте, потом на основании собранных данных получить в другом. Нужно оговориться, что получать информацию мы будем только о физических сетевых интерфейсах. Все остальные, то есть виртуальные, нас будут волновать в последнюю очередь.

Создадим функцию nic_winreg(). На выходе она будет возвращать словарь с полученными данными. Для начала проверим ветку реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards. Информации здесь немного, но, мы получим отсюда имя сетевого адаптера и его идентификатор в системе. Сразу же создадим словарь, куда добавим полученные значения. Идентификатор будет служить в качестве ключа для всей остальной информации об интерфейсах. Также создадим список, куда сложим идентификаторы. Они понадобятся нам в дальнейшей работе функции.

Python:
def nic_winreg():
    nic = dict()
    loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
    desc = []
    if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
        for idx in range(QueryInfoKey(adapt_name)[0]):
            adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
            nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
            nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
                "Description": QueryValueEx(adapter, 'Description')[0]
            })
            desc.append(QueryValueEx(adapter, 'Description')[0])

Подключаемся к следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}. Здесь содержаться подразделы, в которых содержится чуть больше информации. Но, для начала, нужно понять, из какого подраздела нужно получать информацию, так как нам нужно будет забраться чуть-чуть поглубже.

003.png

Как вы помните, ранее мы создавали список с идентификаторами. Поэтому, перебираем каждый подраздел, забираем из него значение NetCfgInstanceId и сравниваем со списком. Если данное значение содержится в списке, создаем новый список и добавляем в него полученные значения.

Python:
            loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
            cfg = []
            if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
                for idc in range(QueryInfoKey(adapt)[0]):
                    try:
                        adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
                        if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
                            nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
                                "Description": QueryValueEx(adpt, 'DriverDesc')[0]
                            })
                            cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
                    except (FileNotFoundError, PermissionError):
                        continue

Теперь двигаемся глубже. Имя адаптера мы получили, необходимо теперь получить такие данные, как шлюз по умолчанию, ip-адрес. Переходим в следующую ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces и забираем оттуда данные, которые добавляем в словарь.

Python:
            if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
                for idb in range(QueryInfoKey(inter_cfg)[0]):
                    if EnumKey(inter_cfg, idb).upper() in cfg:
                        nic[EnumKey(inter_cfg, idb).upper()].update({
                            "NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
                        intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass

И еще один раздел реестра, куда нам нужно заглянуть: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}. В данном разделе содержится имя адаптера в системе. Помните, вот это вот: Ethernet, Ethernet2 и т.д. Вышеуказанная ветка – это только первая часть пути. Далее, нужно указать идентификатор адаптера и добавить слово «Connection». Чтобы получилось, для примера, как-то так: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1ECDE6DD-4799-4A4B-AEDB-F2C8C2AD528F}\Connection.

Затем подключаемся к данной ветке и уже оттуда забираем значение ключа Name.

Python:
                        netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
                               EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
                        if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "Name": QueryValueEx(netw_cfg, 'Name')[0]})
    return nic if nic else False

После того, как данные будут получены, возвращаем словарь с полученными значениями из функции.

Python:
# Network Interface
def nic_winreg():
    nic = dict()
    loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
    desc = []
    if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
        for idx in range(QueryInfoKey(adapt_name)[0]):
            adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
            nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
            nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
                "Description": QueryValueEx(adapter, 'Description')[0]
            })
            desc.append(QueryValueEx(adapter, 'Description')[0])
            loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
            cfg = []
            if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
                for idc in range(QueryInfoKey(adapt)[0]):
                    try:
                        adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
                        if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
                            nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
                                "Description": QueryValueEx(adpt, 'DriverDesc')[0]
                            })
                            cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
                    except (FileNotFoundError, PermissionError):
                        continue
            inter = r'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces'
            if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
                for idb in range(QueryInfoKey(inter_cfg)[0]):
                    if EnumKey(inter_cfg, idb).upper() in cfg:
                        nic[EnumKey(inter_cfg, idb).upper()].update({
                            "NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
                        intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass
                        netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
                               EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
                        if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "Name": QueryValueEx(netw_cfg, 'Name')[0]})
    return nic if nic else False


Что же, думаю, что на этот раз, достаточно. В довесок к предыдущей информации можно было бы получить данные обо всех установленных программах. Но найти об этом информацию и реализовать нужную функцию самостоятельно не особо сложно. А мы, тем временем, завершим с получением информации и перейдем к обработке полученных данных, выводу информации для пользователя в терминал и сохранению данных в документ Microsoft Word.


Обрабатываем полученные данные

Для того, чтобы обработать полученные данные я создал функцию print_wmic(part, dict_info). На вход она получает part, содержащий текст о той информации, которую нужно вывести в терминал. dict_info – собственно, сам словарь с полученными данными.

Для того чтобы не присваивать значения вручную, да и не присвоишь их при обработке в циклах множества словарей, я создал словарь, в котором содержится ключ, соответствующий названию параметра и его расшифровка на русском. Так что, большую часть функции занимает именно он.

Вот он собственно и сам:

Python:
def print_wmic(part, dict_info, num):
    global wmic_info
    synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
                "LastBootUpTime": "Время последней загрузки", "Version": "Версия",
                "WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
                "Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
                "MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
                "NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
                "NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
                "AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
                "Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
                "ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
                "SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
                "FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
                "VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
                "NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
                "IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
                "DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
                "IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
                "ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
                "RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
                "SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
                "BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
                "EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
                "SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
                "CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
                "VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
                "Model": "Модель", "Revision": "Ревизия", "Description": "Название",
                "NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
                "DhcpIPAddress": "IP-адрес"}

А теперь, функция печати. Для того чтобы сохранить полученные данные в документ, я создал глобальную переменную wmic_info = "", куда и буду помещать полученные данные. То есть, по сути, там будет содержаться большая строка разделенная переносами.
Ну, а дальше все просто. Проверяю тип ключа. Если это словарь, запускаю еще один цикл и добавляю информацию в созданную глобальную переменную. Если не словарь, то добавляю полученные значения.
Возвращать здесь ничего не нужно.

Python:
    part += f'{"-" * 50}\n'
    for key in dict_info:
        if type(dict_info[key]) == dict:
            for item in dict_info[key]:
                part += f'{synonyms[item]}: {dict_info[key][item]}\n'
            part += "\n"
        else:
            part += f'{synonyms[key]}: {dict_info[key]}\n'
    print(part)
    wmic_info += f'{part}\n'

Python:
def print_wmic(part, dict_info):
    global wmic_info
    synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
                "LastBootUpTime": "Время последней загрузки", "Version": "Версия",
                "WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
                "Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
                "MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
                "NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
                "NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
                "AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
                "Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
                "ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
                "SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
                "FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
                "VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
                "NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
                "IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
                "DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
                "IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
                "ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
                "RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
                "SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
                "BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
                "EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
                "SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
                "CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
                "VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
                "Model": "Модель", "Revision": "Ревизия", "Description": "Название",
                "NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
                "DhcpIPAddress": "IP-адрес"}
    part += f'{"-" * 50}\n'
    for key in dict_info:
        if type(dict_info[key]) == dict:
            for item in dict_info[key]:
                part += f'{synonyms[item]}: {dict_info[key][item]}\n'
            part += "\n"
        else:
            part += f'{synonyms[key]}: {dict_info[key]}\n'
    print(part)
    wmic_info += f'{part}\n'


Функция main

В данной функции мы будем проверять возвращаемые значения на истинности и в соответствии с этим запускать или не запускать функцию обработки. Перед этим добавляем с документ создаваемый с помощью python-docx заголовок. Ну и в конце добавляем в документ параграф в котором будет текст с полученными данными. Сохраняем документ с именем компьютера. И выводим в терминал время работы скрипта.

Python:
def main():
    global wmic_info
    t = time.monotonic()
    document = Document()
    document.add_heading(f'Сводная информация о компьютере: {node()}')

    if os_info := winreg_os():
        print_wmic("Информация об операционной системе\n", os_info)
    if bios_info := bios_winreg():
        print_wmic("Информация о BIOS\n", bios_info)
    if mb_info := motherboard_winreg():
        print_wmic("Информация о материнской плате\n", mb_info)
    if cpu_info := cpu_winreg():
        print_wmic("Информация о процессоре\n", cpu_info)
    if gpu_info := gpu_winreg():
        print_wmic("Информация о видеокарте\n", gpu_info)
    if drive_info := hdd_ssd_winreg():
        print_wmic("Информация о HDD и SSD\n", drive_info)
    if cd_rom_info := cdrom_winreg():
        print_wmic("Информация о CD/DVD-ROM\n", cd_rom_info)
    if nic_info := nic_winreg():
        print_wmic("Информация о физических сетевых интерфейсах\n", nic_info)

    document.add_paragraph(wmic_info)
    document.save(f'{node()}.docx')
    print(f"Собранная информация сохранена в файл: {node()}.docx")
    print(f'\nВремя работы скрипта: {time.monotonic() - t} с.')


if __name__ == "__main__":
    main()

Что же. На этом мы завершим создание скрипта по получению данных из реестра. Хочу сказать дополнительно, что к большому сожалению не нашел информации о том, как получить, хотя бы, общий объем оперативной памяти из реестра. Не знаю, может быть, плохо искал. Если кто-то знает, буду благодарен за подсказку.

А теперь давайте посмотрим на время его работы:

004.png


Что же, достаточно шустро. Но, это только первая часть из небольшого цикла о получении информации об ос и оборудовании различными способами, а также сравнении времени работы каждого способа. Продолжение в следующей статье. Далее мы рассмотрим способы получения информации с помощью subprocess.check_out, который будет парсить вывод утилиты wmic, запускаемой с различными параметрами. А в конце статьи также посмотрим на скорость работы полученной функции.

Python:
# pip install python-docx
# pip install windows-tools

import time
from datetime import datetime as dt, timedelta
from platform import node
from struct import unpack
from winreg import OpenKeyEx, QueryValueEx, HKEY_LOCAL_MACHINE, QueryInfoKey, EnumKey, KEY_READ

from docx import Document

from windows_tools import product_key


# Узнаем версию ОС
def winreg_os() -> dict:
    win_info = dict()
    if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
        win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
    if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
        shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
        shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
            strftime('%Y-%m-%d %H:%M:%S')
        win_info.update({'ShutdownTime': shutdown_time})
    if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
        for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
                    "RegisteredOwner"]:
            try:
                if key == "InstallDate":
                    win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
                else:
                    win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
            except FileNotFoundError:
                continue
    if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
        win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})
    if pkey := product_key.get_windows_product_key_from_reg():
        win_info.update({"ActivateKey": pkey})
    elif pkey := product_key.get_windows_product_key_from_wmi():
        win_info.update({"ActivateKey": pkey})
    else:
        win_info.update({"ActivateKey": "No Key"})
    return win_info if win_info else False


# BIOS
def bios_winreg() -> dict:
    md_dict = dict()
    if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
        md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})
    for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
        if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
            try:
                md_dict.update({key: QueryValueEx(bios, key)[0]})
            except FileNotFoundError:
                continue
        else:
            return False
    return md_dict if md_dict else False


# Материнская плата
def motherboard_winreg() -> (dict, bool):
    md_dict = dict()
    if mb_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\SystemInformation"):
        md_dict.update({'SystemManufacturer': QueryValueEx(mb_info, 'SystemManufacturer')[0]})
        md_dict.update({'SystemProductName': QueryValueEx(mb_info, 'SystemProductName')[0]})
        return md_dict if md_dict else False
    return False


# CPU
def cpu_winreg():
    proc_info = dict()
    loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
        if QueryInfoKey(h_apps)[0]:
            proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})
            try:
                core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
                proc_info.update({
                    "ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
                    "Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
                    "VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
                    "~MHz": QueryValueEx(core, '~MHz')[0]
                })
            except FileNotFoundError:
                return False
    return proc_info if proc_info else False


# GPU
def gpu_winreg():
    loc = r'SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000'
    key = OpenKeyEx(HKEY_LOCAL_MACHINE, loc, 0, KEY_READ)
    value = {"Name": QueryValueEx(key, "DriverDesc")[0]}
    return value if value else False


# HDD, SSD
def hdd_ssd_winreg():
    hdd_ssd_info = dict()
    loc = "HARDWARE\\DEVICEMAP\\Scsi"
    with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
        for idx in range(QueryInfoKey(h_apps)[0]):
            try:
                scsi_port = OpenKeyEx(h_apps, EnumKey(h_apps, idx))
                for ids in range(QueryInfoKey(scsi_port)[0]):
                    scsi_bus = OpenKeyEx(scsi_port, EnumKey(scsi_port, ids))
                    for idb in range(QueryInfoKey(scsi_bus)[0]):
                        target = OpenKeyEx(scsi_bus, EnumKey(scsi_bus, idb))
                        for idc in range(QueryInfoKey(target)[0]):
                            log_unit = OpenKeyEx(target, EnumKey(target, idc))
                            hdd_ssd_info.update({QueryValueEx(log_unit, 'SerialNumber')[0].strip(): {
                                    "Vendor": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[0].strip(),
                                    "Model": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[1].strip(),
                                    "SerialNumber": QueryValueEx(log_unit, 'SerialNumber')[0].strip()
                                }})
            except FileNotFoundError:
                continue
    return hdd_ssd_info if hdd_ssd_info else False


# CD-ROM
def cdrom_winreg():
    disks = dict()
    loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
    cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
    count = QueryValueEx(cd_rom, 'Count')[0]
    if count > 0:
        for num in range(count):
            try:
                ven = QueryValueEx(cd_rom, f'{num}')[0].split("&")[1].split("_")[1]
            except Exception:
                ven = None
            try:
                prod = " ".join(QueryValueEx(cd_rom, f'{num}')[0].split("&")[2].split("_")[1:])
            except Exception:
                prod = None
            try:
                rev = QueryValueEx(cd_rom, f'{num}')[0].split("&")[3].split("_")[1].split("\\")[0]
            except Exception:
                rev = None
            try:
                serial = QueryValueEx(cd_rom, f'{num}')[0].split("&")[6]
            except Exception:
                serial = None

            disks.update({num: {
                "Vendor": ven,
                "Product": prod,
                "Revision": rev,
                "SerialNumber": serial
            }})
        return disks if disks else False
    return False


# Network Interface
def nic_winreg():
    nic = dict()
    loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
    desc = []
    if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
        for idx in range(QueryInfoKey(adapt_name)[0]):
            adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
            nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
            nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
                "Description": QueryValueEx(adapter, 'Description')[0]
            })
            desc.append(QueryValueEx(adapter, 'Description')[0])
            loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
            cfg = []
            if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
                for idc in range(QueryInfoKey(adapt)[0]):
                    try:
                        adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
                        if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
                            nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
                                "Description": QueryValueEx(adpt, 'DriverDesc')[0]
                            })
                            cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
                    except (FileNotFoundError, PermissionError):
                        continue
            inter = r'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces'
            if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
                for idb in range(QueryInfoKey(inter_cfg)[0]):
                    if EnumKey(inter_cfg, idb).upper() in cfg:
                        nic[EnumKey(inter_cfg, idb).upper()].update({
                            "NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
                        intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass
                        try:
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
                        except FileNotFoundError:
                            pass
                        netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
                               EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
                        if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
                            nic[EnumKey(inter_cfg, idb).upper()].update({
                                "Name": QueryValueEx(netw_cfg, 'Name')[0]})
    return nic if nic else False


wmic_info = ""


def print_wmic(part, dict_info):
    global wmic_info
    synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
                "LastBootUpTime": "Время последней загрузки", "Version": "Версия",
                "WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
                "Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
                "MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
                "NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
                "NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
                "AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
                "Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
                "ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
                "SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
                "FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
                "VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
                "NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
                "IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
                "DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
                "IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
                "ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
                "RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
                "SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
                "BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
                "EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
                "SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
                "CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
                "VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
                "Model": "Модель", "Revision": "Ревизия", "Description": "Название",
                "NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
                "DhcpIPAddress": "IP-адрес"}
    part += f'{"-" * 50}\n'
    for key in dict_info:
        if type(dict_info[key]) == dict:
            for item in dict_info[key]:
                part += f'{synonyms[item]}: {dict_info[key][item]}\n'
            part += "\n"
        else:
            part += f'{synonyms[key]}: {dict_info[key]}\n'
    print(part)
    wmic_info += f'{part}\n'


def main():
    global wmic_info
    t = time.monotonic()
    document = Document()
    document.add_heading(f'Сводная информация о компьютере: {node()}')

    if os_info := winreg_os():
        print_wmic("Информация об операционной системе\n", os_info)
    if bios_info := bios_winreg():
        print_wmic("Информация о BIOS\n", bios_info)
    if mb_info := motherboard_winreg():
        print_wmic("Информация о материнской плате\n", mb_info)
    if cpu_info := cpu_winreg():
        print_wmic("Информация о процессоре\n", cpu_info)
    if gpu_info := gpu_winreg():
        print_wmic("Информация о видеокарте\n", gpu_info)
    if drive_info := hdd_ssd_winreg():
        print_wmic("Информация о HDD и SSD\n", drive_info)
    if cd_rom_info := cdrom_winreg():
        print_wmic("Информация о CD/DVD-ROM\n", cd_rom_info)
    if nic_info := nic_winreg():
        print_wmic("Информация о физических сетевых интерфейсах\n", nic_info)

    document.add_paragraph(wmic_info)
    document.save(f'{node()}.docx')
    print(f"Собранная информация сохранена в файл: {node()}.docx")
    print(f'\nВремя работы скрипта: {time.monotonic() - t} с.')


if __name__ == "__main__":
    main()

А на сегодня, пожалуй, все.

Спасибо за внимание. Надеюсь, данная информация будет вам полезна
 

Вложения

  • winreg_win_merge.zip
    4,2 КБ · Просмотры: 153
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!