Сканирование сети, это всегда полезная и нужная задача, которую нужно выполнить в первую очередь на любой тестируемой системе. Делать это можно по-разному. Иногда достаточно вывода команды «arp –n» и соответственно просмотра ip и mac адресов, но желательно получить как можно больше информации об открытых портах на удаленном компьютере. Также хотелось бы знать, какие службы могут быть запущены на этих портах без помощи заглядывания в справочники, пусть и в интернете. Это может упростить понимание того, какая операционная система используется. Хотя и не в полной мере, тем не менее, общую картину сети это даст. И тут уже нужно использовать специализированые инструменты. А что, если эти инструменты мы создадим сами, используя Python? Ну, или хотя бы, попытаемся.
Банально, скажете вы. Сканер сети. Ничего необычного. Ну, в принципе да. Сканировать сети можно по-разному, причем на разных сетевых уровнях и даже используя только лишь одни сокеты. Но, при использовании сокетов нужно четко понимать правила составления пакетов для запросов на разных уровнях, в каком виде эти пакеты приходят в ответе, чтобы получить из них необходимую информацию. И это без сомнения нужно изучать. Но, использовать сокеты, в чистом виде, мы не будем. А для сканирования сети и портов воспользуемся библиотекой scapy.
Именно про сканер ip и mac адресов я рассказывать здесь не буду. О нем я писал вот в этой статье. В принципе, ничего из используемых инструментов в нем не поменялось. Я только убрал функцию определения ip-адреса, так как она была лишней, а для определения ip-адреса воспользовался функцией scapy:
которая, помимо локального ip-адреса возвращает также и адрес шлюза по умолчанию. Как несложно догадаться, здесь просто парсится локальная таблица маршрутизации. А также добавил функцию определения маски подсети, которая использует парсинг команды Linux «ip -h -br a | grep UP». Ведь кто знает, какая именно маска подсети будет задана. Поэтому, не помешает ее определить, для более корректного сканирования.
Ну и на этом, пожалуй, изменения в скрипте из предыдущей статьи закончились. Все остальное используется, так же как и ранее. А теперь давайте посмотрим, что добавилось в новый скрипт и допишем недостающие функции.
Что потребуется?
Для начала, операционная система Linux. В сканировании сетей и различного рода выполнении операций в них я пока на Windows решил не проводить. Чтобы не дублировать один и тот же код в различных вариациях. Далее, если вы используете операционную систему не Kali Linux, то вам потребуется установить пакет nbtscan для получения NetBIOS-имен компьютеров в сети, если таковые у них имеются. Установка происходит с помощью команды в терминале:
Если вы не пользовались раньше библиотекой scapy, вам также нужно будет ее установить. Пишем в терминале команду:
И да, если вы не программировали на Linux, то, скорее всего у вас pip не установлен. Установить его можно командой:
И еще одна библиотека, уже для вывода данных в терминал в удобочитаемом виде rich. Для ее установки напишите в терминале:
Теперь нужно импортировать все, что у нас установлено и, что будет использоваться в процессе работы скрипта.
Приступаем к написанию кода. Для начала определим словарь, в который будем помещать полученные значения. Определим его глобально, чтобы доступ к нему был из любой функции:
Функция сканирования портов
Теперь напишем функцию syn_ack_scan(ip, ports), которая на входе принимает ip-адрес удаленного компьютера, а также порт или кортеж из портов, который будет являться диапазоном для сканирования. В данном скрипте сканируется только системные порты от 1 до 1024-го. Так как сканировать компьютер на наличие неизвестного порта, на котором работает не совсем понятно что, такое себе занятие. Впрочем, при желании можно изменить диапазон. Однако здесь он является заданным по умолчанию и не зависит от пользовательского ввода.
А теперь немного пояснений. Для начала сформируем пакет, который будет использовать слой 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 имя. Адрес у нас уже есть, а вот имя забираем и возвращаем туда, откуда была запущена функция.
Печатаем полученные значения
Теперь функция печати. Ее мне пришлось перетряхнуть основательно, так как надо было печатать не только IP и MAC адреса, но также и другие полученные значения в привязке к ним. Я создал функцию print_port(dict_netbios, ip_mac_network), которая получает словари со значениями. Третий словарь со значениями открытых портов передавать не надо, так как он является глобальным.
Для начала создаем промежуточный список, в который будем добавлять объединенные значения из разных словарей. Создадим таблицу из пакета rich. Добавим заголовок, в котором укажем выравнивание по левому краю. Теперь создадим столбцы таблицы. Я решил их сделать фиксированными для удобства. И запишем заголовки столбцов, а также некоторые параметры, которые позволят переносить длинный текст на другую строку, выравнивать в столбце значения по левому краю и собственно цвет значений в таблице.
Дальше наполняем список. Тут ничего необычного, просто добавление и фильтрация нужных данных.
После этого добавляем значения из сформированного списка в таблицу:
А затем список обнуляем, чтобы на следующей итерации цикла записать в него новые значения.
Ну и выводим всю красоту на печать:
Запуск сканирования сети и портов
И осталась видоизмененная функция main(). В связи с тем, что добавились новые функции, пришлось добавить и новые вызовы, а также, коль я использую пакет rich, почему бы не добавить немного красоты.
Для начала проверяем, запущен ли скрипт под рутом. Если нет, выходим из функции. Ну и печатаем сообщение жирным красным цветом.
Если же все хорошо и рут найден, получаем IP-адрес и запускаем функцию сканирования ip и mac адресов, которая возвращает словарь с полученными значениями:
Запускаем сканирование портов. Сюда я добавил прогресс-бар, чтобы все отображалось в интерактивном режиме и, было понятно, что скрипт не завис, а что-то делает. На основании количества полученных значений в словаре с ip и mac адресами, устанавливается значение ползунка у прогресс-бара. Ну и по мере выполнения цикла обновляет свое значение, а также показывает предположительное время до завершения процесса.
После получения всех данных осталось только напечатать значения из словарей.
Локальный IP и шлюз я теперь печатаю под таблицей.
Проверка работы сканера
Сначала хотел воспользоваться для вывода значений PrettyTable, но после экспериментов с ним понял, что он совершенно не подойдет для моих целей. Так как, если терминал уже, чем вывод, таблица, сформированная с его помощью, просто ломается и в итоге на экране получается каша. Поэтому rich.
Ну и вот моя тестовая сеть, просканированная с помощью данного сканера:
Таблица поддерживает разделители, но мне показалось, что так будет немного эстетичнее, что ли.
А вот так выглядит вывод из другого Linux на тестовом стенде:
Никакой особо разницы, за исключением того, что в Mint пришлось доустановить пакеты. Но, это мелочи. И да, на сканирование 1024-х портов и остальные запросы у восьми машин в сети уходит около минуты. Но, это на тестовом стенде. В реальности же время будет, скорее всего, немного больше. Тут конечно не помешали бы тесты.
Видео-иллюстрация работы сканера:
А на этом, пожалуй, все.
Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезной.
Банально, скажете вы. Сканер сети. Ничего необычного. Ну, в принципе да. Сканировать сети можно по-разному, причем на разных сетевых уровнях и даже используя только лишь одни сокеты. Но, при использовании сокетов нужно четко понимать правила составления пакетов для запросов на разных уровнях, в каком виде эти пакеты приходят в ответе, чтобы получить из них необходимую информацию. И это без сомнения нужно изучать. Но, использовать сокеты, в чистом виде, мы не будем. А для сканирования сети и портов воспользуемся библиотекой 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.
Ну и вот моя тестовая сеть, просканированная с помощью данного сканера:
Таблица поддерживает разделители, но мне показалось, что так будет немного эстетичнее, что ли.
А вот так выглядит вывод из другого Linux на тестовом стенде:
Никакой особо разницы, за исключением того, что в Mint пришлось доустановить пакеты. Но, это мелочи. И да, на сканирование 1024-х портов и остальные запросы у восьми машин в сети уходит около минуты. Но, это на тестовом стенде. В реальности же время будет, скорее всего, немного больше. Тут конечно не помешали бы тесты.
Видео-иллюстрация работы сканера:
А на этом, пожалуй, все.
Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезной.