Статья Сканирование портов с помощью Scapy. Трехстороннее рукопожатие в Python

Сканирование сети, это всегда полезная и нужная задача, которую нужно выполнить в первую очередь на любой тестируемой системе. Делать это можно по-разному. Иногда достаточно вывода команды «arp –n» и соответственно просмотра ip и mac адресов, но желательно получить как можно больше информации об открытых портах на удаленном компьютере. Также хотелось бы знать, какие службы могут быть запущены на этих портах без помощи заглядывания в справочники, пусть и в интернете. Это может упростить понимание того, какая операционная система используется. Хотя и не в полной мере, тем не менее, общую картину сети это даст. И тут уже нужно использовать специализированые инструменты. А что, если эти инструменты мы создадим сами, используя Python? Ну, или хотя бы, попытаемся.

000.jpg

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

Именно про сканер ip и mac адресов я рассказывать здесь не буду. О нем я писал вот в этой статье. В принципе, ничего из используемых инструментов в нем не поменялось. Я только убрал функцию определения ip-адреса, так как она была лишней, а для определения ip-адреса воспользовался функцией scapy:

sc.conf.route.route("0.0.0.0")[1]

которая, помимо локального ip-адреса возвращает также и адрес шлюза по умолчанию. Как несложно догадаться, здесь просто парсится локальная таблица маршрутизации. А также добавил функцию определения маски подсети, которая использует парсинг команды Linux «ip -h -br a | grep UP». Ведь кто знает, какая именно маска подсети будет задана. Поэтому, не помешает ее определить, для более корректного сканирования.

Python:
# получение маски сети
def get_net_mask_linx():
    net_mask = str(check_output('ip -h -br a  | grep UP', shell=True).decode()).split()[2].split("/")[1]
    return net_mask

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


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

Для начала, операционная система Linux. В сканировании сетей и различного рода выполнении операций в них я пока на Windows решил не проводить. Чтобы не дублировать один и тот же код в различных вариациях. Далее, если вы используете операционную систему не Kali Linux, то вам потребуется установить пакет nbtscan для получения NetBIOS-имен компьютеров в сети, если таковые у них имеются. Установка происходит с помощью команды в терминале:

sudo apt install nbtscan

Если вы не пользовались раньше библиотекой scapy, вам также нужно будет ее установить. Пишем в терминале команду:

pip install --pre scapy[basic]

И да, если вы не программировали на Linux, то, скорее всего у вас pip не установлен. Установить его можно командой:

sudo apt install python3-pip

И еще одна библиотека, уже для вывода данных в терминал в удобочитаемом виде rich. Для ее установки напишите в терминале:

pip install rich

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

Python:
import os
import time
from socket import gaierror
from subprocess import check_output, CalledProcessError

import scapy.all as sc
from rich.console import Console
from rich.progress import Progress
from rich.table import Table
from rich.text import Text

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

result = dict()


Функция сканирования портов

Теперь напишем функцию syn_ack_scan(ip, ports), которая на входе принимает ip-адрес удаленного компьютера, а также порт или кортеж из портов, который будет являться диапазоном для сканирования. В данном скрипте сканируется только системные порты от 1 до 1024-го. Так как сканировать компьютер на наличие неизвестного порта, на котором работает не совсем понятно что, такое себе занятие. Впрочем, при желании можно изменить диапазон. Однако здесь он является заданным по умолчанию и не зависит от пользовательского ввода.

Python:
# сканирование с помощью TCP пакетов на открытые порты
def syn_ack_scan(ip, ports):
    # создание пакета для сканирование
    try:
        request_syn = sc.IP(dst=ip) / sc.TCP(dport=ports, flags="S")
    except gaierror:
        raise ValueError(f'{ip} получить не удалось')
    answer = sc.sr(request_syn, timeout=2, retry=1, verbose=False)[0]  # отправка пакета

    # добавление полученных значений в словарь
    for send, receiv in answer:
        if receiv['TCP'].flags == "SA":
            try:
                if str(receiv['IP'].src) not in result:
                    result[str(receiv['IP'].src)] = dict()
                if str(receiv['TCP'].sport) not in result[str(receiv['IP'].src)]:
                    result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = dict()
                if str(sc.TCP_SERVICES[receiv['TCP'].sport]) not in result[str(receiv['IP'].src)] \
                        [str(receiv['TCP'].sport)]:
                    result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = str(sc.TCP_SERVICES[receiv['TCP'].sport])
            except KeyError:
                result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = 'Undefined'

