• XSS.stack #1 – первый литературный журнал от юзеров форума

Делаем свой Сканер портов на С++

Dido

HDD-drive
Пользователь
Регистрация
06.01.2021
Сообщения
35
Реакции
34
Вступление
Сканер портов - это софтина, которая проверяет сервер на наличие открытых портов, пытаясь подключиться к серверу через каждый порт по очереди. После программа обычно сообщает, какие порты были открыты, а какие закрыты. Более сложные реализации сканеров портов, такие как Nmap помимо всего могут предоставлять и другую инфу, начиная от версии ПО заканчивая версией браузера. Сканирование портов обычно выполняется системными администраторами для проверки безопасности сети или злоумышленниками (хаЦкерами), которые ищут открытый порт, через который можно поставить под угрозу безопасность сервера, или в народе - просунуть свою жопу, дабы чем поживится.

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


Функция port_is_open:
C++:
bool port_is_open(const std::string& address, int port)
{
    sf::TcpSocket socket; // через глобал. пилим сокет
    bool open = (socket.connect(sf::IpAddress(address), port) == sf::Socket::Done); // проверяем на open
    socket.disconnect(); // закрываем
    return open;
}

Пояснительная бригада на выезд:
  1. Сначала мы создаем экземпляр sf :: TcpSocket, который позволяет нам подключаться к удаленному сокету.
  2. Затем подключаем сокет. Мы преобразуем строку «адрес» в экземпляр sf :: IpAddress, вызывая конструктор для sf :: IpAddress. Если явный вызов конструктора не заюзали, компилятор все равно выполнит его неявно. После попытки подключения мы проверяем, было ли соединение успешным, сравнивая возвращаемое значение функции sf :: TcpSocket :: connect со значением sf :: Socket :: Done. Если они равны, это означает, что соединение установлено и порт открыт. В этом случае для переменной open установлено значение true.
  3. Далее мы отключаем сокет с помощью функции sf :: TcpSocket :: disconnect. Это будет сделано автоматически в деструкторе, если мы оставим явный вызов.
  4. Наконец, мы возвращаем значение open вызывающей функции.
Здесь мы создаем новый sf :: TcpSocket, подключаемся к адресу и порту и затем возвращаем 1 или 0 в зависимости от того, удалось ли соединение. Мы избавляемся от напряжного явного вызова конструктора sf :: IpAddress, а также от вызова sf :: TcpSocket :: disconnect (). Мы можем использовать эту функцию прямо здесь:

Функция FirtsTempMain:

C++:
#include <iostream>
#include <SFML/Network.hpp> // не забудь её скачать и подключить
#include <string>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}

int main()
{
    std::cout << "Port 80 : ";
    if (port_is_open("localhost", 80))
        std::cout << "OPEN" << std::endl;
    else
        std::cout << "CLOSED" << std::endl;
    return 0;
}
Снимок экрана 2021-09-17 194158.png

Есть пробитие!
Попробуйте скомпилировать и запустить эту программу. Она проверит, открыт ли порт 80 на вашем компьютере. Обратите внимание, что «localhost» означает локальный компьютер; вы также можете использовать IP-адрес 127.0.0.1 или :: 1 (версия IPv6, хотя SFML еще не поддерживает IPv6). Вы можете изменить "localhost" на IP-адрес или веб-адрес другого веб-сайта (не включая "http: //" и любую информацию о пути).

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


Улучшенный сканер портов
Теперь, когда мы успешно разработали программу, которая может сканировать порт по адресу, мы изменим нашу программу, чтобы юзер, то есть вы, могли сами выбирать порты и адреса для скана:
C++:
#include <iostream>
#include <SFML/Network.hpp>
#include <string>
// тут все ясно. чекнули получили валид-сокет идем дальше
static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}


