From 6031c45917622bb487013f2af66cfaca59222083 Mon Sep 17 00:00:00 2001 From: bilal Date: Tue, 21 Apr 2026 00:40:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?src=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ai_analyser.py | 21 +++++++++++++++ src/embedder.py | 14 ++++++++++ src/main.py | 66 +++++++++++++++++++++++++++++++++++++++++++++ src/parser.py | 31 +++++++++++++++++++++ src/vector_store.py | 36 +++++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 src/ai_analyser.py create mode 100644 src/embedder.py create mode 100644 src/main.py create mode 100644 src/parser.py create mode 100644 src/vector_store.py diff --git a/src/ai_analyser.py b/src/ai_analyser.py new file mode 100644 index 0000000..f255768 --- /dev/null +++ b/src/ai_analyser.py @@ -0,0 +1,21 @@ +def analyze_threat(self, packet_text, context): + prompt = f""" + ROLE: Senior CyberSecurity Researcher (Wireless Networks). + DATA: Current packet: {packet_text} + HISTORY_CONTEXT: {context} + + TASK: + 1. Проверь пакет на признаки известных атак: + - Deauthentication Flood (уязвимость 802.11w) + - Rogue AP / Evil Twin (несоответствие RSSI/MAC) + - WPA Handshake sniffing (EAPOL flow) + - WPS Pixie Dust / Brute Force + 2. Если это атака, укажи её название и CVE (если есть). + 3. Оцени риск по шкале от 1 до 10. + 4. Предложи команду для исправления (например, 'enable 802.11w' или 'disable WPS'). + + ОТВЕТЬ КРАТКО И ТЕХНИЧЕСКИ ТОЧНО. + """ + # Вызов Ollama API... + # Временная заглушка - в реальном приложении здесь будет вызов API + return "Анализ не реализован в текущей версии" diff --git a/src/embedder.py b/src/embedder.py new file mode 100644 index 0000000..9fc7bb7 --- /dev/null +++ b/src/embedder.py @@ -0,0 +1,14 @@ +import requests +import os + +class Embedder: + def __init__(self, model_name="qwen3-embedding:8b"): + self.model_name = model_name + self.ollama_url = os.getenv("OLLAMA_URL", "http://localhost:11434") + + def get_vector(self, text): + response = requests.post( + f"{self.ollama_url}/api/embeddings", + json={"model": self.model_name, "prompt": text} + ) + return response.json()["embedding"] diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..09104c5 --- /dev/null +++ b/src/main.py @@ -0,0 +1,66 @@ +import sys +import uuid +import json +import socket +from scapy.all import sniff +import os +from parser import PacketParser +from embedder import Embedder +from vector_store import VectorDB +from ai_analyser import AIAnalyser + +# Настройки моделей из вашего списка +EMBEDDING_MODEL = "qwen3-embedding:8b" +ANALYSIS_MODEL = "qwen3.5:35b-a3b" # или Qwen3-Coder для глубокого тех-анализа + +# Инициализация +embedder = Embedder(model_name=EMBEDDING_MODEL) +db = VectorDB(collection_name="wifi_lab", vector_size=4096) # Размерность Qwen 8B +ai = AIAnalyser() +parser = PacketParser() + +def process_packet(pkt): + # 1. Извлекаем признаки + text_desc = parser.parse_to_text(pkt) + if not text_desc: return + + # 2. Генерируем вектор через Ollama + vector = embedder.get_vector(text_desc) + + # 3. Ищем аномалии в Qdrant + # Мы смотрим на "дистанцию". Если похожих векторов нет (score низкий), + # значит поведение сети изменилось. + search_results = db.find_similar(vector) + + # Порог аномалии (экспериментально для Косинусного сходства < 0.7) + is_anomaly = len(search_results) == 0 or search_results[0].score < 0.7 + + # 4. Сохраняем в Qdrant для истории + db.add_packet(str(uuid.uuid4()), vector, {"subtype": pkt.subtype}, text_desc) + + if is_anomaly: + print(f"\n[!] ОБНАРУЖЕНА УЯЗВИМОСТЬ/АНОМАЛИЯ: {text_desc}") + # 5. Анализ через мощную Qwen 35B + context = [res.payload['text'] for res in search_results] + report = ai.analyze_threat(text_desc, context) + print(f"[AI REPORT]:\n{report}\n" + "-"*50) + +def main(): + HOST = '0.0.0.0' + PORT = 9999 + + print(f"[*] Анализатор слушает на {HOST}:{PORT}...") + + # Создаем серверный сокет + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((HOST, PORT)) + s.listen(1) + conn, addr = s.accept() + with conn: + print(f"[*] Соединение установлено с {addr}") + # Превращаем сокет в объект файла для Scapy + fileobj = conn.makefile(mode='rb') + sniff(opened_socket=fileobj, prn=process_packet, store=0) + +if __name__ == "__main__": + main() diff --git a/src/parser.py b/src/parser.py new file mode 100644 index 0000000..0b5891b --- /dev/null +++ b/src/parser.py @@ -0,0 +1,31 @@ +from scapy.all import Dot11, Dot11Deauth, Dot11Beacon, raw + +class PacketParser: + @staticmethod + def parse_to_text(pkt): + """Превращает пакет в текстовое описание для эмбеддинга""" + if not pkt.haslayer(Dot11): + return None + + # Извлекаем базовые параметры + info = { + "type": pkt.type, + "subtype": pkt.subtype, + "addr1": pkt.addr1, # Получатель + "addr2": pkt.addr2, # Отправитель + "rssi": getattr(pkt, 'dBm_AntSignal', 'N/A') # Мощность сигнала + } + + # Определяем тип фрейма человеческим языком + frame_types = {0: "Management", 1: "Control", 2: "Data"} + type_str = frame_types.get(pkt.type, "Unknown") + + # Формируем строку-описание + text_desc = f"Type: {type_str}, Subtype: {pkt.subtype}, From: {info['addr2']}, To: {info['addr1']}, RSSI: {info['rssi']}" + + # Добавляем часть полезной нагрузки (payload) если есть + payload = raw(pkt.payload).hex()[:64] + if payload: + text_desc += f", Payload: {payload}" + + return text_desc diff --git a/src/vector_store.py b/src/vector_store.py new file mode 100644 index 0000000..9699984 --- /dev/null +++ b/src/vector_store.py @@ -0,0 +1,36 @@ +import os +from qdrant_client import QdrantClient +from qdrant_client.models import Distance, VectorParams, PointStruct + +class VectorDB: + def __init__(self, collection_name="wifi_lab", vector_size=4096): + host = os.getenv("QDRANT_HOST", "localhost") + port = int(os.getenv("QDRANT_PORT", 6333)) + self.client = QdrantClient(host=host, port=port) + self.collection_name = collection_name + + # Создаем коллекцию, если её нет (размерность 4096 для Qwen 8B Embedding) + self.client.recreate_collection( + collection_name=self.collection_name, + vectors_config=VectorParams(size=vector_size, distance=Distance.COSINE), + ) + + def add_packet(self, pkt_id, vector, metadata, text): + self.client.upsert( + collection_name=self.collection_name, + points=[ + PointStruct( + id=pkt_id, + vector=vector, + payload={"metadata": metadata, "text": text} + ) + ] + ) + + def find_similar(self, vector): + return self.client.search( + collection_name=self.collection_name, + query_vector=vector, + limit=3, + with_payload=True + )