• Познакомьтесь с пентестом веб-приложений на практике в нашем новом бесплатном курсе

    «Анализ защищенности веб-приложений»

    🔥 Записаться бесплатно!

  • CTF с учебными материалами Codeby Games

    Обучение кибербезопасности в игровой форме. Более 200 заданий по Active Directory, OSINT, PWN, Веб, Стеганографии, Реверс-инжинирингу, Форензике и Криптографии. Школа CTF с бесплатными курсами по всем категориям.

Статья Изменение MAC-адреса в Linux и Windows с помощью Python

В сети полно статей об изменении MAC-адреса в операционных системах Linux. И, к моему удивлению, я почти не нашел информации о том, как изменять MAC-адрес с помощью Python в операционной системе Windows. Конечно же, может быть, плохо искал. Но, дело не в этом. Оказывается, все не так просто и банально, как представлялось. Казалось бы, чего проще, выполнить с помощью subprocess команду и дело в шляпе. Но, давайте по порядку.

000.jpg

Скрипт по изменению MAC-адреса под Windows нужно запускать в командной строке, запущенной от имени администратора. А в Linux под суперпользователем. Если же этот скрипт запустить от обычного пользователя, в доступе для изменения MAC-адреса будет отказано.

Начать нужно с того, что изменение MAC-адреса в Windows происходит с изменением или созданием соответствующего ключа реестра у того сетевого адаптера, который является активным на данный момент. Но, как программно определить, какой именно адаптер является активным? Проще всего отправить запрос и в зависимости от полученного ответа определять активный адаптер по умолчанию.

Как раз таки с этим проблем особо не возникло. На помощь пришел модуль getmac. Устанавливается он командой:

pip install getmac

На данном этапе я говорю об определении активного сетевого адаптера, а также смене MAC-адреса под операционной системой Windows, чтобы не возникло путаницы. С помощью модуля getmac можно получить MAC-адрес адаптера, который является активным в настоящий момент. Далее, будем использовать командную строку. Получаем данные о всех сетевых адаптерах, которые есть в системе с помощью запроса, отправленного subprocess.check_output. Запрос будет иметь вид: getmac /FO csv /NH /V. Почему именно csv? Мне показалось и, так оно было на самом деле, что вычленить данные здесь будет проще в связи с тем, что присутствуют разделители в виде запятых.

Вот результат выполнения запроса на разных машинах с Windows:

screenshot1.png

Почему-то у меня не особо получилось поработать с данными из запроса напрямую, все время получалась какая-то ерунда. Пришлось сохранять запрос во временный текстовый файл, считывать оттуда данные построчно и уже работать с ними. После этого временный файл удаляется, чтобы не мусорить в директории. Отсюда, с помощью условий мы можем получить имя сетевого интерфейса. Для этого я сделал отдельную функцию def get_interface_win().

Python:
def get_interface_win():
    com = 'getmac /FO csv /NH /V'.split()
    address = getmac.getmac.get_mac_address().replace(":", "-").upper()
    interface_temp = subprocess.check_output(com, shell=False).decode('cp866')
    with open('temp.txt', 'w') as file:
        file.write(interface_temp)
    with open('temp.txt') as file:
        src = file.readlines()
    os.remove('temp.txt')
    for line in src:
        if address in line:
            return line.split(",")[0].replace('"', ""), line.split(",")[-1].replace('"', "").split("_")[-1].strip()

А теперь по порядку. Вот здесь com = 'getmac /FO csv /NH /V'.split() переменной присваивается строковое значение с запросом, после чего оно разделяется на части по пробелам. Это нужно для того, чтобы передать параметры в запрос subprocess.check_output.

Далее мы получаем MAC-адрес: address = getmac.getmac.get_mac_address().replace(":", "-").upper() с помощью функции get_mac_address() модуля getmac и переводим его в верхний регистр. Это нужно для того, чтобы выполнить поиск, так как адрес в csv содержится именно в верхнем регистре.

Далее делаем запрос и получаем данные, которые декодируем в понятный для чтения язык, так как русские буквы читаются некорректно: interface_temp = subprocess.check_output(com, shell=False).decode('cp866').

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


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

