dos/src/DosAtk.cpp

553 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ====== DCL библиотеки ====== //
#include <iostream> // Для работы с вводом-выводом (cout, cin и др.)
#include <stdlib.h> // Стандартная библиотека C (функции работы с памятью, преобразования типов и др.)
#include <string> // Для работы со строками string
#include <chrono> // Для работы с временем и таймерами
#include <ctime> // Функции работы со временем C (time_t, localtime и др.)
#include <iomanip> // Для форматированного вывода (setw, setprecision и др.)
#include <getopt.h> // Для разбора аргументов командной строки
#include <cctype> // Функции для работы с символами (isdigit, isalpha и др.)
#include <curl/curl.h> // Библиотека libcurl для HTTP-запросов (отправка в Telegram)
// ====== DCL глобальные переменные ====== //
// Параметры
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; // Статус работы программы
// ====== DCL процедуры ====== //
void my_check_params()
{
// Данная процедура записывает в глобальные переменные параметры
// (attack_type, domain, ip, port, log_file, telegram_id, telegram_token) проводимой атаки, поступившие при вызове программы
// На вход получает int argc, char **argv, возвращает код выполнения
// Коды возврата:
// 2 - Атака флуд, все нужные опции есть
// 1 - Атака порт скан, все нужные опции есть
// 0 - нужна помощь
// -1 - пользователь не ввел тип атаки или ввел неверный тип атаки
// -10 - Пользователь выбрал тип атаки порт сканнинг, но не ввел нужные параметры
// -20 - Пользователь выбрал тип атаки флуд, но не ввел нужные параметры
// -101 - неизвестная опция или потерян аргумент, следует предложить вызвать флаг помощи
// -600 - пользователь ввел токен, но не id или наоборот
//Объявление
std::chrono::milliseconds ms; // Количество миллисекунд от целой секунды времени начала выполнения программы
int opt; // Прочитанный параметр
const char* short_options; // Сокращения для параметров
struct option long_options[]; // Структура, описывающая пеобходимые программе параметры
int i; // Счётчик для цикла
// Инициализация
ms = std::chrono::duration_cast<std::chrono::milliseconds>(start_timestamp.time_since_epoch()) % 1000;
opt = -1;
short_options = "a:d:i:p:l:t:b:h";
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}
};
i = 0;
printf("begin my_check_params"); // debug
// Выводим информацию о времени запуска программы
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': // Обработка опции -a (--attack)
attack_type = optarg; // Сохраняем тип атаки (scan/flood)
break;
case 'd': // Обработка опции -d (--domain)
domain = optarg; // Сохраняем доменное имя цели
break;
case 'i': // Обработка опции -i (--ip)
ip = optarg; // Сохраняем IP-адрес цели
break;
case 'p': // Обработка опции -p (--port)
port = optarg; // Сохраняем номер порта
break;
case 'l': // Обработка опции -l (--log)
log_file = optarg; // Сохраняем путь к лог-файлу
break;
case 't': // Обработка опции -t (--telegram)
telegram_id = optarg; // Сохраняем Telegram ID
break;
case 'b': // Обработка опции -b (--token)
telegram_token = optarg; // Сохраняем токен бота Telegram
break;
case 'h': // Обработка опции -h (--help)
status = 0; // Устанавливаем статус "показать справку"
break;
case '?': // Обработка неизвестной опции
status = -101; // Устанавливаем статус "неизвестная опция"
break;
}
}
// Проверяем корректность введенных параметров
if (status != 0 && status != -101)
{
// Проверяем валидность типа атаки
if (attack_type != "flood" && attack_type != "scan") {
status = -1; // Некорректный тип атаки
}
// Для сканирования портов должна быть указана цель (домен или IP)
else if (attack_type == "scan" && domain.empty() && ip.empty()) {
status = -10; // Не указана цель для сканирования
}
// Для флуд-атаки должна быть указана цель (домен или IP)
else if (attack_type == "flood" && domain.empty() && ip.empty()) {
status = -20; // Не указана цель для флуда
}
// Проверяем, что если указан Telegram ID или токен, то должны быть указаны оба
else if ((!telegram_id.empty() && telegram_token.empty()) ||
(telegram_id.empty() && !telegram_token.empty())) {
status = -600; // Неполные данные для Telegram
}
// Если все проверки пройдены и тип атаки - сканирование
else if (attack_type == "scan") {
status = 1; // Валидные параметры для сканирования
}
// Если все проверки пройдены и тип атаки - флуд
else if (attack_type == "flood") {
status = 2; // Валидные параметры для флуда
}
}
printf("end my_check_params status: %i\n", status); // debug
}
void my_diag()
{
// Данная функция вызывается в случае ошибки на каком-то этапе и на основании поступившего кода,
// формирует сообщение с описанием произошедшей ошибки
printf("begin my_diag, status: %i\n", status); // debug
// Выбор сообщения в зависимости от кода ошибки
switch (status)
{
case 0: // Специальный случай - вывод справки по использованию
printf("Usage: ./DosAtk [options]\n"
"Required:\n"
" -a, --attack TYPE Type of attack (scan|flood)\n"
" -d, --domain DOMAIN Target domain\n"
" -i, --ip IP Target IP\n"
" -p, --port PORT Port. Required only for flood type!\n"
"Optional:\n"
" -l, --log FILE Log file\n"
" -t, --telegram ID Telegram ID\n"
" -b, --token TOKEN Telegram bot token\n");
break;
case -1: // Некорректный тип атаки
printf("Error: Invalid attack type!\n--help for more info\n");
break;
case -10: // Отсутствуют обязательные параметры для сканирования портов
printf("Error: Missing required parameters for port scanning!\n--help for more info\n");
break;
case -20: // Отсутствуют обязательные параметры для SYN flood атаки
printf("Error: Missing required parameters for tcp syn dos attack!\n--help for more info\n");
break;
case -101: // Встречена неизвестная опция командной строки
printf("Error: Unknown option!\n.--help for info\n");
break;
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;
}
// Отладочный вывод - завершение работы функции
printf("end my_diag\n"); // debug
}
// Функция для экранирования специальных символов в строке перед использованием в JSON
// Принимает: const std::string& s - исходная строка для обработки
// Возвращает: std::string - строка с экранированными спецсимволами
std::string escape_json(const std::string& s) {
std::string result; // Результирующая строка с экранированными символами
// Проходим по каждому символу входной строки
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; // Индекс, с которого начинать проверку цифр
if (s.empty()) return false; // Пустая строка не может быть числом
// Проверяем наличие знака минус в начале
start = 0;
if (s[0] == '-') {
// Строка из одного минуса не является числом
if (s.size() == 1) return false;
// Если минус есть, начинаем проверку с следующего символа
start = 1;
}
// Проверяем все оставшиеся символы на цифры
for (size_t i = start; i < s.size(); ++i) {
// Найден нецифровой символ - строка не число
if (!isdigit(s[i])) return false;
}
// Все проверки пройдены - строка является числом
return true;
}
void 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 = nullptr;
// Проверка наличия обязательных параметров 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
}
void my_log()
{
// Данная функция записывает в файл лога сообщение
printf("start my_log"); // debug
printf("end my_log"); // debug
}
void my_fin()
{
// Данная процедура завершает программу и рассчитывает итоговое время выполнения программы
// Объявления
time_t end_time_t; // Время завершения выполненя программы
std::chrono::duration duration; // Длительность выполнения программы
std::chrono::hours hours; // Компонента часов времени завершения
std::chrono::minutes minutes; // Компонента минут времени завершения
std::chrono::seconds seconds; // Компонента секунд времени завершения
std::chrono::milliseconds milliseconds; // Компонента миллисекунд времени завершения
// Иницаализация
end_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
duration = end_timestamp - start_timestamp; // Вычисляем продолжительность работы программы
hours = std::chrono::duration_cast<std::chrono::hours>(duration); // Разбиваем продолжительность на компоненты
minutes = std::chrono::duration_cast<std::chrono::minutes>(duration % std::chrono::hours(1)); // Разбиваем продолжительность на компоненты
seconds = std::chrono::duration_cast<std::chrono::seconds>(duration % std::chrono::minutes(1)); // Разбиваем продолжительность на компоненты
milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration % std::chrono::seconds(1)); // Разбиваем продолжительность на компоненты
double total_seconds
printf("begin my_diag\n"); // debug
std::cout << "Worked for "; // Выводим информацию о времени работы
// Для коротких периодов (<2 минут) выводим в секундах с дробной частью
if (duration < std::chrono::minutes(2))
{
total_seconds = std::chrono::duration<double>(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; // После осуществления вывода переходим на новую строку
// Выводим статистику по запросам
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) << milliseconds.count() << std::endl;
// Завершаем программу с кодом состояния
std::exit(status);
}
void my_dns()
{
// Данная процедура сопостовляет доменное имя с IP
printf("start my_dns"); // debug
printf("end my_dns"); // debug
}
void my_tcp_syn()
{
// Данная процедура выполняет TCP SYN Flood атаку
printf("start my_tcp_syn"); // debug
printf("end my_tcp_syn"); // debug
}
void my_udp()
{
// Данная процедура выполняет UDP Flood (port scanning) атаку
printf("start my_udp"); // debug
printf("end my_udp"); // debug
}
int main(int arg_ctr, char **arg_ptr)
{
// Инициализация глобальных переменных
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;
// Тело программы
// Проверяем параметры командной строки
my_check_params();
// Обрабатываем результат проверки параметров
switch (status)
{
case 1: // Режим сканирования портов (UDP)
// Пытаемся разрешить DNS (если указано доменное имя)
my_dns();
if (status == 0) // Если DNS разрешен успешно
{
// Запускаем цикл UDP-атаки
while (true)
{
my_udp()
if (status == 2) // Код завершения атаки
{
break;
}
else if (status < 0) // Обработка ошибок
{
my_diag(); // Выводим диагностику
my_log(); // Пытаемся записать в лог
if (status == 1) // Если записать лог не удалось
{
my_msg(); // Отправляем сообщение
}
}
}
// Завершающие действия после атаки
my_log(); // Логируем завершение
my_msg(); // Отправляем финальное сообщение
my_fin(); // Корректно завершаем программу
}
else if (status == 1) // Ошибка DNS-разрешения
{
my_diag(); // Выводим ошибку
my_log(); // Логируем ошибку
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (log_status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
}
break;
case 2: // Режим SYN-флуда (TCP)
// Пытаемся разрешить DNS (если указано доменное имя)
my_dns();
if (status == 0) // Если DNS разрешен успешно
{
// Запускаем цикл UDP-атаки
while (true)
{
my_tcp_syn()
if (status == 2) // Код завершения атаки
{
break;
}
else if (status < 0) // Обработка ошибок
{
my_diag(); // Выводим диагностику
my_log(); // Пытаемся записать в лог
if (status == 1) // Если записать лог не удалось
{
my_msg(); // Отправляем сообщение
}
}
}
// Завершающие действия после атаки
my_log(); // Логируем завершение
my_msg(); // Отправляем финальное сообщение
my_fin(); // Корректно завершаем программу
}
else if (status == 1) // Ошибка DNS-разрешения
{
my_diag(); // Выводим ошибку
my_log(); // Логируем ошибку
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (log_status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
}
break;
case 0: // Некорректные параметры или запрос справки
case -1:
case -10:
case -20:
case -101:
case -600:
my_diag(); // Выводим диагностику
my_log(); // Логируем событие
if (status == 0) // Без уведомления
{
my_fin(); // Завершаем программу
}
else if (status == 1) // С уведомлением
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
break;
}
return 0; // Возвращаем код успешного завершения
}