int main()
{
    std::string address; // пилим адресс
    int port; // номер апорта
    // получаем адресс.
    std::cout << "Address: " << std::flush;
    std::getline(std::cin, address);
    // получае порт.
    std::cout << "Port: " << std::flush;
    std::cin >> port;
    // поехали шерстить
    std::cout << "Scanning " << address << "...\n" << "Port " << port << " : ";
    if (port_is_open(address, port))
        std::cout << "OPEN" << std::endl;
    else
        std::cout << "CLOSED" << std::endl;
    return 0;
}
Помните, что 127.0.0.1 эквивалентно localhost. Кроме того, порт 80 может быть закрыт на вашем компьютере. Он открыт только у меня, потому что я использую HTTP-сервер Apache (порт 80 - это основной порт, обычно используемый для HTTP; другой порт, обычно используемый для HTTP, - это порт 8080).
Снимок экрана 2021-09-17 200124.png

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

Стек Портов
Скан одного порта душная тема, мы хотим разогнатся на все массив портов. Один из способов сделать это - позволить юзеру вводить столько портов, сколько он хочет, а затем сканировать их все за один раз. Проблема заключается в том, что юзер может захотеть просканировать множество портов, и ему придется вводить каждый из них. Мы также могли бы позволить пользователю указать диапазон портов, скажем, 0–100, но тогда они не могли бы указывать значения за пределами этой ранжи. Нырнем глубже, и заюзаем и то, и другое - укажем диапазоны И отдельные порты в списке: '80, 8080 '; диапазон, например: «20-80»; или список, содержащий диапазоны: «20–80,8080». Этот код становится довольно сложным из-за использования шаблонов, std :: vector, std :: stringstream и цикла for C ++ 11 на основе диапазона.

Для начала нам нужна функция для разделения строк:
Функция std::vector<std::string> split:
C++:
// Разбиваем строку на "токены" возле разделителя (пробел),
// опционально пропускаем пустые токены.
static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}
Тип данных портов будет строкой, но нам нужно целое число, поэтому нам также понадобится функция для преобразования строк в целые числа:
Функция string_to_int:

C++:
static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}

Если у нас есть диапазон портов, нам понадобится функция для генерации всех значений в этом диапазоне:
C++:
// Функция обмена.
template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}
// Создает вектор, содержащий диапазон значений.
template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}

Наконец, нам нужна функция для анализа списка портов с использованием вышеуказанных функций:

Функция parse_ports_list:
C++:
// Лист для парсинга портов
static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    // Лист портов.
    for (const std::string& token : split(list, ',')) {
        // Лист диапазона.
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            // Только одно знач. (добавьте только  в конце 'ports').
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            // Два значения (диапазон - все в этом диапазоне).
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}

Финалочка:
C++:
#include <iostream>
#include <SFML/Network.hpp>
#include <sstream>
#include <string>
#include <vector>

static bool port_is_open(const std::string& address, int port)
{
    return (sf::TcpSocket().connect(address, port) == sf::Socket::Done);
}


static std::vector<std::string> split(const std::string& string,
                                      char delimiter = ' ',
                                      bool allow_empty = false)
{
    std::vector<std::string> tokens;
    std::stringstream sstream(string);
    std::string token;
    while (std::getline(sstream, token, delimiter)) {
        if (allow_empty || token.size() > 0)
            tokens.push_back(token);
    }
    return tokens;
}

static int string_to_int(const std::string& string)
{
    std::stringstream sstream(string);
    int i;
    sstream >> i;
    return i;
}

template <typename T>
static void swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

template <typename T>
static std::vector<T> range(T min, T max)
{
    if (min > max)
        swap(min, max);
    if (min == max)
        return std::vector<T>(1, min);
    std::vector<T> values;
    for (; min <= max; ++min)
        values.push_back(min);
    return values;
}

static std::vector<int> parse_ports_list(const std::string& list)
{
    std::vector<int> ports;
    for (const std::string& token : split(list, ',')) {
        std::vector<std::string> strrange = split(token, '-');
        switch (strrange.size()) {
            case 0: ports.push_back(string_to_int(token));       break;
            case 1: ports.push_back(string_to_int(strrange[0])); break;
            case 2:
            {
                int min = string_to_int(strrange[0]),
                    max = string_to_int(strrange[1]);
                for (int port : range(min, max))
                    ports.push_back(port);
                break;
            }
            default:
                break;
        }
    }
    return ports;
}

