Создание Сетевого Сканера на Python: Подробное Руководство
В современном мире, где сети стали неотъемлемой частью нашей жизни, понимание их безопасности и структуры становится все более важным. Сетевой сканер – это мощный инструмент, позволяющий администраторам сетей и специалистам по безопасности выявлять активные хосты, открытые порты и другие важные сведения о сети. Создание собственного сетевого сканера не только углубляет ваше понимание принципов работы сети, но и дает вам возможность настроить инструмент под конкретные нужды. В этой статье мы подробно рассмотрим, как создать сетевой сканер на Python, начиная с самых основ и заканчивая более продвинутыми функциями.
## Зачем создавать свой сетевой сканер?
Существует множество готовых сетевых сканеров, таких как Nmap, но создание собственного инструмента имеет ряд преимуществ:
* **Понимание основ:** Вы получите глубокое понимание принципов работы сканирования сети.
* **Гибкость:** Вы сможете настроить сканер под конкретные задачи и требования.
* **Оптимизация:** Вы можете оптимизировать сканер для конкретной сетевой среды.
* **Образование:** Создание собственного сканера – отличный способ улучшить навыки программирования на Python и понимание сетевых технологий.
## Необходимые инструменты и библиотеки
Для создания сетевого сканера на Python нам понадобятся следующие инструменты и библиотеки:
* **Python:** Язык программирования, на котором будет написан сканер. Рекомендуется использовать Python 3.
* **`socket`:** Встроенная библиотека Python для работы с сетевыми сокетами.
* **`argparse`:** Библиотека для разбора аргументов командной строки (необязательно, но рекомендуется).
* **`threading`:** Библиотека для реализации многопоточности (для ускорения сканирования).
* **`subprocess`:** Библиотека для запуска внешних команд (например, `ping` для проверки доступности хоста, полезно в некоторых сценариях).
* **`ipaddress`:** Библиотека для работы с IP-адресами и сетями.
Установите необходимые библиотеки, если они еще не установлены:
bash
pip install ipaddress
## Основной код сканера (базовая версия)
Начнем с простого сканера, который проверяет наличие активных хостов в сети путем отправки ICMP-пакетов (ping). Этот пример не использует `ipaddress` или многопоточность для простоты. Обратите внимание, что отправка ICMP-пакетов может потребовать прав администратора.
python
import socket
import subprocess
import platform
def ping(host):
“””Проверяет доступность хоста с помощью ping.”””
param = ‘-n 1’ if platform.system().lower() == ‘windows’ else ‘-c 1’
command = [‘ping’, param, host]
return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
def scan_network(network_address):
“””Сканирует сеть на наличие активных хостов.”””
for i in range(1, 255):
ip = network_address + ‘.’ + str(i)
if ping(ip):
print(f’Хост {ip} активен’)
if __name__ == “__main__”:
network = input(“Введите адрес сети (например, 192.168.1): “)
scan_network(network)
Этот код выполняет следующие действия:
1. **`ping(host)`:** Функция отправляет ICMP-пакет (ping) на указанный хост и возвращает `True`, если хост ответил, и `False` в противном случае. Использует `subprocess` для запуска команды `ping` операционной системы.
2. **`scan_network(network_address)`:** Функция перебирает все возможные IP-адреса в указанной сети (от `.1` до `.254`) и вызывает функцию `ping` для каждого адреса.
3. **`if __name__ == “__main__”:`:** Этот блок кода выполняется только при запуске скрипта напрямую. Он запрашивает у пользователя адрес сети и вызывает функцию `scan_network`.
**Как запустить этот код:**
1. Сохраните код в файл, например, `scanner.py`.
2. Откройте командную строку или терминал.
3. Перейдите в каталог, где находится файл `scanner.py`.
4. Запустите скрипт с помощью команды `python scanner.py`.
5. Введите адрес сети, которую хотите просканировать (например, `192.168.1`).
## Улучшение сканера: использование `ipaddress`
Библиотека `ipaddress` предоставляет удобные инструменты для работы с IP-адресами и сетями. Мы можем использовать ее, чтобы упростить и сделать код более надежным.
python
import socket
import subprocess
import platform
import ipaddress
def ping(host):
“””Проверяет доступность хоста с помощью ping.”””
param = ‘-n 1’ if platform.system().lower() == ‘windows’ else ‘-c 1’
command = [‘ping’, param, host]
return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
def scan_network(network_address):
“””Сканирует сеть на наличие активных хостов.”””
try:
network = ipaddress.ip_network(network_address, strict=False)
for ip_address in network:
ip = str(ip_address)
if ping(ip):
print(f’Хост {ip} активен’)
except ValueError:
print(“Некорректный адрес сети”)
if __name__ == “__main__”:
network = input(“Введите адрес сети (например, 192.168.1.0/24): “)
scan_network(network)
**Изменения:**
* Импортирована библиотека `ipaddress`.
* В функции `scan_network` используется `ipaddress.ip_network` для создания объекта сети из введенной строки. Параметр `strict=False` позволяет использовать адреса сетей без указания маски подсети (например, `192.168.1.0` вместо `192.168.1.0/24`).
* Цикл `for ip_address in network:` перебирает все IP-адреса в сети, представленные в виде объектов `ipaddress.IPv4Address`.
* `str(ip_address)` преобразует объект `ipaddress.IPv4Address` в строку.
* Добавлена обработка исключения `ValueError` для случаев, когда введен некорректный адрес сети.
Теперь можно вводить адреса сетей с маской подсети (например, `192.168.1.0/24`) или без нее (например, `192.168.1.0`).
## Улучшение сканера: многопоточность
Сканирование сети последовательно может занять много времени. Чтобы ускорить процесс, можно использовать многопоточность. Библиотека `threading` позволяет запускать несколько потоков одновременно, каждый из которых будет сканировать часть сети.
python
import socket
import subprocess
import platform
import ipaddress
import threading
import queue
def ping(host):
“””Проверяет доступность хоста с помощью ping.”””
param = ‘-n 1’ if platform.system().lower() == ‘windows’ else ‘-c 1’
command = [‘ping’, param, host]
return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
def worker(q):
“””Функция, выполняемая каждым потоком.”””
while True:
ip = q.get()
if ip is None:
break
if ping(ip):
print(f’Хост {ip} активен’)
q.task_done()
def scan_network(network_address, num_threads=20):
“””Сканирует сеть на наличие активных хостов с использованием многопоточности.”””
try:
network = ipaddress.ip_network(network_address, strict=False)
q = queue.Queue()
# Запускаем потоки
threads = []
for i in range(num_threads):
t = threading.Thread(target=worker, daemon=True, args=(q,))
threads.append(t)
t.start()
# Заполняем очередь IP-адресами
for ip_address in network:
q.put(str(ip_address))
# Добавляем стоп-сигналы в очередь для каждого потока
for i in range(num_threads):
q.put(None)
# Ждем завершения всех задач в очереди
q.join()
# Ждем завершения всех потоков (хотя они daemon=True)
for t in threads:
t.join()
except ValueError:
print(“Некорректный адрес сети”)
if __name__ == “__main__”:
network = input(“Введите адрес сети (например, 192.168.1.0/24): “)
num_threads = int(input(“Введите количество потоков (например, 20): “))
scan_network(network, num_threads)
**Изменения:**
* Импортированы библиотеки `threading` и `queue`.
* Добавлена функция `worker(q)`, которая извлекает IP-адрес из очереди `q`, проверяет его доступность с помощью `ping` и выводит результат. После обработки каждого IP-адреса вызывается `q.task_done()`, чтобы сообщить очереди о завершении задачи.
* В функции `scan_network` создается очередь `q` типа `queue.Queue()`. Затем запускается указанное количество потоков (`num_threads`), каждый из которых выполняет функцию `worker`. IP-адреса из сети добавляются в очередь. После добавления всех IP-адресов в очередь, в очередь добавляются `None` для каждого потока. Это сигнализирует потокам о завершении работы после обработки всех IP-адресов. `q.join()` блокирует выполнение до тех пор, пока все задачи в очереди не будут выполнены.
* Добавлена возможность указать количество потоков при запуске скрипта.
* `daemon=True` указывает, что поток является демоном. Это означает, что поток автоматически завершится, когда завершится основной поток (главная программа).
* Добавлен `t.join()` для каждого потока, чтобы убедиться, что все потоки завершили свою работу до завершения основной программы.
Теперь сканер будет работать значительно быстрее, особенно при сканировании больших сетей. Рекомендуется экспериментировать с количеством потоков, чтобы найти оптимальное значение для вашей сети.
## Сканирование портов (TCP)
Помимо проверки доступности хостов, сетевой сканер может также сканировать открытые порты на этих хостах. Это позволяет определить, какие сервисы работают на хосте и какие порты открыты для подключения. В этом примере мы будем сканировать TCP-порты.
python
import socket
import ipaddress
import threading
import queue
def scan_port(ip, port):
“””Сканирует TCP-порт на указанном IP-адресе.”””
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((ip, port))
if result == 0:
print(f’Порт {port} открыт на {ip}’)
sock.close()
except socket.gaierror:
print(f”Невозможно разрешить имя хоста для {ip}”)
except socket.error:
print(f”Не удалось подключиться к {ip}:{port}”)
def worker(q):
“””Функция, выполняемая каждым потоком.”””
while True:
item = q.get()
if item is None:
break
ip, port = item
scan_port(ip, port)
q.task_done()
def scan_network(network_address, ports, num_threads=20):
“””Сканирует сеть на наличие открытых портов с использованием многопоточности.”””
try:
network = ipaddress.ip_network(network_address, strict=False)
q = queue.Queue()
# Запускаем потоки
threads = []
for i in range(num_threads):
t = threading.Thread(target=worker, daemon=True, args=(q,))
threads.append(t)
t.start()
# Заполняем очередь IP-адресами и портами
for ip_address in network:
ip = str(ip_address)
for port in ports:
q.put((ip, port))
# Добавляем стоп-сигналы в очередь для каждого потока
for i in range(num_threads):
q.put(None)
# Ждем завершения всех задач в очереди
q.join()
# Ждем завершения всех потоков (хотя они daemon=True)
for t in threads:
t.join()
except ValueError:
print(“Некорректный адрес сети”)
if __name__ == “__main__”:
network = input(“Введите адрес сети (например, 192.168.1.0/24): “)
ports_str = input(“Введите порты для сканирования (например, 21,22,80,443): “)
ports = [int(port) for port in ports_str.split(‘,’)]
num_threads = int(input(“Введите количество потоков (например, 20): “))
scan_network(network, ports, num_threads)
**Изменения:**
* Добавлена функция `scan_port(ip, port)`, которая пытается подключиться к указанному порту на указанном IP-адресе. Если подключение успешно, то порт считается открытым.
* `socket.socket(socket.AF_INET, socket.SOCK_STREAM)` создает TCP сокет.
* `sock.settimeout(1)` устанавливает таймаут на подключение в 1 секунду. Это предотвращает зависание сканера, если порт не отвечает.
* `sock.connect_ex((ip, port))` пытается установить соединение. Метод `connect_ex` возвращает код ошибки, а не выбрасывает исключение, если соединение не удалось.
* Если `result == 0`, то соединение установлено успешно, и порт считается открытым.
* `sock.close()` закрывает сокет после проверки.
* Добавлены блоки `try…except` для обработки исключений, связанных с разрешением имен хостов и ошибками подключения.
* Функция `worker(q)` теперь извлекает из очереди кортеж `(ip, port)` и вызывает функцию `scan_port` для сканирования порта.
* Функция `scan_network` теперь принимает список портов `ports` в качестве аргумента. Она перебирает все IP-адреса в сети и все порты в списке и добавляет кортежи `(ip, port)` в очередь.
* При запуске скрипта запрашивается список портов для сканирования.
Теперь сканер будет сканировать указанные порты на всех хостах в сети и выводить информацию об открытых портах.
## Добавление разбора аргументов командной строки (argparse)
Использование библиотеки `argparse` позволяет сделать сканер более удобным в использовании, предоставляя возможность указывать параметры сканирования через командную строку.
python
import socket
import ipaddress
import threading
import queue
import argparse
def scan_port(ip, port):
“””Сканирует TCP-порт на указанном IP-адресе.”””
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((ip, port))
if result == 0:
print(f’Порт {port} открыт на {ip}’)
sock.close()
except socket.gaierror:
print(f”Невозможно разрешить имя хоста для {ip}”)
except socket.error:
print(f”Не удалось подключиться к {ip}:{port}”)
def worker(q):
“””Функция, выполняемая каждым потоком.”””
while True:
item = q.get()
if item is None:
break
ip, port = item
scan_port(ip, port)
q.task_done()
def scan_network(network_address, ports, num_threads=20):
“””Сканирует сеть на наличие открытых портов с использованием многопоточности.”””
try:
network = ipaddress.ip_network(network_address, strict=False)
q = queue.Queue()
# Запускаем потоки
threads = []
for i in range(num_threads):
t = threading.Thread(target=worker, daemon=True, args=(q,))
threads.append(t)
t.start()
# Заполняем очередь IP-адресами и портами
for ip_address in network:
ip = str(ip_address)
for port in ports:
q.put((ip, port))
# Добавляем стоп-сигналы в очередь для каждого потока
for i in range(num_threads):
q.put(None)
# Ждем завершения всех задач в очереди
q.join()
# Ждем завершения всех потоков (хотя они daemon=True)
for t in threads:
t.join()
except ValueError:
print(“Некорректный адрес сети”)
if __name__ == “__main__”:
parser = argparse.ArgumentParser(description=’Сетевой сканер на Python’)
parser.add_argument(‘network’, help=’Адрес сети для сканирования (например, 192.168.1.0/24)’)
parser.add_argument(‘-p’, ‘–ports’, help=’Список портов для сканирования (например, 21,22,80,443)’, required=True)
parser.add_argument(‘-t’, ‘–threads’, type=int, default=20, help=’Количество потоков (по умолчанию: 20)’)
args = parser.parse_args()
network = args.network
ports = [int(port) for port in args.ports.split(‘,’)]
num_threads = args.threads
scan_network(network, ports, num_threads)
**Изменения:**
* Импортирована библиотека `argparse`.
* Создается объект `ArgumentParser` с описанием сканера.
* Добавляются аргументы командной строки:
* `network`: Адрес сети для сканирования (обязательный аргумент).
* `-p`, `–ports`: Список портов для сканирования (обязательный аргумент).
* `-t`, `–threads`: Количество потоков (необязательный аргумент, по умолчанию 20).
* Аргументы командной строки разбираются с помощью `parser.parse_args()`. Результат сохраняется в объекте `args`.
* Значения аргументов извлекаются из объекта `args` и используются для вызова функции `scan_network`.
**Как запустить этот код:**
bash
python scanner.py 192.168.1.0/24 -p 21,22,80,443 -t 30
В этом примере сканер будет сканировать сеть `192.168.1.0/24` на портах `21`, `22`, `80` и `443`, используя 30 потоков.
## Дополнительные возможности и улучшения
* **Сканирование UDP-портов:** Добавьте поддержку сканирования UDP-портов. Для этого необходимо использовать `socket.SOCK_DGRAM` вместо `socket.SOCK_STREAM` и отправлять UDP-пакеты на указанные порты.
* **Определение сервисов:** Попробуйте определить сервисы, работающие на открытых портах, отправляя запросы на эти порты и анализируя ответы.
* **Использование Nmap:** Вместо создания собственного сканера с нуля, вы можете использовать библиотеку `python-nmap` для работы с Nmap из Python. Это позволит вам использовать все возможности Nmap, такие как определение операционной системы, версии сервисов и т.д.
* **Графический интерфейс:** Создайте графический интерфейс для сканера с использованием библиотек, таких как Tkinter или PyQt.
* **Сохранение результатов:** Сохраняйте результаты сканирования в файл (например, в формате CSV или JSON).
* **Улучшение обработки ошибок:** Добавьте более детальную обработку ошибок и логирование событий.
* **Асинхронное сканирование:** Используйте библиотеки `asyncio` и `aiohttp` для реализации асинхронного сканирования, что может значительно увеличить производительность.
## Заключение
Создание сетевого сканера на Python – это отличный способ углубить свои знания в области сетевых технологий и программирования. В этой статье мы рассмотрели основные этапы создания сканера, начиная с простого ping-сканера и заканчивая многопоточным сканером портов с использованием библиотеки `argparse`. Вы можете использовать этот код в качестве отправной точки для создания собственного мощного и гибкого инструмента сканирования сети. Помните о необходимости соблюдения этических норм и законов при сканировании сетей, особенно если это не ваша собственная сеть.
Регулярно обновляйте свои знания в области сетевой безопасности, чтобы оставаться в курсе последних угроз и методов защиты. Удачи в ваших экспериментах!