Сканер локальной сети для получения списка IP и MAC адресов на Python

если не начинал с С и это не универовский предмет, а самостоятельное изучение, возможно стоит начать учить Rust вместо С. Думаю, на нем тоже можно написать модули для питона, но еще и много всего другого

Спасибо за наводку. С раньше не учил нигде. Попробую. Что-то должно получиться ))
 
Вау, сколько жару из-за одного скрипта :)
Подведем итоги:

  • Python НЕ имеет надежной встроенной/стандартной функции определения адреса IP локальной машины. Документация и примеры посылают к
    import socket
    hostname = socket.gethostname()
    Ipaddr = socket.gethostbyname(hostname)
    print("Your Computer Name is:" + Ipaddr) Но тут адрес определяется запросом в ДНС и другие сервисы такого типа (/etc/hosts итд), то
    есть если резолвинга на адрес нет то выдаст ошибочный адрес (127.0.0.1).
  • имхо - питон зарождался во времена когда сети не были настолько важны и пропустили сей момент
  • Проблему пробовали решать/обойти, но на сегодня тот хак что @Johan Van привел самый популярный (и наверное надежный). Обзор
    всевозможных способов например тут
  • Моя проблема с таким хаком в том что он основан на "побочном эффекте" использования модуля socket и поэтому может перестать работать в будущем в любой момент.
    Как я бы решил проблему ? Не использовал бы Питон для такой задачи:
Golang:
C-подобный:
package main

import (
    "fmt"
    "net"
)

func main() {
    addrs, _ := net.InterfaceAddrs()
    fmt.Printf("%v\n", addrs)

1653487536836.png


Всем хорошего дня.
 
  • Нравится
Реакции: Архонт и Johan Van
Вау, сколько жару из-за одного скрипта :)
Подведем итоги:

  • Python НЕ имеет надежной встроенной/стандартной функции определения адреса IP локальной машины. Документация и примеры посылают к
    import socket
    hostname = socket.gethostname()
    Ipaddr = socket.gethostbyname(hostname)
    print("Your Computer Name is:" + Ipaddr) Но тут адрес определяется запросом в ДНС и другие сервисы такого типа (/etc/hosts итд), то
    есть если резолвинга на адрес нет то выдаст ошибочный адрес (127.0.0.1).
  • имхо - питон зарождался во времена когда сети не были настолько важны и пропустили сей момент
  • Проблему пробовали решать/обойти, но на сегодня тот хак что @Johan Van привел самый популярный (и наверное надежный). Обзор
    всевозможных способов например тут
  • Моя проблема с таким хаком в том что он основан на "побочном эффекте" использования модуля socket и поэтому может перестать работать в будущем в любой момент.
    Как я бы решил проблему ? Не использовал бы Питон для такой задачи:
Golang:
C-подобный:
package main

import (
    "fmt"
    "net"
)

func main() {
    addrs, _ := net.InterfaceAddrs()
    fmt.Printf("%v\n", addrs)

Посмотреть вложение 59940

Всем хорошего дня.

А в Go много чего интересного, похоже есть )) Надо будет подробнее с ним познакомиться ))
 
Добрый день!
Подскажите, пожалуйста.
Зачем тут используется метод сплит в конце строки?
Python:
com = f'route PRINT 0* | findstr {local_ipv4()}'.split()
Ведь в check_output команду можно передать в виде строки.
 
Добрый день!
Подскажите, пожалуйста.
Зачем тут используется метод сплит в конце строки?
Python:
com = f'route PRINT 0* | findstr {local_ipv4()}'.split()
Ведь в check_output команду можно передать в виде строки.

Можно. Делить строку не обязательно. Но, у меня почему-то не получалось, уже не помню почему, передать команду с "|", вылетала ошибка. А когда делил на запчасти, тогда все работало. Попробуйте не делить. Может быть все будет работать и без split()
 
Спасибо. Еще пара вопросов:
1. Почему в этой строке мы находим адрес первого узла сети, а не адрес самой сети?
Код:
ip_mac_network = get_ip_mac_nework(f'{local_ip.split(".")[0]}.{local_ip.split(".")[1]}.{local_ip.split(".")[2]}.1/24')

По ссылке
сказано следующее:

ARP Ping

The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method:

>>> ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"), timeout=2)

Есть ли разница?

Там же по ссылке, кстати, предлагается использовать команду arping("192.168.1.0/24"), вместо той, что выше. Только не понял, как ее вывод перегнать в словарь. Если вернуть результат выполнения функции в переменную, то айпишники и макадреса внутри элементов видно, но обратиться к ним не получается.

2. Почему итоговая программа выдает всегда разное количество адресов устройств в сети? У меня к вай-фаю подключены всякие телефоны, телевизоры, камеры и все они находятся редко. Обычно сам ноут, роутер и 1-2 других устройств.
 