Изначально я ошибочно считал, что активный сетевой адаптер почти всегда находиться по пути реестра: HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0001. Но, я ошибался. Хотя, только лишь частично. Дело в том, что в этой папке хранятся параметры того сетевого адаптера, который использовался по умолчанию во время установки операционной системы. Это в том случае, если у вас был интернет. Если у вас интернет был в это время отключен, а сетевой адаптер после использовался какой-то другой, то вполне вероятно, будет создана еще одна ветка реестра, к примеру 0012, где и будут находиться параметры адаптера по умолчанию.

Поэтому нужно прочитать все ветки реестра, которые находятся в ключе {4D36E972-E325-11CE-BFC1-08002BE10318}. А там может находиться очень много веток. Конечно же, тут все зависит от того, сколько у вас сетевых адаптеров, устанавливали ли вы после этого виртуальные машины или еще какие-то программы, которые, например, создают свой виртуальный адаптер.

Вот скрин моей ветки реестра, где создано аж 12 веток с параметрами:

screenshot2.png

И вот вопрос, как теперь понять, в какой из веток находиться текущий адаптер. Получается, что нужно по очереди подключить все ветки 0001, 0002 и далее, пробежаться по их параметрам, найти параметр NetCfgInstanceId, в котором храниться ID, прочитать значение параметра, сравнить его с тем, что мы получили из запроса. И если это значение совпадает, то вернуть имя ветки для того, чтобы использовать его в запросе на изменение MAC-адреса активного адаптера. Я под это сделал отдельную функцию: def get_key_reg_win(). На вход она ничего не получает, а возвращает имя сетевого интерфейса, которое было получено из предыдущего запроса и ветку реестра, в которой нужно производить действия с адресом.

Для доступа к реестру используем библиотеку, которая устанавливается вместе с питоном winreg, поэтому нужно просто ее импортировать прямо в функции. Если эту библиотеку импортировать глобально, то Linux начинает ругаться, что такой библиотеки нет. А при попытке установить ее посылает куда подальше, просто потому, что для Linux данная библиотека бесполезна и потому реализации под него нет. И да, эта библиотека именно под Windows, то есть, код для поиска надо писать в этой ОС.

Python:
def get_key_reg_win():
    from winreg import ConnectRegistry, HKEY_LOCAL_MACHINE, OpenKey, EnumValue
    devicekey = get_interface_win()[1]
    interface = get_interface_win()[0]
    aKey_dict = [r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0000',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0001',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0002',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0003',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0004',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0005',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0006',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0007',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0008',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0009',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0010',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0011',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0012',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0013',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0014',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0015',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0016',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0017',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0018',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0019',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0020',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0021',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0022',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0023',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0024'
                 ]
    aReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
    for key in aKey_dict:
        try:
            aKey = OpenKey(aReg, key)
            for n in range(0, 100):
                keyname = EnumValue(aKey, n)
                if keyname[0] == 'NetCfgInstanceId':
                    if keyname[1] == devicekey:
                        return interface, key.split("\\")[-1]
                    break
        except:
            continue

Для начала получаем имя сетевого интерфейса и ID сетевого адаптера. Дальше я просто сделал список с полным путем для получения значения. Изначально я использовал цикл for, для того, чтобы подставлять конечные значения. Но, в этом случае данная функция неоправданно усложнялась, так как нужно было еще и проверять, какая цифра сейчас используется, чтобы соответственно сократить запрос на один 0. А если еще добавить сюда комбинацию try – except, то функция становиться неудобоваримой. Тем более, при необходимости количество элементов списка можно увеличить. Но, я думаю, что для большинства задач данного количества элементов будет достаточно.

Далее устанавливаем соединение с веткой реестра. И запускаем цикл по списку. По очереди открываем все ветки, перебираем параметры, которые возвращаются в виде кортежей. И сравниваем их с полученным ранее ID сетевого адаптера. Если параметры совпадают, возвращаем название сетевого интерфейса и ветку реестра, куда будем вносить изменения. После чего прерываем цикл. Почему в цикле значение 100? Я тоже удивился, когда не мог получить значение по индексу ключа из параметров. И что только я не пробовал. Тем не менее, на физической машине все отрабатывало без проблем, а на виртуалке скрипт выполнялся, но ничего не возвращал. И в один прекрасный момент я просто увеличил циферку в цикле. И о чудо, параметры появились. Вот потому и такая цифра. Пути винды неисповедимы :) И хранятся параметры в реестре не в том порядке, в каком мы их видим в редакторе, а в каком-то своем, особом хаосе :)


