• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Получаем информацию о системе с помощью Python. Часть #1

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

000.jpg

А что же может предложить Python в этом плане? Скажем так, много чего интересного. Если задастся вопросом, то можно углубляться в эту тему, тему сбора информации, очень долгое время. И порою, кажется, что этой теме нет конца. Как говорил Козьма Прутков: «Нельзя объять необъятное».

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


Что понадобиться?

Из того, что нужно устанавливать, нам понадобиться библиотека psutil. Установить ее можно с помощью команды:

pip install psutil

Также нужно импортировать библиотеку json и модуль uname из библиотеки platform. Давайте сделаем это:

Python:
import json

import psutil
from platform import uname

Итак, необходимые библиотеки установлены и импортированы, пора приступать к написанию программы. Что она должна уметь? Получать информацию о версии ОС, процессоре, RAM и прочих интересных подробностях. То есть, для начала, нам нужен сборщик информации, который пробежится по всем доступным параметрам и сложит их, к примеру, в словарь для удобства дальнейшего использования и чтения. Ну и конечно же, нужно вывести полученную информацию на экран. Хотя, это вовсе не обязательно. И нужно, чтобы это можно было легко отключить. А соответственно, понадобиться отдельная функция для печати. Ну и определение версии операционной системы. На данном этапе это не существенно. Так как те библиотеки, что используются в настоящее время, кроссплатформенные. Но, если мы хотим усовершенствовать программу и получить от нее еще больше результатов, это совершенно необходимо. Давайте же приступим.

На начальном этапе создадим функцию, с помощью которой будем переводить большое количество байтов, то есть информацию об объеме или частоте в формат, который будет удобен для восприятия. Назову ее def correct_size(bts, ending='iB'). На входе она будет получать число, которое нужно перевести в воспринимаемый формат, и тут же укажем префикс для того, чтобы он автоматически подставлялся к полученному значению. Создадим список, в котором будут содержаться буквы, обозначающие определенный размер: килобайты, мегабайты, гигабайты, террабайты и петабайты. Мало ли что :oops:

Python:
def correct_size(bts, ending='iB'):
    size = 1024
    for item in ["", "K", "M", "Г", "T", "П"]:
        if bts < size:
            return f"{bts:.2f}{item}{ending}"
        bts /= size

После запустим цикл, в котором будет определено условие, срабатывающее тогда, когда количество байтов меньше 1024, то будем возвращать количество байтов и единицы измерения. В том случае, если условие не сработало, то байты будем делить целочисленно на size.


Сбор информации

Теперь можно приступить к сбору информации. Создадим функцию creating_file(), в которой и будем это делать. Создадим словарь, в который и будем складывать полученные значения по мере их поступления. Затем добавляем в словарь раздел «info». Это делается с помощью условия, в котором проверяется, есть ли такое значение в текущем словаре. Если его нет, то создаем и далее складываем в него получаемую информацию. Первым разделом в словаре будет system, в котором уже из названия понятно, что в нем будет содержаться информация о системе. Та, до которой сможем дотянуться на первоначальном этапе.

Для начала узнаем имя компьютера с помощью модуля uname() и его функции node. Кладем его в словарь. С помощью функции system модуля uname() узнаем наименование операционной системы: uname().system. Затем ее релиз, то есть, что это за версия. Семерка, десятка или восьмерка с помощью uname().release. Ее номер сборки: uname().version, а также архитектуру: 32 или 64 с помощью uname().machine.

Теперь переходим к процессору. Особо много информации мы не получим. Но хотя бы что-то. Получаем его семейство с помощью функции uname().processor. Записываем его в словарь и тут уже настало время подключить библиотеку psutil. С помощью нее узнаем количество физических ядер: psutil.cpu_count(logical=False). Для этого параметр logical ставим в значение False. Затем узнаем общее количество ядер: psutil.cpu_count(logical=True). Тут уже logical=True. И максимальную частоту, до которой способен турбиться данный процессор: psutil.cpu_freq().max:.2f}Мгц. Еще можно получить минимальную частоту, а также текущую частоту процессора.

