Actualiser 7lna.py

This commit is contained in:
2026-03-05 08:32:08 +00:00
parent 73304456dc
commit a9ab09e49c

317
7lna.py
View File

@@ -6,6 +6,12 @@ import subprocess
import urllib.request import urllib.request
import time import time
import random import random
import platform
import datetime
import glob
import hashlib
import string
import secrets
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@@ -19,6 +25,17 @@ WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
ctk.set_appearance_mode("dark") ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue") ctk.set_default_color_theme("blue")
# ==========================================
# UTILITAIRES SYSTÈME
# ==========================================
def send_desktop_notification(title, message, is_critical=False):
"""Envoie une notification native sous Linux"""
try:
urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message])
except Exception:
pass # Ignore si notify-send n'est pas installé
# ========================================== # ==========================================
# GESTIONNAIRE DU BOUCLIER TEMPS RÉEL # GESTIONNAIRE DU BOUCLIER TEMPS RÉEL
# ========================================== # ==========================================
@@ -28,7 +45,7 @@ class RealTimeShieldHandler(FileSystemEventHandler):
def on_created(self, event): def on_created(self, event):
if not event.is_directory: if not event.is_directory:
time.sleep(1.5) time.sleep(1.5) # Laisse le temps au fichier d'être copié/téléchargé
self.app.trigger_realtime_scan(event.src_path) self.app.trigger_realtime_scan(event.src_path)
# ========================================== # ==========================================
@@ -37,9 +54,9 @@ class RealTimeShieldHandler(FileSystemEventHandler):
class Antivirus7LnA(ctk.CTk): class Antivirus7LnA(ctk.CTk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("7LnA Security Suite - Advanced Edition") self.title("7LnA Security Suite - Ultimate Edition V7")
self.geometry("1100x700") self.geometry("1200x800")
self.minsize(900, 600) self.minsize(950, 650)
os.makedirs(QUARANTINE_DIR, exist_ok=True) os.makedirs(QUARANTINE_DIR, exist_ok=True)
self.shield_observer = None self.shield_observer = None
@@ -60,21 +77,22 @@ class Antivirus7LnA(ctk.CTk):
self.grid_columnconfigure(1, weight=1) self.grid_columnconfigure(1, weight=1)
# --- BARRE LATÉRALE --- # --- BARRE LATÉRALE ---
self.sidebar = ctk.CTkFrame(self, width=240, corner_radius=0, fg_color="#111827") self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0, fg_color="#111827")
self.sidebar.grid(row=0, column=0, sticky="nsew") self.sidebar.grid(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(7, weight=1) self.sidebar.grid_rowconfigure(8, weight=1)
ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=28, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=30) ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=30, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=(30, 20))
self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard") self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner") self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner")
self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield") self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit") self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit")
self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools") self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 6, "update") self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 7, "update")
self.version_label = ctk.CTkLabel(self.sidebar, text="v6.0 - Advanced", text_color="#6B7280") self.version_label = ctk.CTkLabel(self.sidebar, text="v7.0 - Ultimate", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
self.version_label.grid(row=7, column=0, pady=20, sticky="s") self.version_label.grid(row=8, column=0, pady=20, sticky="s")
# --- CONTENEUR DES VUES --- # --- CONTENEUR DES VUES ---
self.views = {} self.views = {}
@@ -83,15 +101,16 @@ class Antivirus7LnA(ctk.CTk):
self.init_realtime_view() self.init_realtime_view()
self.init_audit_view() self.init_audit_view()
self.init_tools_view() self.init_tools_view()
self.init_quarantine_view()
self.init_update_view() self.init_update_view()
self.select_view("dashboard") self.select_view("dashboard")
def create_nav_button(self, text, row, view_name): def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent", btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent",
text_color="#D1D5DB", hover_color="#1F2937", font=ctk.CTkFont(size=14), text_color="#D1D5DB", hover_color="#1F2937", font=ctk.CTkFont(size=15), height=40,
command=lambda: self.select_view(view_name)) command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=20, pady=10, sticky="ew") btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew")
return btn return btn
def select_view(self, view_name): def select_view(self, view_name):
@@ -99,51 +118,93 @@ class Antivirus7LnA(ctk.CTk):
view.grid_forget() view.grid_forget()
if view_name in self.views: if view_name in self.views:
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30) self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30)
if view_name == "quarantine":
self.refresh_quarantine_list()
# --- UTILITAIRES DE CONSOLE (COULEURS) --- # --- UTILITAIRES DE CONSOLE ---
def setup_console_tags(self, console): def setup_console_tags(self, console):
"""Ajoute le support des couleurs dans les consoles textuelles""" console.tag_config("danger", foreground="#EF4444")
console.tag_config("danger", foreground="#EF4444") # Rouge console.tag_config("success", foreground="#10B981")
console.tag_config("success", foreground="#10B981") # Vert console.tag_config("warning", foreground="#F59E0B")
console.tag_config("warning", foreground="#F59E0B") # Orange console.tag_config("info", foreground="#3B82F6")
console.tag_config("info", foreground="#3B82F6") # Bleu
def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ")
# --- VUE : TABLEAU DE BORD --- # --- VUE : TABLEAU DE BORD ---
def init_dashboard_view(self): def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["dashboard"] = frame self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=28, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15) status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15)
status_card.pack(fill="x", pady=10, ipady=20) status_card.pack(fill="x", pady=10, ipady=20)
status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable" status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=20, weight="bold"), text_color="white").pack(expand=True) ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=24, weight="bold"), text_color="white").pack(expand=True)
sys_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
sys_frame.pack(fill="x", pady=10, ipady=10)
sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}"
ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w")
info_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) info_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
info_frame.pack(fill="x", pady=20, ipady=10) info_frame.pack(fill="x", pady=10, ipady=10)
ctk.CTkLabel(info_frame, text=f"📂 Dossier Quarantaine : {QUARANTINE_DIR}\n📁 Dossier Surveillé : {WATCH_FOLDER}", justify="left", font=ctk.CTkFont(size=14)).pack(padx=20, pady=10, anchor="w") ctk.CTkLabel(info_frame, text=f"📂 Dossier Quarantaine : {QUARANTINE_DIR}\n📁 Dossier Surveillé : {WATCH_FOLDER}", justify="left", font=ctk.CTkFont(size=14)).pack(padx=20, pady=10, anchor="w")
# --- VUE : SCANNER MANUEL --- # --- VUE : SCANNER MANUEL ---
def init_scanner_view(self): def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1) frame.grid_rowconfigure(3, weight=1)
frame.grid_columnconfigure((0, 1), weight=1) frame.grid_columnconfigure((0, 1, 2), weight=1)
self.views["scanner"] = frame self.views["scanner"] = frame
ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=28, weight="bold")).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 20))
self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser un Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8") self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8")
self.btn_scan_f.grid(row=1, column=0, padx=(0, 10), sticky="ew") self.btn_scan_f.grid(row=1, column=0, padx=(0, 5), sticky="ew")
self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser un Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA") self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA")
self.btn_scan_d.grid(row=1, column=1, padx=(10, 0), sticky="ew") self.btn_scan_d.grid(row=1, column=1, padx=(5, 5), sticky="ew")
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827") self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures (freshclam)", command=self.update_virus_db, height=45, fg_color="#059669", hover_color="#047857")
self.scan_console.grid(row=2, column=0, columnspan=2, pady=20, sticky="nsew") self.btn_db_update.grid(row=1, column=2, padx=(5, 0), sticky="ew")
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10)
self.scan_progress.grid(row=2, column=0, columnspan=3, pady=(20, 0), sticky="ew")
self.scan_progress.set(0)
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.scan_console.grid(row=3, column=0, columnspan=3, pady=20, sticky="nsew")
self.setup_console_tags(self.scan_console) self.setup_console_tags(self.scan_console)
self.scan_console.insert("end", "[*] Moteur prêt. En attente de cible...\n", "info") self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V7 prêt...\n", "info")
def update_virus_db(self):
threading.Thread(target=self._run_freshclam, daemon=True).start()
def _run_freshclam(self):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Mise à jour des signatures...\n", "info")
self.scan_progress.start()
self.btn_db_update.configure(state="disabled")
try:
process = subprocess.Popen(['freshclam'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
for line in process.stdout:
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
if process.returncode == 0:
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Base de données virale à jour.\n", "success")
send_desktop_notification("Mise à jour réussie", "Les signatures ClamAV sont à jour.")
else:
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Échec ou droits root requis (sudo freshclam).\n", "warning")
except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
self.scan_progress.stop()
self.scan_progress.set(0)
self.btn_db_update.configure(state="normal")
self.scan_console.see("end")
def start_manual_scan(self, is_dir): def start_manual_scan(self, is_dir):
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename() path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename()
@@ -160,14 +221,14 @@ class Antivirus7LnA(ctk.CTk):
header = ctk.CTkFrame(frame, fg_color="transparent") header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="⚡ Bouclier Actif", font=ctk.CTkFont(size=28, weight="bold")).pack(side="left") ctk.CTkLabel(header, text="⚡ Bouclier Actif", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
self.btn_toggle_shield = ctk.CTkButton(header, text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=40) self.btn_toggle_shield = ctk.CTkButton(header, text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=45, font=ctk.CTkFont(weight="bold"))
self.btn_toggle_shield.pack(side="right") self.btn_toggle_shield.pack(side="right")
self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827") self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.rt_console.grid(row=2, column=0, sticky="nsew") self.rt_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.rt_console) self.setup_console_tags(self.rt_console)
self.rt_console.insert("end", "[-] Bouclier en attente.\n") self.rt_console.insert("end", f"{self.get_time_prefix()}[-] Sentinelle en attente d'activation.\n")
def toggle_shield(self): def toggle_shield(self):
if not self.shield_active: if not self.shield_active:
@@ -176,25 +237,30 @@ class Antivirus7LnA(ctk.CTk):
self.shield_observer.start() self.shield_observer.start()
self.shield_active = True self.shield_active = True
self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C") self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C")
self.rt_console.insert("end", f"\n[+] BOUCLIER ARMÉ sur {WATCH_FOLDER}\n", "success") self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success")
send_desktop_notification("Bouclier Activé", f"Surveillance en direct de {WATCH_FOLDER}")
else: else:
self.shield_observer.stop() self.shield_observer.stop()
self.shield_active = False self.shield_active = False
self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857") self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857")
self.rt_console.insert("end", "\n[-] Bouclier désactivé.\n", "warning") self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning")
send_desktop_notification("Bouclier Désactivé", "La surveillance en temps réel est coupée.")
self.rt_console.see("end") self.rt_console.see("end")
def trigger_realtime_scan(self, filepath): def trigger_realtime_scan(self, filepath):
self.rt_console.insert("end", f"\n⚡ Fichier capté : {os.path.basename(filepath)}\n", "info") self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info")
threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start() threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start()
# --- MOTEUR CLAMAV (Cœur) --- # --- MOTEUR CLAMAV (Cœur) ---
def run_clamav_scan(self, path, is_dir, console): def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: if not self.clamav_installed:
console.insert("end", "❌ Erreur critique : Moteur introuvable.\n", "danger") console.insert("end", f"{self.get_time_prefix()} Moteur introuvable.\n", "danger")
return return
console.insert("end", f"[*] Analyse en cours : {path}\n") console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
if console == self.scan_console:
self.scan_progress.start()
try: try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
if is_dir: cmd.append('-r') if is_dir: cmd.append('-r')
@@ -205,21 +271,27 @@ class Antivirus7LnA(ctk.CTk):
for line in process.stdout: for line in process.stdout:
clean_line = line.strip() clean_line = line.strip()
if "FOUND" in clean_line: if "FOUND" in clean_line:
console.insert("end", f"☠️ MENACE : {clean_line}\n", "danger") console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger")
console.insert("end", "🛡️ Action : Isolé en quarantaine.\n", "warning") console.insert("end", f"{self.get_time_prefix()}🛡️ Action : Fichier mis en quarantaine.\n", "warning")
infected += 1 infected += 1
else: elif clean_line:
console.insert("end", clean_line + "\n") console.insert("end", f" -> {clean_line}\n")
console.see("end") console.see("end")
process.wait() process.wait()
if infected == 0: if infected == 0:
console.insert("end", "[+] Cible sécurisée (Propre).\n", "success") console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success")
else: else:
console.insert("end", f"🚨 {infected} virus neutralisé(s) !\n", "danger") console.insert("end", f"{self.get_time_prefix()}🚨 {infected} MENACE(S) NEUTRALISÉE(S) !\n", "danger")
# Envoi d'une notification critique au système
send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s) et isolée(s) dans {os.path.basename(path)}.", is_critical=True)
except Exception as e: except Exception as e:
console.insert("end", f"❌ Panne moteur : {e}\n", "danger") console.insert("end", f"{self.get_time_prefix()}❌ Erreur Moteur : {e}\n", "danger")
console.see("end") finally:
if console == self.scan_console:
self.scan_progress.stop()
self.scan_progress.set(0)
console.see("end")
# --- VUE : AUDIT & PARE-FEU --- # --- VUE : AUDIT & PARE-FEU ---
def init_audit_view(self): def init_audit_view(self):
@@ -228,15 +300,15 @@ class Antivirus7LnA(ctk.CTk):
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame self.views["audit"] = frame
ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=28, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
btn_frame = ctk.CTkFrame(frame, fg_color="transparent") btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10)) btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
ctk.CTkButton(btn_frame, text="Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309").pack(side="left", padx=(0, 10)) ctk.CTkButton(btn_frame, text="Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="Vérifier Statut UFW (Pare-feu)", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151").pack(side="left") ctk.CTkButton(btn_frame, text="Vérifier Statut UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827") self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.audit_console.grid(row=2, column=0, sticky="nsew") self.audit_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.audit_console) self.setup_console_tags(self.audit_console)
@@ -245,12 +317,12 @@ class Antivirus7LnA(ctk.CTk):
threading.Thread(target=self.perform_audit, daemon=True).start() threading.Thread(target=self.perform_audit, daemon=True).start()
def perform_audit(self): def perform_audit(self):
self.audit_console.insert("end", "[*] Recherche de connexions fantômes (Backdoors)...\n", "info") self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Recherche de connexions fantômes (Ports ouverts)...\n", "info")
try: try:
self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True)) self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass except: pass
self.audit_console.insert("end", "\n[*] Top 10 Processus (Charge CPU)...\n", "info") self.audit_console.insert("end", f"\n{self.get_time_prefix()}[*] Top 10 Processus (Charge CPU)...\n", "info")
try: try:
res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True) res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True)
self.audit_console.insert("end", "\n".join(res.split('\n')[:11]) + "\n") self.audit_console.insert("end", "\n".join(res.split('\n')[:11]) + "\n")
@@ -258,85 +330,158 @@ class Antivirus7LnA(ctk.CTk):
def check_firewall(self): def check_firewall(self):
self.audit_console.delete("0.0", "end") self.audit_console.delete("0.0", "end")
self.audit_console.insert("end", "[*] Interrogation du pare-feu Ubuntu (UFW)...\n", "info") self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Interrogation du pare-feu Ubuntu (UFW)...\n", "info")
try: try:
# systemctl est utilisé pour éviter le prompt sudo de ufw
res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip() res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip()
if res == "active": if res == "active":
self.audit_console.insert("end", "✅ PARE-FEU UFW ACTIF ET EN LIGNE.\n", "success") self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n", "success")
else: else:
self.audit_console.insert("end", "⚠️ ATTENTION : PARE-FEU INACTIF.\nActivez-le via terminal avec 'sudo ufw enable'.\n", "danger") self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\nTerminal: 'sudo ufw enable'.\n", "danger")
except Exception: except Exception:
self.audit_console.insert("end", "[-] Impossible de déterminer l'état de UFW.\n", "warning") self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Impossible de déterminer l'état de UFW.\n", "warning")
# --- VUE : OUTILS AVANCÉS (NOUVEAU !) --- # --- VUE : OUTILS AVANCÉS (V7 - Scrollable) ---
def init_tools_view(self): def init_tools_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") # On utilise un CTkScrollableFrame pour empiler les outils proprement
frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame self.views["tools"] = frame
ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=28, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
# Outil 1 : Destructeur de fichiers # --- Outil 1 : Destructeur de fichiers ---
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
shred_card.pack(fill="x", pady=10, ipady=15) shred_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers (File Shredder)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(shred_card, text="🔥 Destructeur de fichiers sécurisé (File Shredder)", font=ctk.CTkFont(size=16, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20) ctk.CTkLabel(shred_card, text="Écrase le fichier avec des données aléatoires (3 passes) pour empêcher sa récupération.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
ctk.CTkLabel(shred_card, text="Écrase un fichier avec des données aléatoires (3 passes) avant de le supprimer.\nEmpêche toute récupération par un logiciel tiers.", justify="left", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 15))
ctk.CTkButton(shred_card, text="Détruire un fichier à jamais", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20) ctk.CTkButton(shred_card, text="Détruire un fichier à jamais", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20)
# --- Outil 2 : Calculateur de Hash ---
hash_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
hash_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(hash_card, text="🧬 Extracteur d'Empreintes (Hash)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#3B82F6").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(hash_card, text="Calcule les empreintes MD5 et SHA-256 d'un fichier pour vérifier sa légitimité.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
self.hash_result_var = ctk.StringVar(value="Aucun fichier sélectionné.")
ctk.CTkButton(hash_card, text="Sélectionner un fichier", fg_color="#2563EB", hover_color="#1D4ED8", command=self.calculate_hash).pack(anchor="w", padx=20, pady=(0, 10))
ctk.CTkEntry(hash_card, textvariable=self.hash_result_var, state="readonly", width=500, fg_color="#111827").pack(anchor="w", padx=20)
# --- Outil 3 : Générateur de Mot de Passe ---
pwd_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
pwd_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(pwd_card, text="🔑 Générateur de Mot de Passe Blindé", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(pwd_card, text="Génère un mot de passe cryptographiquement sécurisé (20 caractères).", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
self.pwd_result_var = ctk.StringVar(value="*****")
btn_pwd_frame = ctk.CTkFrame(pwd_card, fg_color="transparent")
btn_pwd_frame.pack(anchor="w", padx=20, fill="x")
ctk.CTkButton(btn_pwd_frame, text="Générer", fg_color="#059669", hover_color="#047857", command=self.generate_password).pack(side="left", padx=(0, 10))
ctk.CTkEntry(btn_pwd_frame, textvariable=self.pwd_result_var, state="normal", width=300, fg_color="#111827", font=ctk.CTkFont(family="Consolas", size=14)).pack(side="left")
def run_shredder(self): def run_shredder(self):
filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ LE FICHIER À DÉTRUIRE") filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ LE FICHIER À DÉTRUIRE")
if not filepath: return if not filepath: return
confirm = messagebox.askyesno("DANGER IRRÉVERSIBLE", f"Voulez-vous vraiment DÉTRUIRE ce fichier ?\n\n{filepath}\n\nCette action est permanente et ne va PAS dans la corbeille.") if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
if confirm:
threading.Thread(target=self.shred_file, args=(filepath,), daemon=True).start() threading.Thread(target=self.shred_file, args=(filepath,), daemon=True).start()
def shred_file(self, filepath): def shred_file(self, filepath):
try: try:
file_size = os.path.getsize(filepath) file_size = os.path.getsize(filepath)
# 3 passages de réécriture for _ in range(3):
for i in range(3):
with open(filepath, "ba+", buffering=0) as f: with open(filepath, "ba+", buffering=0) as f:
f.seek(0) f.seek(0)
f.write(os.urandom(file_size)) f.write(os.urandom(file_size))
# Suppression finale du système
os.remove(filepath) os.remove(filepath)
messagebox.showinfo("Succès", "Fichier pulvérisé avec succès. Récupération impossible.") messagebox.showinfo("Succès", "Fichier détruit de manière irréversible.")
except Exception as e: except Exception as e:
messagebox.showerror("Erreur Shredder", f"Impossible de détruire le fichier.\nErreur : {e}") messagebox.showerror("Erreur", f"Erreur : {e}")
def calculate_hash(self):
filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ UN FICHIER")
if not filepath: return
try:
sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
self.hash_result_var.set(f"SHA-256: {sha256_hash.hexdigest()}")
except Exception as e:
self.hash_result_var.set(f"Erreur de lecture.")
def generate_password(self):
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
pwd = ''.join(secrets.choice(alphabet) for i in range(20))
self.pwd_result_var.set(pwd)
# --- VUE : QUARANTAINE ---
def init_quarantine_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["quarantine"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="🗑️ Vider", fg_color="#DC2626", hover_color="#991B1B", command=self.empty_quarantine, height=40).pack(side="right")
self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10)
self.quarantine_listbox.grid(row=1, column=0, sticky="nsew")
def refresh_quarantine_list(self):
self.quarantine_listbox.delete("0.0", "end")
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files:
self.quarantine_listbox.insert("end", "\n ✅ Aucun fichier malveillant isolé.\n")
else:
self.quarantine_listbox.insert("end", f" ⚠️ {len(files)} menace(s) :\n\n")
for f in files:
size_kb = os.path.getsize(f) / 1024
self.quarantine_listbox.insert("end", f" -> {os.path.basename(f)} ({size_kb:.1f} KB)\n")
def empty_quarantine(self):
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files: return
if messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"):
for f in files:
try: os.remove(f)
except: pass
self.refresh_quarantine_list()
# --- VUE : MISE À JOUR (OTA) --- # --- VUE : MISE À JOUR (OTA) ---
def init_update_view(self): def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=28, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Restez synchronisé avec le serveur officiel 7ka1 Git.", text_color="#9CA3AF").pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Synchronisation avec le dépôt 7ka1 Git.", text_color="#9CA3AF", font=ctk.CTkFont(size=14)).pack(anchor="w", pady=(0, 20))
self.btn_update = ctk.CTkButton(frame, text="⬇️ Forcer la mise à jour depuis Gitea", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45) self.btn_update_soft = ctk.CTkButton(frame, text="⬇️ Mettre à jour", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45)
self.btn_update.pack(anchor="w") self.btn_update_soft.pack(anchor="w")
self.update_console = ctk.CTkTextbox(frame, height=150, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827") self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.update_console.pack(fill="x", pady=20) self.update_console.pack(fill="x", pady=20)
self.setup_console_tags(self.update_console) self.setup_console_tags(self.update_console)
def run_software_update(self): def run_software_update(self):
self.btn_update.configure(state="disabled") self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", "[*] Négociation avec git.7ka1.com...\n", "info") self.update_console.insert("end", f"{self.get_time_prefix()}[*] Négociation avec git.7ka1.com...\n", "info")
try: try:
current_file_path = os.path.abspath(__file__) current_file_path = os.path.abspath(__file__)
urllib.request.urlretrieve(UPDATE_URL, current_file_path) urllib.request.urlretrieve(UPDATE_URL, current_file_path)
self.update_console.insert("end", "[+] Code source remplacé avec succès !\n", "success") self.update_console.insert("end", f"{self.get_time_prefix()}[+] Code source mis à jour !\n", "success")
messagebox.showinfo("Mise à jour Appliquée", "Redémarrage requis pour initialiser la nouvelle version.") messagebox.showinfo("Succès", "Redémarrage requis.")
self.destroy() self.destroy()
except Exception as e: except Exception as e:
self.update_console.insert("end", f"[-] Échec de la liaison avec le serveur : {e}\n", "danger") self.update_console.insert("end", f"{self.get_time_prefix()}[-] Échec : {e}\n", "danger")
self.btn_update.configure(state="normal") self.btn_update_soft.configure(state="normal")
if __name__ == "__main__": if __name__ == "__main__":
app = Antivirus7LnA() app = Antivirus7LnA()