А теперь немного пояснений. Для начала сформируем пакет, который будет использовать слой TCP поверх слоя IP. sc.IP(dst=ip) / sc.TCP(dport=ports, flags="S"). В этой строке, в протоколе IP, dst=ip задает IP-адрес, по которому будет отправлен пакет. В протоколе TCP, dport=ports – это порт или диапазон портов для отправки пакета. И флаг flags="S" указывает на то, что это SYN запрос. То есть, первый из пакетов трехстороннего рукопожатия.

Возможно, тут нужно отвлечься немного на теорию. В общем виде происходит вот что. При установке TCP-соединения клиент и сервер обмениваются серией сообщений. Клиент отправляет SYN-сообщение серверу, но что тот отвечает SYN-ACK-сообщением, которое подтверждает запрос от клиента. Далее клиент снова отправляет сообщение серверу, но уже ACK, и устанавливает соединение. Сообщение отправляется на определенный порт, на котором работает сервис нужный клиенту. Если же от сервера приходит флаг RST, то попытки соединения прекращаются, так как сервер отвечает, что порт закрывается для соединений, а потому в ответ ничего отправлено не будет.

В данном коде используются только первые две части трехстороннего рукопожатия. То есть, мы отправляем запрос компьютеру на определенный порт, фильтруем ответ. Если он SA, то есть SYN-ACK, значит, порт открыт и готов к соединению. После чего заносим полученные данные в словарь.

С помощью функции scapy.sr отправляем пакет. Данная функция не только отправляет сообщение, но также ждет ответа от удаленного компьютера. Параметр timeout=2 означает, что нужно ждать две секунды перед тем, как сбросить соединение, а параметр retry=1 указывает на количество повторов, то есть, в случае не ответа, сколько раз отправить сообщение повторно, перед тем как совсем переключится на другой порт. А параметр verbose=False позволяет не выводить сообщения об отправке пакетов в терминал. Потому, как выглядит оно не особо эстетично.

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


Получение NetBIOS-имени

Теперь давайте напишем функцию netbios_check(ip), которая будет проверять на основе полученного ip-адреса, NetBIOS имя удаленного компьютера, если такое у него имеется. Тут все просто. Получаем ip-адрес, запускаем команду с помощью subprocess и выводим полученные значения в переменную. Возвращаемых значений здесь два. Это IP-адрес и NetBIOS имя. Адрес у нас уже есть, а вот имя забираем и возвращаем туда, откуда была запущена функция.

Python:
# получение NETBios-имени компьютеров
def netbios_check(ip):
    try:
        nb = check_output(f'nbtscan {ip} -e', shell=True).decode().split()
    except CalledProcessError:
        return
    try:
        nb_name = nb[1]
    except IndexError:
        return
    return nb_name


Печатаем полученные значения

Теперь функция печати. Ее мне пришлось перетряхнуть основательно, так как надо было печатать не только IP и MAC адреса, но также и другие полученные значения в привязке к ним. Я создал функцию print_port(dict_netbios, ip_mac_network), которая получает словари со значениями. Третий словарь со значениями открытых портов передавать не надо, так как он является глобальным.

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

Python:
list_data_table = []
    table = Table(title='"Network Information (IP, MAC, NetBIOS-Name). Open Port Range (1-1024): "',
                  title_justify='left')
    table.add_column("IP", no_wrap=False, justify="left", style="green")
    table.add_column("MAC", no_wrap=False, justify="left", style="green")
    table.add_column("Ports", no_wrap=False, justify="left", style="green")
    table.add_column("NB-Name", no_wrap=False, justify="left", style="green")

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

Python:
for ip in ip_mac_network:
        list_data_table.append(ip['ip'])
        list_data_table.append(ip['mac'])
        if ip['ip'] in result:
            list_data_table.append(str(result[ip['ip']]).replace("': '", "/").replace("{", "[").replace("}","]"))
        else:
            list_data_table.append(" --- ")
        if ip['ip'] in dict_netbios:
            list_data_table.append(dict_netbios[ip['ip']])
        else:
            list_data_table.append(" --- ")

После этого добавляем значения из сформированного списка в таблицу:

table.add_row(list_data_table[0], list_data_table[1], list_data_table[2], list_data_table[3])

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

list_data_table = []

Ну и выводим всю красоту на печать:

Python:
    console = Console()
    print(' ')
    console.print(table)