Изменение MAC-адреса в Windows

Создадим теперь функцию, которая и меняет значение MAC-адреса в Windows: def change_mac_windows(new_mac, interface, key_reg). На вход она принимает новый мак адрес, который ввел пользователь, название сетевого интерфейса и ключ, по которому нужно создать ключ со значением MAC-адреса.

Python:
def change_mac_windows(new_mac, interface, key_reg):
    print(f"[+] Changing MAC address for {interface} to {new_mac}")
    com_reg = r'reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{' \
              r'4D36E972-E325-11CE-BFC1-08002BE10318}' + f'\\{key_reg} /v NetworkAddress /d ' + f'{new_mac} /f '
    subprocess.call(com_reg, shell=True)
    subprocess.call(f'netsh interface set interface "{interface}" disabled', shell=True)
    subprocess.call('ping -n 6 127.0.0.1 >nul', shell=True)
    subprocess.call(f'netsh interface set interface "{interface}" enabled', shell=True)
    subprocess.call('ping -n 8 127.0.0.1 >nul', shell=True)

Ну, здесь то все достаточно просто. Сначала выводим принт, что будем сейчас менять адрес. Формируем строку с путем к нужному ключу реестра. И дальше выполняем команду по изменению ключа. После чего отключаем сетевой интерфейс, выдерживаем с помощью пинга паузу в 5 секунд, затем снова включаем и снова выдерживаем паузу. Паузы нужны для того, чтобы сетевой интерфейс успел отключиться и включиться. Так как в Windows это занимает некоторое время. А не мгновенно, как в Linix. На этом, в этой функции всё )

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

И еще немного, для информации. MAC-адрес в Windows передается в запросе на изменение не в том же виде, что и в Linux. Они немного отличаются наличием и отсутствием двоеточия. К примеру, если вы вводите в Linux MAC-адрес для изменения в виде: 00:11:22:33:44:55, то в Windows это будет выглядеть на этапе изменения: 001122334455. То есть, без двоеточия и тире. Это после, уже в системе ОС сама делит полученное значение на пары и ставит между ними тире. И то, думаю, что это лишь в выводе, для удобства пользователя. То есть, в выводе Windows MAC-адрес выглядит вот так: 00-11-22-33-44-55.


Определение операционной системы

Следующая функция, это функция определения операционной системы: def os_get(new_macs). В нее мы передаем только адрес, который ввел пользователь для изменения. Затем, с помощью функции getmac.getmac.LINUX, которая возвращает True, если ОС Linux, заменяем, на всякий случай тире на двоеточие, мало ли. Вдруг будут. Печатаем принт о текущем MAC-адресе. А далее вызываем функцию смены адреса. По ее завершении снова получаем MAC-адрес из системы и сравниваем с тем, что вводил пользователь. Если адреса совпадают – БИНГО!. MAC-адрес изменен успешно, о чем и сообщаем пользователю. Ну, а нет, так нет. Как говориться – не шмогла, так не шмогла. Бывает :ROFLMAO:

Python:
def os_get(new_macs):
    if getmac.getmac.LINUX:
        new_mac = new_macs.replace("-", ":")
        print(f'[+] Current MAC:{getmac.getmac.get_mac_address()}')
        change_mac_linux(new_mac)
        if getmac.getmac.get_mac_address() == new_mac:
            print(f'[+] MAC address was successfully changed to: {new_mac}')
        else:
            print('[-] MAC address did not get changed')
    if getmac.getmac.WINDOWS:
        new_mac = new_macs.replace(":", "").replace("-", "")
        intkey = get_key_reg_win()
        print(f'[+] Current MAC: {getmac.getmac.get_mac_address()}')
        change_mac_windows(new_mac, intkey[0], intkey[1])
        if getmac.getmac.get_mac_address() == new_macs.lower():
            print(f'[+] MAC address was successfully changed to: {new_mac}')
        else:
            print('[-] MAC address did not get changed')

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

Вот, собственно и все. Осталась только лишь функция main(), в которой вызываем функцию определения операционной системы и передаем в качестве параметров пользовательский ввод.

Python:
def main():
    os_get(input('Input Mac to Change: '))

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

Python:
import subprocess
import os

import getmac


# получение названия сетевого интерфейса Windows
def get_interface_win():
    com = 'getmac /FO csv /NH /V'.split()
    address = getmac.getmac.get_mac_address().replace(":", "-").upper()
    interface_temp = subprocess.check_output(com, shell=False).decode('cp866')
    with open('temp.txt', 'w') as file:
        file.write(interface_temp)
    with open('temp.txt') as file:
        src = file.readlines()
    os.remove('temp.txt')
    for line in src:
        if address in line:
            return line.split(",")[0].replace('"', ""), line.split(",")[-1].replace('"', "").split("_")[-1].strip()


# получение раздела реестра, где располагаются параметры сетевого интерфейса
# используемого по умолчанию
def get_key_reg_win():
    from winreg import ConnectRegistry, HKEY_LOCAL_MACHINE, OpenKey, EnumValue
    devicekey = get_interface_win()[1]
    interface = get_interface_win()[0]
    aKey_dict = [r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0000',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0001',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0002',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0003',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0004',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0005',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0006',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0007',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0008',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0009',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0010',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0011',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0012',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0013',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0014',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0015',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0016',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0017',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0018',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0019',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0020',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0021',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0022',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0023',
                 r'SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0024'
                 ]
    aReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
    for key in aKey_dict:
        try:
            aKey = OpenKey(aReg, key)
            for n in range(0, 100):
                keyname = EnumValue(aKey, n)
                if keyname[0] == 'NetCfgInstanceId':
                    if keyname[1] == devicekey:
                        return interface, key.split("\\")[-1]
                    break
        except:
            continue


# изменение MAC-адреса Windows
def change_mac_windows(new_mac, interface, key_reg):
    print(f"[+] Changing MAC address for {interface} to {new_mac}")
    com_reg = r'reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{' \
              r'4D36E972-E325-11CE-BFC1-08002BE10318}' + f'\\{key_reg} /v NetworkAddress /d ' + f'{new_mac} /f '
    subprocess.call(com_reg, shell=True)
    subprocess.call(f'netsh interface set interface "{interface}" disabled', shell=True)
    subprocess.call('ping -n 6 127.0.0.1 >nul', shell=True)
    subprocess.call(f'netsh interface set interface "{interface}" enabled', shell=True)
    subprocess.call('ping -n 6 127.0.0.1 >nul', shell=True)


# изменение MAC-адреса Linux
def change_mac_linux(new_mac):
    interface = getmac.getmac._get_default_iface_linux()
    print(f"[+] Changing MAC address for {interface} to {new_mac}")
    subprocess.call(f"ifconfig {interface} down", shell=True)
    subprocess.call(f"ifconfig {interface} hw ether {new_mac}", shell=True)
    subprocess.call(f"ifconfig {interface} up", shell=True)


# определение ОС, получение параметров и вызов функции для изменения MAC
def os_get(new_macs):
    if getmac.getmac.LINUX:
        new_mac = new_macs.replace("-", ":")
        print(f'[+] Current MAC:{getmac.getmac.get_mac_address()}')
        change_mac_linux(new_mac)
        if getmac.getmac.get_mac_address() == new_mac:
            print(f'[+] MAC address was successfully changed to: {new_mac}')
        else:
            print('[-] MAC address did not get changed')
    if getmac.getmac.WINDOWS:
        new_mac = new_macs.replace(":", "").replace("-", "")
        intkey = get_key_reg_win()
        print(f'[+] Current MAC: {getmac.getmac.get_mac_address()}')
        change_mac_windows(new_mac, intkey[0], intkey[1])
        if getmac.getmac.get_mac_address() == new_macs.lower():
            print(f'[+] MAC address was successfully changed to: {new_mac}')
        else:
            print('[-] MAC address did not get changed')


# получение MAC для изменения и вызов функции определения ОС
def main():
    os_get(input('Input Mac to Change: '))


if __name__ == "__main__":
    main()

Что же, на этом можно завершить данную статью. Поставленной задачи я добился. Код был протестирован на паре машин. Реальной и виртуальной. Причем, у виртуальной машины я добавил штук пять адаптеров и по очереди их включал и выключал, чтобы посмотреть, как отработает код. Вроде бы, пока все работало корректно.

И да, вот результат работы скрипта в Windows:

screenshot3.png

И в Linux Mint:

screenshot4.png

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна.
 
Последнее редактирование:
Мы в соцсетях:

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