Последнее редактирование:
Спасибо. Еще пара вопросов:
1. Почему в этой строке мы находим адрес первого узла сети, а не адрес самой сети?
Код:
ip_mac_network = get_ip_mac_nework(f'{local_ip.split(".")[0]}.{local_ip.split(".")[1]}.{local_ip.split(".")[2]}.1/24')

По ссылке
сказано следующее:

ARP Ping

The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method:

>>> ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"), timeout=2)

Есть ли разница?

Там же по ссылке, кстати, предлагается использовать команду arping("192.168.1.0/24"), вместо той, что выше. Только не понял, как ее вывод перегнать в словарь. Если вернуть результат выполнения функции в переменную, то айпишники и макадреса внутри элементов видно, но обратиться к ним не получается.

2. Почему итоговая программа выдает всегда разное количество адресов устройств в сети? У меня к вай-фаю подключены всякие телефоны, телевизоры, камеры и все они находятся редко. Обычно сам ноут, роутер и 1-2 других устройств.

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

2. srp и arping по большому счету, наверное ничем. Что от одного, что от другого, ответы одинаковые.

screenshot5.png


3. По поводу доступа к элементам. Обратите внимание, что прилетает в ответ кортеж из двух частей. То есть, чтобы обратиться к одной из них, надо указать индекс. Соответственно, пишем:

screenshot2.png


И указываем то, что вам нужно получить.

Ответ состоит также из двух частей. То есть отвеченных адресов и не отвеченных. Потому d и n. Можно убрать лишнюю переменную и записать так:

Python:
import scapy.all as sc

d = sc.arping("192.168.42.1/24", verbose=False)[0]


for i in d:
    print(i[1].hwsrc)

screenshot3.png


Соответственно, если вы наберете вместо .hwsrc .show() вы увидите, что можно еще оттуда вытащить. Ну или можно так глянуть, распечатав элемент. А можно без цикла, вот так:

Python:
import scapy.all as sc

d = sc.arping("192.168.42.1/24", verbose=False)[0]
print(d[0][1].hwsrc)

4. Насчет разного количества устройства в сети, точно не знаю. Тут могу только предположить. Уже вроде где-то натыкался на такой вопрос, что сканер не видит мобильные устройства. Так вот, скорее всего так происходит потому, что телефоны и прочие мобильные устройства находятся в сети не постоянно, а периодически делают запросы на обновление. А потому, подключение тоже может быть не постоянным. Возможно, поэтому.
 
Последнее редактирование:
  • Нравится
Реакции: bagbier
1. Почему передаем адрес первого узла сети. Ну, логично же, что мы сканируем всю сеть, то есть все ip адреса, а следовательно, нужно начать с самого первого. Но, я немного поэкспериментировал и, если не ошибаюсь, передать можно просто локальный адрес и маску. А scapy сам прошерстит нужный диапазон.

2. srp и arping по большому счету, наверное ничем. Что от одного, что от другого, ответы одинаковые.

Посмотреть вложение 60583

3. По поводу доступа к элементам. Обратите внимание, что прилетает в ответ кортеж из двух частей. То есть, чтобы обратиться к одной из них, надо указать индекс. Соответственно, пишем:

Посмотреть вложение 60580

И указываем то, что вам нужно получить.

Ответ состоит также из двух частей. То есть отвеченных адресов и не отвеченных. Потому d и n. Можно убрать лишнюю переменную и записать так:

Python:
import scapy.all as sc

d = sc.arping("192.168.42.1/24", verbose=False)[0]


for i in d:
    print(i[1].hwsrc)

Посмотреть вложение 60581

Соответственно, если вы наберете вместо .hwsrc .show() вы увидите, что можно еще оттуда вытащить. Ну или можно так глянуть, распечатав элемент. А можно без цикла, вот так:

Python:
import scapy.all as sc

d = sc.arping("192.168.42.1/24", verbose=False)[0]
print(d[0][1].hwsrc)

4. Насчет разного количества устройства в сети, точно не знаю. Тут могу только предположить. Уже вроде где-то натыкался на такой вопрос, что сканер не видит мобильные устройства. Так вот, скорее всего так происходит потому, что телефоны и прочие мобильные устройства находятся в сети не постоянно, а периодически делают запросы на обновление. А потому, подключение тоже может быть не постоянным. Возможно, поэтому.
По поводу телефонов вроде когда то тест делал. Если ты по кабелю подключён то мобилки с вай фай ты не увидишь. А вот с ноута на вафле трубки тоже сканятся по-моему.
 
  • Нравится
Реакции: bagbier
По поводу телефонов вроде когда то тест делал. Если ты по кабелю подключён то мобилки с вай фай ты не увидишь. А вот с ноута на вафле трубки тоже сканятся по-моему.

У меня вафли нет, к сожалению. Вернее есть ноут. Да и роутер есть. Руки не доходят потестировать ))
 
Спасибо за ответы и за статью в целом.
Загадку wi-fi устройств так и не разгадал. Пробовал на телефоне включать ютуб и при этом запускать сканер - не помогло, все равно телефон не всегда находится. Более того, иногда сканер не получает ответа от ноута, на котором запускается, иногда от шлюза. Если verbose отключить, то видно, что запросы отправляются, а ответы не приходят.

Функцию получения IP сделал по советам ниже и гугла вцелом:
Код:
def local_ipv4():
    return gethostbyname(gethostname())

Прелесть в том, что при отключении всех интерфейсах выдаст 127.0.0.1 без всяких дополнительных условий.
 
Спасибо за ответы и за статью в целом.
Загадку wi-fi устройств так и не разгадал. Пробовал на телефоне включать ютуб и при этом запускать сканер - не помогло, все равно телефон не всегда находится. Более того, иногда сканер не получает ответа от ноута, на котором запускается, иногда от шлюза. Если verbose отключить, то видно, что запросы отправляются, а ответы не приходят.

Функцию получения IP сделал по советам ниже и гугла вцелом:
Код:
def local_ipv4():
    return gethostbyname(gethostname())

Прелесть в том, что при отключении всех интерфейсах выдаст 127.0.0.1 без всяких дополнительных условий.

Ну, так то оно так, вот только вы получаете не адрес сетевого интерфейса, а адрес локальной петли, которая называется "localhost". А вам нужно получить ip вида 192.168.11.10, например. Так что, тот код, что приведен в статье, он работает и работает нормально. В комментах не было спора из-за того, что код не рабочий )) Там другие моменты были ))

UPD: Тут штука в чем, с тем кодом, что приведен у вас. Он правильный и каноничный, если так можно сказать. Но он начинает вести себя странно, когда у вас несколько сетевых интерфейсов или есть работающие интерфейсы виртуальных машин. Так что, полагаться на него на 100% не стоит. Хотя, если работает, то почему нет. А так да. При отключении инета конечно он выдаст петлю ))
 
Последнее редактирование:
Ну, так то оно так, вот только вы получаете не адрес сетевого интерфейса, а адрес локальной петли, которая называется "localhost". А вам нужно получить ip вида 192.168.11.10, например. Так что, тот код, что приведен в статье, он работает и работает нормально. В комментах не было спора из-за того, что код не рабочий )) Там другие моменты были ))

UPD: Тут штука в чем, с тем кодом, что приведен у вас. Он правильный и каноничный, если так можно сказать. Но он начинает вести себя странно, когда у вас несколько сетевых интерфейсов или есть работающие интерфейсы виртуальных машин. Так что, полагаться на него на 100% не стоит. Хотя, если работает, то почему нет. А так да. При отключении инета конечно он выдаст петлю ))
У меня было два интерфейса: wi-fi, virtualbox (оба активных и с айпишниками). Сначала он выдавал wi-fi'ный. После отключения wi-fi он выдавал virtualboxвский. Ну а после отключения последнего, уже выдавал 127.
Действительно меня беспокоит то, что непонятно, как эти функции выбирают какой айпишник выбрать из нескольких.

Но и по функции из примера, как я понял из обсуждения на SO, такой же вопрос. Как будто бы это не декларируемая возможность, а не штатное использование функции connect. И не понятно, как оно поведет себя в будущем.
 
У меня было два интерфейса: wi-fi, virtualbox (оба активных и с айпишниками). Сначала он выдавал wi-fi'ный. После отключения wi-fi он выдавал virtualboxвский. Ну а после отключения последнего, уже выдавал 127.
Действительно меня беспокоит то, что непонятно, как эти функции выбирают какой айпишник выбрать из нескольких.

Но и по функции из примера, как я понял из обсуждения на SO, такой же вопрос. Как будто бы это не декларируемая возможность, а не штатное использование функции connect. И не понятно, как оно поведет себя в будущем.

Все началось с того, что я просто нашел данный код на СО, и не особо разобрался как он работает. А у меня об этом спросили. Я не смог ответить толком. Вот в этом была и загвоздка. Тут все дело в том, что он пока работает. Можно изобрести разные костыли, но все они будут сводиться к сокетам, так или иначе. Либо вы их будете использовать напрямую, либо через библиотеки, которые внутри тоже используют сокеты. Мы и обсуждали, как можно обойти это ограничение сокетов. Просто потому, что кто его знает, как измениться питон в будущем. И сокеты могут просто перестать работать так, как от них ожидали ранее. А раз код который в статье работает по не задокументированным возможностям, значит они могут просто отвалиться. Вот в чем была загвоздка. Но, на данный момент, код с СО лучше всего определяет локальный IP.
 
  • Нравится
Реакции: bagbier
А в чем смысл то данного кода, узнать свой айпишник и мак я могу и через настройки вай-фай сети посмотреть, мне нужен код который найдет мак и айпи адреса подключенных к сети вайфай, а этот код показывает только мои данные
 
Мы в соцсетях:

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