Python:
# печать полученных данных (IP, MAC, NetBIOS-Name, Open Port)
def print_port(dict_netbios, ip_mac_network):
    list_data_table = []
    table = Table(title='"Network Information (IP, MAC, NetBIOS-Name). Open Port Range (1-1024): "',
                  title_justify='left')
    table.add_column("IP", no_wrap=False, justify="left", style="green")
    table.add_column("MAC", no_wrap=False, justify="left", style="green")
    table.add_column("Ports", no_wrap=False, justify="left", style="green")
    table.add_column("NB-Name", no_wrap=False, justify="left", style="green")

    for ip in ip_mac_network:
        list_data_table.append(ip['ip'])
        list_data_table.append(ip['mac'])
        if ip['ip'] in result:
            list_data_table.append(str(result[ip['ip']]).replace("': '", "/").replace("{", "[").replace("}","]"))
        else:
            list_data_table.append(" --- ")
        if ip['ip'] in dict_netbios:
            list_data_table.append(dict_netbios[ip['ip']])
        else:
            list_data_table.append(" --- ")

        table.add_row(list_data_table[0], list_data_table[1], list_data_table[2], list_data_table[3])
        list_data_table = []
    console = Console()
    print(' ')
    console.print(table)


Запуск сканирования сети и портов

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

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

Python:
    # проверка прав пользователя
    if not os.getuid() == 0:
        console = Console()
        text = Text("\n [+] Run the script as root user!")
        text.stylize("bold red")
        console.print(text)
        return

Если же все хорошо и рут найден, получаем IP-адрес и запускаем функцию сканирования ip и mac адресов, которая возвращает словарь с полученными значениями:

Python:
# получение IP- и MAC-адресов машин в сети
    local_ip = sc.conf.route.route("0.0.0.0")[1]
    ip_mac_network = get_ip_mac_nework(f'{local_ip}/{get_net_mask_linx()}')

Запускаем сканирование портов. Сюда я добавил прогресс-бар, чтобы все отображалось в интерактивном режиме и, было понятно, что скрипт не завис, а что-то делает. На основании количества полученных значений в словаре с ip и mac адресами, устанавливается значение ползунка у прогресс-бара. Ну и по мере выполнения цикла обновляет свое значение, а также показывает предположительное время до завершения процесса.

Python:
# сканирование открытых портов
    print(f'\n\n[+] Network scanning:\n{"-" * 21}')
    netbios_dict = {}
    with Progress() as progress:
        task = progress.add_task("[green]Scaning...", total=len(ip_mac_network))
        for ip in ip_mac_network:
            syn_ack_scan(ip["ip"], (1, 1024))
            name = netbios_check(ip["ip"])
            if name:
                netbios_dict[ip["ip"]] = name
            progress.update(task, advance=1)

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

Python:
    # печать полученных данных
    console = Console()
    print_port(netbios_dict, ip_mac_network)
    text = Text(f'\n [+] Local IP: {local_ip}    [+] Local Gateway: {sc.conf.route.route("0.0.0.0")[2]}\n')
    text.stylize("bold")
    console.print(text)

    text = Text(f' [-] Scan time: {time.monotonic() - start}')
    text.stylize("green")
    console.print(text)

Локальный IP и шлюз я теперь печатаю под таблицей.

Python:
# sudo apt install nbtscan
# sudo apt install python3-pip
# pip install --pre scapy[basic]
# pip install rich

import os
import time
from socket import gaierror
from subprocess import check_output, CalledProcessError

import scapy.all as sc
from rich.console import Console
from rich.progress import Progress
from rich.table import Table
from rich.text import Text

result = dict()


# сканируем сеть, получаем ip и mac сетевых машин
def get_ip_mac_nework(ip):
    answered_list = sc.srp(sc.Ether(dst='ff:ff:ff:ff:ff:ff') / sc.ARP(pdst=ip), timeout=1, verbose=False)[0]
    clients_list = []
    for element in answered_list:
        clients_list.append({'ip': element[1].psrc, 'mac': element[1].hwsrc})
    return clients_list


# получение маски сети
def get_net_mask_linx():
    net_mask = str(check_output('ip -h -br a  | grep UP', shell=True).decode()).split()[2].split("/")[1]
    return net_mask