int main()
{
    std::string address;
    std::string port_list;
    std::vector<int> ports;
    std::cout << "Address: " << std::flush;
    std::getline(std::cin, address);
    std::cout << "Port: " << std::flush;
    std::getline(std::cin, port_list);
    ports = parse_ports_list(port_list);
    std::cout << "Scanning " << address << "...\n";
    for (int port : ports) {
        std::cout << "Port " << port << " : ";
        if (port_is_open(address, port))
            std::cout << "OPEN\n";
        else
            std::cout << "CLOSED\n";
    }
    std::cout << std::flush;
    return 0;
}
Снимок экрана 2021-09-17 201221.png

На премию Тьюринга не претендует, но уже есть отправная точка)
 
Пожалуйста, обратите внимание, что пользователь заблокирован
использующей сетевой модуль SFML
какой прикол юзать х пойми шо, если есть чистый винсок.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
терпеть не могу когда простые вещи начинают описывать всякими std:: , векторами и прочей х#йней вроде.. std::string& token (шта? какой еще токен бля??)

простите великодушно, трижды срыгнул читая этот "код"
 
Пожалуйста, обратите внимание, что пользователь заблокирован
когда простые вещи начинают описывать всякими std:: , векторами
плюсовики не могут и helloworld без ООП STL закодить.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Так чего вы ожидали, это самые обычные плюсы, и в этом нет ничего плохого по большому счету.
 
Нет ни raw-сокетов, ни потоков, т.е. скорость с позволения сказать "скана" будет просто ничтожной.
Вместо прямых вызовов сокетных функций юзается какой-то манявраппер. Короче воспринимать это можно только как "пробу пера".
ТС, чисто для информации: чтоб чекнуть TCP-порты не обязательно устанавливать полноценное TCP-соединение.
Можно просто в одном потоке\процессе спамить SYN-пакетами, а в другом потоке\процессе собирать прилетающие SYN+ACK. Примерно так работает masscan.
 
Последнее редактирование:
Очень посредственный перевод этой статьи: https://www.cplusplus.com/articles/o2N36Up4/, к тому же непонятно зачем использовать SFML в масскане, можно же зайти сюда https://github.com/robertdavidgraham/masscan и посмотреть реализацию, взять с гитхаба чей-то простой учебный проект с использованием SYN (например этот) и на его основе сделать статью
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Мда, скорость этого сканера и так скажем не очень из-за полноценного соединения, так еще и SFML решили вмазать.
Допустим, что вариант с полным соединением первый пришел в голову и мы сразу решили написать статью.
Но нахера было использовать SFML??
Если есть обычные сырые сокеты, которые идеально справятся с задачей?

C:
#include <sys/types.h>
#include <sys/socket.h>

...
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // SOCK_STREAM если рассматриваем полноценное соединение
    // а так SOCK_RAW
    if (sockfd == -1) {
        // сокет не создался
        // выйти
    }
     
    int res = connect(sockfd, ...);
       if(res == -1){
        // коннект не удался
        // что-то сделать
        // выйти
    }
Аналогичный код и под Windows, но только другие инклюды и минимальные правки. (ну и там всякие WSAStartup)
(В коде могут быть ошибки, так как писал сразу на форуме, но там примерно так должно быть)


std::string& token (шта? какой еще токен бля??)
Да, с именованием переменных большая беда. Решил посмотреть переводы слова "token" и ни один перевод по смыслу не подходит.
Более того в оригинальной статье есть такой комментарий "Splits a string into tokens arround a delimiter" (Разбивает строку на токены вокруг разделителя)
Видимо автор токеном решил заменить слово элементы. Обидно, что горе-переводчик так сам и не поправил это

UPD1: Прочитал, что это паста галимая. Тогда вопрос, зачем форум засорять мусором, который и так без проблем можно искать?
UPD2: Я так понял, автор просто переводит статьи (статья, ориг), поэтому наши доёбы здесь не о чём.
 
Последнее редактирование:


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх