Сегодня я попробую написать свою первую программу на замечательном, по моему мнению, языке программирования Ruby.
Эксперимент буду проводить на Debian 8.
Сканируем сеть
Итак, для начала давайте просканируем нужный нам диапазон.
Для своих целей я буду использовать Гем ipscanner.
Установим Гем (или Gem, для тех кому режет слух):
# gem install ipscanner
Ipscanner выдает нам массив с перечисленными доступными IP.
Синтаксис у него следующий:
IPScanner.scan(ip_base = ip_first, range = range1..range2, timeout)
Тут я думаю все понятно без слов, сделаю лишь акцент на поле ip_first — это должен быть строковый параметр, range1, range2 и timeout — это integer.
После того как мы просканируем сеть, каждый полученный IP, передаем в отдельный поток, который и будет проверять открытые порты.
Функция сканирования диапазона IP адресов и создания потоков, будет выглядеть вот так:
def scanner (ip,range1,range2,start_port,end_port)
scanned_ip=IPScanner.scan(ip_base = ip, range = range1..range2, t = 5)
allports=*(start_port..end_port)
pc_thread = []
scanned_ip.each do |ip|
pc_thread << Thread.new(ip) do
scanports ip, allports
end
end
pc_thread.each {|t| t.join }
end
Все доступные IP будут в массиве scanned_ip.
Дополнительно я создам массив allports, в который запишу весь диапазон необходимых для сканирования портов.
Ну и для каждого IP адреса создам поток pc_thread, в который передам функцию сканирования портов и все необходимые порты строкой:
scanports ip, allports
Сканируем порты
Функция сканирования портов будет выглядеть следующим образом:
def scanports(host, ports)
ports.each do |tryport|
begin
sock = Socket.new(:INET, :STREAM)
puts "#{host}: #{tryport} open." if sock.connect(Socket.sockaddr_in(tryport, host))
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT
end
end
end
Результатом выполнения данной функции будет надпись о том что порт открыт, если он открыт, и отвечает за это:
puts "#{host}: #{tryport} open." if sock.connect(Socket.sockaddr_in(tryport, host))
Код целиком
#Подключаем необходимые компоненты
#В Ruby это не обязательно делать, но все же, для полноты картины
require 'ipscanner'
require 'socket'
require 'thread'
#Функция сканирования портов
def scanports(host, ports)
#Для каждого порта запускаем процедуру
ports.each do |tryport|
begin
#Создаем сокет
sock = Socket.new(:INET, :STREAM)
#Если сокет подключается к порту выводим на экран сообщение
puts "#{host}: #{tryport} open." if sock.connect(Socket.sockaddr_in(tryport, host))
#Закрывать сокет нет необходимости, в Ruby это происходит автоматически
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT
end
end
end
#Функция сбора ip
def scanner (ip,range1,range2,start_port,end_port)
#Запускаем процедуру сканирования, и все ip адреса записываем в массив scanned_ip
scanned_ip=IPScanner.scan(ip_base = ip, range = range1..range2, t = 5)
#Создаем массив с необходимыми портами
allports=*(start_port..end_port)
pc_thread = []
#Для каждого элемента массива scanned_ip запускаем процедуру
scanned_ip.each do |ip|
#Записываем в массив pc_thread класс потока, в котором идет ссылка на функцию scanports
pc_thread << Thread.new(ip) do
scanports ip, allports
end
end
#Запускаем потоки
pc_thread.each {|t| t.join }
end
#Запуск программы, путем передачи в функцию scanner необходимых параметров
scanner '10.166.10.',1,254,1,70000
Согласитесь, что это очень краткий и я надеюсь симпатичный код :).
Результатом выполнения данного кода будет:
...
10.166.10.200: 21 open.
10.166.10.110: 22 open.
10.166.10.2: 22 open.
10.166.10.171: 111 open.
10.166.10.1: 22 open.
10.166.10.151: 80 open.
10.166.10.128: 80 open.
10.166.10.137: 80 open.
10.166.10.170: 80 open.
10.166.10.159: 80 open.
...
Ну и видео о том как работает программа:
Комментарии (18)
berez
02.09.2015 15:09+2Можно просто взять диапазон адресов и сразу его весь по портам просканировать. А вы сначала все машинки из диапазона пытаетесь пинговать (это именно то, чем занимается ipScanner). Так вот, пинги могут быть заблокированы, а коннекты — нет, и тогда ваш чудо-сканер машинку не найдет.
А еще вы порты перебираете на машинке без паузы и подряд — такое поведение сразу вызовет срабатывание всех возможных IDS в сети. Так что если поиграться со скриптом не в своей домашней сетке, а на работе, то можно и люлей огрести нешутейных.
begin sock = Socket.new(:INET, :STREAM) puts "#{host}: #{tryport} open." if sock.connect(Socket.sockaddr_in(tryport, host)) rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT end
Я не очень знаком с руби, поэтому сразу вопрос: а когда закроется открытый вами сокет? Если не сразу по выходу из области видимости, то ваш сканер при некотором везении сможет выкушать все дескрипторы и позорно умереть (а при удачном стечении обстоятельств еще и систему слегка ушатать).dimult
02.09.2015 17:02-1Можно просто взять диапазон адресов и сразу его весь по портам просканировать. А вы сначала все машинки из диапазона пытаетесь пинговать (это именно то, чем занимается ipScanner). Так вот, пинги могут быть заблокированы, а коннекты — нет, и тогда ваш чудо-сканер машинку не найдет.
да вы правы, но это самый простой вариант, и он скорее в качестве начального примера
при некотором везении сможет выкушать все дескрипторы и позорно умереть
нет, об этом речь не идет, все работает стабильно, единственное, что на Windows это раз в 10 медленнее, опять же из -за простоты и отсутствия оптимизации кода
dimult
02.09.2015 17:08-1А еще вы порты перебираете на машинке без паузы и подряд — такое поведение сразу вызовет срабатывание всех возможных IDS в сети. Так что если поиграться со скриптом не в своей домашней сетке, а на работе, то можно и люлей огрести нешутейных.
пробовал как раз на работе, cisco не среагировала никак, больше там ни каких экранов и не было.
чудо-сканер
)))berez
02.09.2015 17:59больше там ни каких экранов и не было.
Так я не конкретно про экраны, а вообще про IDS.
Они весьма во многих конторах стоят, ими рулят сисадмины, и пользователям об их наличии не докладывают. А настроены они могут быть сильно по-разному. Где-то сразу алярмы во все щели начинают рассылать, а где-то админам постфактум отчетик приходит: «в этом месяце такая-то и такая-то машины сканировали сеть коннектами. Столько-то тыщ коннектов в минуту, в течение стольки-то минут».
В первом случае шершавый фитиль вставят сразу, прямо на месте преступления, в присутствии понятых и начальства. Во втором возможны варианты, вплоть до того, что админы забьют болт на одиночный инцидент.
Но в любом случае массированное сканирование сети коннектами с одной машины — это то, на что любая IDS (если она есть и запущена) сработает обязательно.
A1MaZ
02.09.2015 15:13А зачем такие огромные отступы?
И какая-то странная расстановка пробелов. Где-то присвоение отделено пробелами, а где-то нет. В блоке вон с одной стороны пробел есть, а с другой нет.dimult
02.09.2015 17:15А зачем такие огромные отступы?
извините, исправил (скопировал изначально из рабочего скрипта)
Obramko
02.09.2015 16:43+2Такие вещи нельзя писать с потоками. Нужен какой-нибудь event loop.
dimult
02.09.2015 17:04-1Такие вещи нельзя писать с потоками.
почему?
работает нормально, конечно контроля никакого нет, но в 20 строках кода этого и не могло быть.
сканирование сети в маске /24 бита с 1 по 70000 порт происходит на ура.berez
02.09.2015 18:13+2работает нормально,
Самый худший из возможных аргументов. :)
Да и работает-то, на самом деле, чудом: например, вот здесь люди пишут, что puts в многопоточном руби-приложении может привести к неприятным сюрпризам.
цитатоДанный пример также иллюстрирует глюк. Внутри цикла предпочтительней использовать print для вывода числа, чем puts. Почему? Потому что puts тайно разбивает свою работу на две составляющие: выводит свой аргумент, а затем выводит символ новой строки. Между ними двумя может запуститься поток, и вывод будет чередоваться. Вызывая print одной строки, которая уже содержит символ новой строки, мы можем обойти данную проблему.
zombopanda
02.09.2015 18:43Да, вместо puts нужно писать print "#{string}\n", тогда будет нормально.
UPD: написал не дочитав.
ayurtaykin
02.09.2015 18:19А вы можете сделать сравнение по скорости сканирования между «простым и быстрым сканером IP адресов» и nmap?
Просто интереса ради :)
YourChief
Hello, Threads!