# сканирование с помощью TCP пакетов на открытые порты
def syn_ack_scan(ip, ports):
    # создание пакета для сканирование
    try:
        request_syn = sc.IP(dst=ip) / sc.TCP(dport=ports, flags="S")
    except gaierror:
        raise ValueError(f'{ip} получить не удалось')
    answer = sc.sr(request_syn, timeout=2, retry=1, verbose=False)[0]  # отправка пакета

    # добавление полученных значений в словарь
    for send, receiv in answer:
        if receiv['TCP'].flags == "SA":
            try:
                if str(receiv['IP'].src) not in result:
                    result[str(receiv['IP'].src)] = dict()
                if str(receiv['TCP'].sport) not in result[str(receiv['IP'].src)]:
                    result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = dict()
                if str(sc.TCP_SERVICES[receiv['TCP'].sport]) not in result[str(receiv['IP'].src)] \
                        [str(receiv['TCP'].sport)]:
                    result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = str(sc.TCP_SERVICES[receiv['TCP'].sport])
            except KeyError:
                result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = 'Undefined'


# получение NETBios-имени компьютеров
def netbios_check(ip):
    try:
        nb = check_output(f'nbtscan {ip} -e', shell=True).decode().split()
    except CalledProcessError:
        return
    try:
        nb_name = nb[1]
    except IndexError:
        return
    return nb_name


# печать полученных данных (IP, MAC, NetBIOS-Name, Open Port)
def print_port(dict_netbios, ip_mac_network):
    list_data_table = []
    table = Table(title='"Network Information (IP, MAC, NetBIOS-Name). Open Port Range (1-1024): "',
                  title_justify='left')
    table.add_column("IP", no_wrap=False, justify="left", style="green")
    table.add_column("MAC", no_wrap=False, justify="left", style="green")
    table.add_column("Ports", no_wrap=False, justify="left", style="green")
    table.add_column("NB-Name", no_wrap=False, justify="left", style="green")

    for ip in ip_mac_network:
        list_data_table.append(ip['ip'])
        list_data_table.append(ip['mac'])
        if ip['ip'] in result:
            list_data_table.append(str(result[ip['ip']]).replace("': '", "/").replace("{", "[").replace("}","]"))
        else:
            list_data_table.append(" --- ")
        if ip['ip'] in dict_netbios:
            list_data_table.append(dict_netbios[ip['ip']])
        else:
            list_data_table.append(" --- ")

        table.add_row(list_data_table[0], list_data_table[1], list_data_table[2], list_data_table[3])
        list_data_table = []
    console = Console()
    print(' ')
    console.print(table)


def main():
    start = time.monotonic()
    # проверка прав пользователя
    if not os.getuid() == 0:
        console = Console()
        text = Text("\n [+] Run the script as root user!")
        text.stylize("bold red")
        console.print(text)
        return
    # получение IP- и MAC-адресов машин в сети
    local_ip = sc.conf.route.route("0.0.0.0")[1]
    ip_mac_network = get_ip_mac_nework(f'{local_ip}/{get_net_mask_linx()}')

    # сканирование открытых портов
    print(f'\n\n[+] Network scanning:\n{"-" * 21}')
    netbios_dict = {}
    with Progress() as progress:
        task = progress.add_task("[green]Scaning...", total=len(ip_mac_network))
        for ip in ip_mac_network:
            syn_ack_scan(ip["ip"], (1, 1024))
            name = netbios_check(ip["ip"])
            if name:
                netbios_dict[ip["ip"]] = name
            progress.update(task, advance=1)

    # печать полученных данных
    console = Console()
    print_port(netbios_dict, ip_mac_network)
    text = Text(f'\n [+] Local IP: {local_ip}    [+] Local Gateway: {sc.conf.route.route("0.0.0.0")[2]}\n')
    text.stylize("bold")
    console.print(text)

    text = Text(f' [-] Scan time: {time.monotonic() - start}')
    text.stylize("green")
    console.print(text)


if __name__ == "__main__":
    main()


Проверка работы сканера

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

Ну и вот моя тестовая сеть, просканированная с помощью данного сканера:

screenshot1.png

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

А вот так выглядит вывод из другого Linux на тестовом стенде:

screenshot2.png

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

Видео-иллюстрация работы сканера:


А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезной.
 
  • Нравится
Реакции: Notsaint, debolg и puni359
17.06.2022
6
0
Добрый день!
А подскажите, пожалуйста, по первой функции "сканирование с помощью TCP пакетов на открытые порты". Что конкретно планируется перехватывать этим эксепшеном?
Python:
    except gaierror:

        raise ValueError(f'{ip} получить не удалось')

И в каких случаях будет возникать ошибка, обрабатываемая тут?
Python:
            except KeyError:

                result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = 'Undefined'
 

Johan Van