Переходим к оперативной памяти. Узнаем ее полный объем: correct_size(psutil.virtual_memory().total. Чтобы значения выводились удобно для восприятия прогоняем ее через функцию для коррекции значений. Так же получаем доступную в данный момент память: correct_size(psutil.virtual_memory().available), и тот ее объем, который используется: correct_size(psutil.virtual_memory().used).

Все это, как я и писал ранее, складываем в словарь для дальнейшего использования. Вот код скрипта до этого момента.

Python:
import json

import psutil
from platform import uname


def correct_size(bts, ending='iB'):
    size = 1024
    for item in ["", "K", "M", "G", "T", "P"]:
        if bts < size:
            return f"{bts:.2f}{item}{ending}"
        bts /= size


def creating_file():
    collect_info_dict = dict()
    if 'info' not in collect_info_dict:
        collect_info_dict['info'] = dict()
        collect_info_dict['info']['system_info'] = dict()
        collect_info_dict['info']['system_info'] = {'system': {'comp_name': uname().node,
                                                               'os_name': f"{uname().system} {uname().release}",
                                                               'version': uname().version,
                                                               'machine': uname().machine},
                                                    'processor': {'name': uname().processor,
                                                                  'phisycal_core': psutil.cpu_count(logical=False),
                                                                  'all_core': psutil.cpu_count(logical=True),
                                                                  'freq_max': f"{psutil.cpu_freq().max:.2f}Мгц"},
                                                    'ram': {'volume': correct_size(psutil.virtual_memory().total),
                                                            'aviable': correct_size(psutil.virtual_memory().available),
                                                            'used': correct_size(psutil.virtual_memory().used)}}

Что же, двигаемся дальше, по нелегкому пути сбора информации. Следующим этапом будет получение информации о дисках. Так как их может быть несколько нужно будет использовать функцию, чтобы добыть все значения обо всех дисках, которые у нас в системе. Поэтому создаем цикл который будет пробегаться по значениям возвращаемым psutil.disk_partitions(). Здесь получаем с помощью psutil.disk_usage(partition.mountpoint) такие значения как имя диска, файловую систему, объем, сколько места занято и свободно, а также заполненность диска в процентах. Получение значений обернем в try – except для того, чтобы программа не вылетала с ошибкой. Потому, как попытка доступа к диску, который используется системой или другой программой, будет заблокирован. Поэтому, просто пропускаем этот этап и двигаемся далее. Ну и конечно складываем все в словарь.

Python:
    for partition in psutil.disk_partitions():
        try:
            partition_usage = psutil.disk_usage(partition.mountpoint)
        except PermissionError:
            continue
        if 'disk_info' not in collect_info_dict['info']:
            collect_info_dict['info']['disk_info'] = dict()
        if f"'device': {partition.device}" not in collect_info_dict['info']['disk_info']:
            collect_info_dict['info']['disk_info'][partition.device] = dict()
            collect_info_dict['info']['disk_info'][partition.device] = {'file_system': partition.fstype,
                                                                        'size_total': correct_size(
                                                                            partition_usage.total),
                                                                        'size_used': correct_size(
                                                                            partition_usage.used),
                                                                        'size_free': correct_size(
                                                                            partition_usage.free),
                                                                        'percent':
                                                                            f'{partition_usage.percent}'}

Теперь получим информацию о сетевых интерфейсах, которые доступны в нашей системе. Сделаем это с помощью psutil.net_if_addrs().items(). Она возвращает такую информацию об интерфейсе как: Имя интерфейса, MAC-адрес, IPv4 и IPv6 адреса. Конечно же, в системе может быть установлено несколько сетевых адаптеров, не считая тех виртуальных адаптеров, которые добавляются программами VmWare и VirtualBox. И конечно же локальная петля, которая также является сетевым интерфейсом. Поэтому, запускаем цикл по значениям данной функции, а так как нам возвращается словарь, то отдельно определим переменную для имени интерфейса и остальных параметров. На данном этапе информацию о локальной петле я решил не включать, а просто пропустить с помощью условия. Так как мне показалось, что данная информация не особо ценна для исследования. Ну и конечно же, складываем все добытое непосильным трудом в словарь.

Python:
for interface_name, interface_address in psutil.net_if_addrs().items():
        if interface_name == 'Loopback Pseudo-Interface 1':
            continue
        else:
            if 'net_info' not in collect_info_dict['info']:
                collect_info_dict['info']['net_info'] = dict()
            if interface_name not in collect_info_dict['info']['net_info']:
                collect_info_dict['info']['net_info'][interface_name] = dict()
                collect_info_dict['info']['net_info'][interface_name] = {
                    'mac': interface_address[0].address.replace("-", ":"),
                    'ipv4': interface_address[1].address,
                    'ipv6': interface_address[2].address}

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

Python:
def creating_file():
    collect_info_dict = dict()
    if 'info' not in collect_info_dict:
        collect_info_dict['info'] = dict()
        collect_info_dict['info']['system_info'] = dict()
        collect_info_dict['info']['system_info'] = {'system': {'comp_name': uname().node,
                                                               'os_name': f"{uname().system} {uname().release}",
                                                               'version': uname().version,
                                                               'machine': uname().machine},
                                                    'processor': {'name': uname().processor,
                                                                  'phisycal_core': psutil.cpu_count(logical=False),
                                                                  'all_core': psutil.cpu_count(logical=True),
                                                                  'freq_max': f"{psutil.cpu_freq().max:.2f}Мгц"},
                                                    'ram': {'volume': correct_size(psutil.virtual_memory().total),
                                                            'aviable': correct_size(psutil.virtual_memory().available),
                                                            'used': correct_size(psutil.virtual_memory().used)}}

    for partition in psutil.disk_partitions():
        try:
            partition_usage = psutil.disk_usage(partition.mountpoint)
        except PermissionError:
            continue
        if 'disk_info' not in collect_info_dict['info']:
            collect_info_dict['info']['disk_info'] = dict()
        if f"'device': {partition.device}" not in collect_info_dict['info']['disk_info']:
            collect_info_dict['info']['disk_info'][partition.device] = dict()
            collect_info_dict['info']['disk_info'][partition.device] = {'file_system': partition.fstype,
                                                                        'size_total': correct_size(
                                                                            partition_usage.total),
                                                                        'size_used': correct_size(
                                                                            partition_usage.used),
                                                                        'size_free': correct_size(
                                                                            partition_usage.free),
                                                                        'percent':
                                                                            f'{partition_usage.percent}'}

    for interface_name, interface_address in psutil.net_if_addrs().items():
        if interface_name == 'Loopback Pseudo-Interface 1':
            continue
        else:
            if 'net_info' not in collect_info_dict['info']:
                collect_info_dict['info']['net_info'] = dict()
            if interface_name not in collect_info_dict['info']['net_info']:
                collect_info_dict['info']['net_info'][interface_name] = dict()
                collect_info_dict['info']['net_info'][interface_name] = {
                    'mac': interface_address[0].address.replace("-", ":"),
                    'ipv4': interface_address[1].address,
                    'ipv6': interface_address[2].address}

    return collect_info_dict

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

Создадим функцию def print_info(dict_info), которая на входе получает собранный ранее словарь. Ну и далее запускаем цикл, в котором пробегаемся по всем значениям. Тут важный момент состоит в том, чтобы определить, какое из значений является вложенным словарем, чтобы и из него забрать все значения. Поэтому пишем условия, в которых определяем, является ли значение разделом со словарем. Если да, то заходим в него и забираем значения уже оттуда. После чего формируем принт с помощью f-строки и выводим на экран.

Python:
def print_info(dict_info):
    for item in dict_info['info']:
        if item == "system_info":
            for elem in dict_info['info'][item]:
                if elem == 'system':
                    print(f"[+] Информация о системе\n"
                          f"\t- Имя компьютера: {dict_info['info'][item][elem]['comp_name']}\n"
                          f"\t- Опереционная система: {dict_info['info'][item][elem]['os_name']}\n"
                          f"\t- Сборка: {dict_info['info'][item][elem]['version']}\n"
                          f"\t- Архитектура: {dict_info['info'][item][elem]['machine']}\n")
                if elem == 'processor':
                    print(f"[+] Информация о процессоре\n"
                          f"\t- Семейство: {dict_info['info'][item][elem]['name']}\n"
                          f"\t- Физические ядра: {dict_info['info'][item][elem]['phisycal_core']}\n"
                          f"\t- Всего ядер: {dict_info['info'][item][elem]['all_core']}\n"
                          f"\t- Максимальная частота: {dict_info['info'][item][elem]['freq_max']}\n")
                if elem == 'ram':
                    print(f"[+] Оперативная память\n"
                          f"\t- Объем: {dict_info['info'][item][elem]['volume']}\n"
                          f"\t- Доступно: {dict_info['info'][item][elem]['aviable']}\n"
                          f"\t- Используется: {dict_info['info'][item][elem]['used']}\n")
        if item == "disk_info":
            for elem in dict_info['info'][item]:
                print(f"[+] Информация о дисках\n"
                      f"\t- Имя диска: {elem}\n"
                      f"\t- Файловая система: {dict_info['info'][item][elem]['file_system']}\n"
                      f"\t- Объем диска: {dict_info['info'][item][elem]['size_total']}\n"
                      f"\t- Занято: {dict_info['info'][item][elem]['size_used']}\n"
                      f"\t- Свободно: {dict_info['info'][item][elem]['size_free']}\n"
                      f"\t- Заполненность: {dict_info['info'][item][elem]['percent']}%\n")
        if item == "net_info":
            for elem in dict_info['info'][item]:
                print(f"[+] Информация о сети\n"
                      f"\t- Имя интерфейса: {elem}\n"
                      f"\t- MAC-адрес: {dict_info['info'][item][elem]['mac']}\n"
                      f"\t- IPv4: {dict_info['info'][item][elem]['ipv4']}\n"
                      f"\t- IPv6: {dict_info['info'][item][elem]['ipv6']}\n")

А теперь перейдем к функции main(), из которой все и запускается. Для начала определяем операционную систему с помощью uname().system и в зависимости от этого запускаем тот или иной код. На данном этапе, как вы видите, он одинаков. Так как определение операционной системы понадобиться в дальнейшем. Это для того, чтобы не использовать функции и команды предназначенные для Windows в Linux и наоборот. Ну и тут же производиться запись полученных значений в файл. Чтобы в потом можно было его легко читать и при необходимости даже использовать, возможно, в какой-нибудь утилите.

Python:
def main():
    if uname().system == "Windows":
        dict_info = creating_file()
        with open(f'info_{uname().node}.json', 'w', encoding='utf-8') as file:
            json.dump(dict_info, file, indent=4, ensure_ascii=False)
        print_info(dict_info)
    elif uname().system == "Linux":
        dict_info = creating_file()
        with open(f'info_{uname().node}.json', 'w', encoding='utf-8') as file:
            json.dump(dict_info, file, indent=4, ensure_ascii=False)
        print_info(dict_info)

А теперь, чтобы вы понимали, как выглядит работа данной утилиты, предоставлю пару скриншотов из Windows и Linux, чтобы посмотреть, что там и там скрипт отрабатывает одинаково.

Результат работы программы в Windows:

screenshot1.png

Обратите внимание на то, что был создан JSON файл с именем компьютера и содержащий информацию о нем:

screenshot2.png

А это результат работы программы в Linux:

screenshot3.png

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

Обратите внимание еще и на то, что для работы скрипта не требуются права администратора в Windows или суперпользователя в Linux. Все происходит от текущего пользователя с ограниченными правами в тихом и мирном режиме. Ничего, можно сказать, даже не шелохнется.

Хотя, тут, наверное, надо упомянуть, что, если собрать данный скрипт в exe с помощью того же PyInstaller, сразу же просыпается антивирусник и начинает почем зря лаять на бедную софтину. Мол троян, бэкдор, вредоносный код. Впрочем, как я почитал, антивирусники относятся к данного вида сборкам не очень благосклонно. Так как файл представляет собой не полноценный скомпилированный exe-файл, а что-то вроде архива, то во время работы он и распаковывается во временную директорию. А антивирусникам это не нравиться. Причем, независимо от того, какой у вас будет там код. Хоть «Hello Word» сделайте. А ругаться все равно будет. Так что, это не особо приятный факт. Но, думаю, что победить его можно. По крайней мере, нужно искать решения.

Python:
import json

import psutil
from platform import uname


def correct_size(bts, ending='iB'):
    size = 1024
    for item in ["", "K", "M", "G", "T", "P"]:
        if bts < size:
            return f"{bts:.2f}{item}{ending}"
        bts /= size


def creating_file():
    collect_info_dict = dict()
    if 'info' not in collect_info_dict:
        collect_info_dict['info'] = dict()
        collect_info_dict['info']['system_info'] = dict()
        collect_info_dict['info']['system_info'] = {'system': {'comp_name': uname().node,
                                                               'os_name': f"{uname().system} {uname().release}",
                                                               'version': uname().version,
                                                               'machine': uname().machine},
                                                    'processor': {'name': uname().processor,
                                                                  'phisycal_core': psutil.cpu_count(logical=False),
                                                                  'all_core': psutil.cpu_count(logical=True),
                                                                  'freq_max': f"{psutil.cpu_freq().max:.2f}Мгц"},
                                                    'ram': {'volume': correct_size(psutil.virtual_memory().total),
                                                            'aviable': correct_size(psutil.virtual_memory().available),
                                                            'used': correct_size(psutil.virtual_memory().used)}}

    for partition in psutil.disk_partitions():
        try:
            partition_usage = psutil.disk_usage(partition.mountpoint)
        except PermissionError:
            continue
        if 'disk_info' not in collect_info_dict['info']:
            collect_info_dict['info']['disk_info'] = dict()
        if f"'device': {partition.device}" not in collect_info_dict['info']['disk_info']:
            collect_info_dict['info']['disk_info'][partition.device] = dict()
            collect_info_dict['info']['disk_info'][partition.device] = {'file_system': partition.fstype,
                                                                        'size_total': correct_size(
                                                                            partition_usage.total),
                                                                        'size_used': correct_size(
                                                                            partition_usage.used),
                                                                        'size_free': correct_size(
                                                                            partition_usage.free),
                                                                        'percent':
                                                                            f'{partition_usage.percent}'}

    for interface_name, interface_address in psutil.net_if_addrs().items():
        if interface_name == 'Loopback Pseudo-Interface 1':
            continue
        else:
            if 'net_info' not in collect_info_dict['info']:
                collect_info_dict['info']['net_info'] = dict()
            if interface_name not in collect_info_dict['info']['net_info']:
                collect_info_dict['info']['net_info'][interface_name] = dict()
                collect_info_dict['info']['net_info'][interface_name] = {
                    'mac': interface_address[0].address.replace("-", ":"),
                    'ipv4': interface_address[1].address,
                    'ipv6': interface_address[2].address}

    return collect_info_dict


def print_info(dict_info):
    for item in dict_info['info']:
        if item == "system_info":
            for elem in dict_info['info'][item]:
                if elem == 'system':
                    print(f"[+] Информация о системе\n"
                          f"\t- Имя компьютера: {dict_info['info'][item][elem]['comp_name']}\n"
                          f"\t- Опереционная система: {dict_info['info'][item][elem]['os_name']}\n"
                          f"\t- Сборка: {dict_info['info'][item][elem]['version']}\n"
                          f"\t- Архитектура: {dict_info['info'][item][elem]['machine']}\n")
                if elem == 'processor':
                    print(f"[+] Информация о процессоре\n"
                          f"\t- Семейство: {dict_info['info'][item][elem]['name']}\n"
                          f"\t- Физические ядра: {dict_info['info'][item][elem]['phisycal_core']}\n"
                          f"\t- Всего ядер: {dict_info['info'][item][elem]['all_core']}\n"
                          f"\t- Максимальная частота: {dict_info['info'][item][elem]['freq_max']}\n")
                if elem == 'ram':
                    print(f"[+] Оперативная память\n"
                          f"\t- Объем: {dict_info['info'][item][elem]['volume']}\n"
                          f"\t- Доступно: {dict_info['info'][item][elem]['aviable']}\n"
                          f"\t- Используется: {dict_info['info'][item][elem]['used']}\n")
        if item == "disk_info":
            for elem in dict_info['info'][item]:
                print(f"[+] Информация о дисках\n"
                      f"\t- Имя диска: {elem}\n"
                      f"\t- Файловая система: {dict_info['info'][item][elem]['file_system']}\n"
                      f"\t- Объем диска: {dict_info['info'][item][elem]['size_total']}\n"
                      f"\t- Занято: {dict_info['info'][item][elem]['size_used']}\n"
                      f"\t- Свободно: {dict_info['info'][item][elem]['size_free']}\n"
                      f"\t- Заполненность: {dict_info['info'][item][elem]['percent']}%\n")
        if item == "net_info":
            for elem in dict_info['info'][item]:
                print(f"[+] Информация о сети\n"
                      f"\t- Имя интерфейса: {elem}\n"
                      f"\t- MAC-адрес: {dict_info['info'][item][elem]['mac']}\n"
                      f"\t- IPv4: {dict_info['info'][item][elem]['ipv4']}\n"
                      f"\t- IPv6: {dict_info['info'][item][elem]['ipv6']}\n")


def main():
    if uname().system == "Windows":
        dict_info = creating_file()
        with open(f'info_{uname().node}.json', 'w', encoding='utf-8') as file:
            json.dump(dict_info, file, indent=4, ensure_ascii=False)
        print_info(dict_info)
    elif uname().system == "Linux":
        dict_info = creating_file()
        with open(f'info_{uname().node}.json', 'w', encoding='utf-8') as file:
            json.dump(dict_info, file, indent=4, ensure_ascii=False)
        print_info(dict_info)


if __name__ == "__main__":
    main()

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

Спасибо за внимание. Надеюсь, что данный код будет кому-нибудь полезен
 

Johan Van

Green Team
13.06.2020
358
681
BIT
328
Вот с лицензионным ключиком будет интересно;)

Ну да. Довольно интересная часть ) На самом деле лицензия храниться в реестре. Но не в открытом виде, а в слегка "зашифрованном". Есть даже скрипты на PowerShell и vbs, которые ее оттуда выдергивают, рассчитывают всяческие смещения, по которым она размазана и выдают. А код, который добрый человек собрал в модуль - это просто повторение данного расчета. Чисто практически можно сделать самостоятельно. Но, зачем изобретать то, что уже сделано, верно ))
 
  • Нравится
Реакции: Shihskauskas
Мы в соцсетях:

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