Green Team
13.06.2020
153
280
Добрый день!
А подскажите, пожалуйста, по первой функции "сканирование с помощью TCP пакетов на открытые порты". Что конкретно планируется перехватывать этим эксепшеном?
Python:
    except gaierror:

        raise ValueError(f'{ip} получить не удалось')

И в каких случаях будет возникать ошибка, обрабатываемая тут?
Python:
            except KeyError:

                result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = 'Undefined'

В первом случае, это ошибка сокета при получении адреса, в этом случае вызываем исключение и сообщаем об ошибке пользователю.
В более полном варианте это будет вот так: socket.gaierror.
Данная ошибка возникает для ошибок, связанных с адресом, в функциях socket.getaddrinfo() и socket.getnameinfo().

Во втором случае ошибка получения ключа в разбираемом json. Если ключа нет, значит присваиваем значение "Undefined".
Если конкретнее, то вот этого ключа:

Python:
str(receiv['TCP'].sport)
 
Последнее редактирование:
  • Нравится
Реакции: Игнатпитон

Johan Van

Green Team
13.06.2020
153
280
Добрый день!
А подскажите, пожалуйста, по первой функции "сканирование с помощью TCP пакетов на открытые порты". Что конкретно планируется перехватывать этим эксепшеном?
Python:
    except gaierror:

        raise ValueError(f'{ip} получить не удалось')

И в каких случаях будет возникать ошибка, обрабатываемая тут?
Python:
            except KeyError:

                result[str(receiv['IP'].src)][str(receiv['TCP'].sport)] = 'Undefined'

На самом деле, я сейчас бы слегка переделал этот сканер. Не в плане функционала, хотя, если углубиться ..., а в плане того, что не стал бы сразу же сканировать сеть. Дал бы возможность пользователю принять решение о сканировании сети самостоятельно и указать диапазон портов, и адресов. Ведь если сеть небольшая, то ладно. Сканирование не займет много времени. А вот если сеть из ста и более машин, тогда это будет на самом деле долго.
 
17.06.2022
6
0
В первом случае, это ошибка сокета при получении адреса, в этом случае вызываем исключение и сообщаем об ошибке пользователю.
В более полном варианте это будет вот так: socket.gaierror.
Данная ошибка возникает для ошибок, связанных с адресом, в функциях socket.getaddrinfo() и socket.getnameinfo().

Не понял, а где в коде именно она возникнет?
Там перед этим эксепшеном мы пытаемся создать пакет
Python:
 request_syn = sc.IP(dst=ip) / sc.TCP(dport=ports, flags="S")
Туда можно вставить и пустой айпишник и любой левый. Ошибка вылезет, если вставить туда нелегитимный, типа из букв состоящий.

Во втором случае ошибка получения ключа в разбираемом json. Если ключа нет, значит присваиваем значение "Undefined".
Если конкретнее, то вот этого ключа:
Имеется ввиду, что если в словаре sc.TCP_SERVICES не будет соответствия запрашиваемому порту, то вылезет ошибка KeyError, которая тут же обрабатывается?
 

Johan Van

Green Team
13.06.2020
153
280
Не понял, а где в коде именно она возникнет?
Там перед этим эксепшеном мы пытаемся создать пакет
Python:
 request_syn = sc.IP(dst=ip) / sc.TCP(dport=ports, flags="S")
Туда можно вставить и пустой айпишник и любой левый. Ошибка вылезет, если вставить туда нелегитимный, типа из букв состоящий.


Имеется ввиду, что если в словаре sc.TCP_SERVICES не будет соответствия запрашиваемому порту, то вылезет ошибка KeyError, которая тут же обрабатывается?

Имеется в виду вот этот ключ: receiv['TCP'].sport
Здесь мы пытаемся получить расшифровку портов, то есть, к примеру 443/https. Так вот если значения для расшифровки не будет в TCP_SERVICES, возникнет исключение, то есть нужно будет обработать отсутствие ключа. В TCP_SERVICES далеко не все порты. А в зависимости от того, какой диапазон для сканирования у вас, вы можете не найти в нем нужной расшифровки.

А про ошибку все верно. Такая же ошибка возникает, когда вы пытаетесь получить имя хоста в интернете. Так же не обрабатывается запрос и возникает исключение. Пакет состоит из двух протоколов, которые так или иначе, работают на сокетах. А следовательно у них ошибки вылезают именно сокетов. Если поискать в интернете, можно найти реализацию сканера чисто на сокетах. Просто scapy взяли, по сути, задачу облегчить составление пакетов. Иначе пришлось бы переводить в hex, кодировать, декодировать ))
 
Последнее редактирование:
Мы в соцсетях: