Actualiser 7lna.py

This commit is contained in:
2026-03-19 14:12:08 +00:00
parent e96f476121
commit 5b3d74607a

187
7lna.py
View File

@@ -14,9 +14,15 @@ import secrets
import json import json
import platform import platform
import shutil import shutil
import base64
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
# --- Imports pour le chiffrement AES-256 (Vault) ---
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
# ========================================== # ==========================================
# CONFIGURATION GLOBALE # CONFIGURATION GLOBALE
# ========================================== # ==========================================
@@ -24,20 +30,11 @@ UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/r
QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine") QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements") WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json") HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
VAULT_FILE = os.path.expanduser("~/.7lna_vault.enc") # Fichier chiffré du coffre-fort
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):
try:
urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message])
except Exception:
pass
class RealTimeShieldHandler(FileSystemEventHandler): class RealTimeShieldHandler(FileSystemEventHandler):
def __init__(self, app_instance): def __init__(self, app_instance):
self.app = app_instance self.app = app_instance
@@ -52,7 +49,7 @@ 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 - V10.1 Quantum Edition") self.title("7LnA Security Suite - V10.3 Quantum Edition")
self.geometry("1250x850") self.geometry("1250x850")
self.minsize(1000, 700) self.minsize(1000, 700)
@@ -62,13 +59,26 @@ class Antivirus7LnA(ctk.CTk):
self.shield_observer = None self.shield_observer = None
self.shield_active = False self.shield_active = False
self.zero_usb_mode = False # <--- AJOUT : État du mode Zero USB self.zero_usb_mode = False
self.gaming_mode = False # <--- État du mode Gaming/Discret
self.check_dependencies() self.check_dependencies()
self.setup_ui() self.setup_ui()
threading.Thread(target=self.monitor_usb_drives, daemon=True).start() threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
# --- UTILITAIRES SYSTÈME (Intégré à la classe pour gérer le Gaming Mode) ---
def send_desktop_notification(self, title, message, is_critical=False):
# Si le mode Gaming est actif et que l'alerte n'est pas critique, on bloque la notification
if self.gaming_mode and not is_critical:
return
try:
urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message])
except Exception:
pass
def check_dependencies(self): def check_dependencies(self):
try: try:
subprocess.run(['clamscan', '--version'], capture_output=True, check=True) subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
@@ -83,7 +93,7 @@ class Antivirus7LnA(ctk.CTk):
# --- BARRE LATÉRALE --- # --- BARRE LATÉRALE ---
self.sidebar = ctk.CTkFrame(self, width=260, 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(10, weight=1) self.sidebar.grid_rowconfigure(11, weight=1)
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)) 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))
@@ -92,13 +102,14 @@ class Antivirus7LnA(ctk.CTk):
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_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine") self.btn_vault = self.create_nav_button("🔐 Coffre-fort (AES)", 6, "vault") # <--- AJOUT BOUTON VAULT
self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule") self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 7, "quarantine")
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") self.btn_schedule = self.create_nav_button("📅 Planification", 8, "schedule")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update") self.btn_history = self.create_nav_button("📜 Rapports", 9, "history")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 10, "update")
self.version_label = ctk.CTkLabel(self.sidebar, text="v10.1 - Quantum", text_color="#6B7280", font=ctk.CTkFont(weight="bold")) self.version_label = ctk.CTkLabel(self.sidebar, text="v10.3 - Quantum", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
self.version_label.grid(row=10, column=0, pady=20, sticky="s") self.version_label.grid(row=11, column=0, pady=20, sticky="s")
self.views = {} self.views = {}
self.init_dashboard_view() self.init_dashboard_view()
@@ -106,6 +117,7 @@ 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_vault_view() # <--- INIT VAULT VIEW
self.init_quarantine_view() self.init_quarantine_view()
self.init_schedule_view() self.init_schedule_view()
self.init_history_view() self.init_history_view()
@@ -158,14 +170,13 @@ class Antivirus7LnA(ctk.CTk):
known_mounts = current_mounts known_mounts = current_mounts
except: pass except: pass
# <--- MODIFICATION : Logique Zero USB intégrée
def prompt_usb_scan(self, path, name): def prompt_usb_scan(self, path, name):
if self.zero_usb_mode: if self.zero_usb_mode:
send_desktop_notification("⚡ Zero USB Actif", f"Analyse automatique forcée pour : {name}", is_critical=True) self.send_desktop_notification("⚡ Zero USB Actif", f"Analyse automatique forcée pour : {name}", is_critical=True)
self.select_view("scanner") self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
else: else:
send_desktop_notification("USB Détectée", f"Disque {name} branché.") self.send_desktop_notification("USB Détectée", f"Disque {name} branché.")
if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB détecté :\n{name}\n\nVoulez-vous l'analyser ?"): if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB détecté :\n{name}\n\nVoulez-vous l'analyser ?"):
self.select_view("scanner") self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
@@ -186,18 +197,32 @@ class Antivirus7LnA(ctk.CTk):
sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}" 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") ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w")
# <--- AJOUT : Switch pour activer/désactiver le Mode Zero USB # Switch Zero USB
self.zero_usb_switch = ctk.CTkSwitch(frame, text="🛡️ Mode Zero USB (Scan auto des clés USB)", self.zero_usb_switch = ctk.CTkSwitch(frame, text="🛡️ Mode Zero USB (Scan auto des clés USB)",
command=self.toggle_zero_usb, command=self.toggle_zero_usb,
font=ctk.CTkFont(size=16, weight="bold"), font=ctk.CTkFont(size=16, weight="bold"),
progress_color="#DC2626") # Rouge pour indiquer un mode "agressif" progress_color="#DC2626")
self.zero_usb_switch.pack(pady=20, anchor="w", padx=20) self.zero_usb_switch.pack(pady=(20, 10), anchor="w", padx=20)
# Switch Mode Gaming/Discret
self.gaming_switch = ctk.CTkSwitch(frame, text="🎮 Mode Gaming / Discret (Silencieux & Basse Priorité CPU)",
command=self.toggle_gaming_mode,
font=ctk.CTkFont(size=16, weight="bold"),
progress_color="#8B5CF6")
self.gaming_switch.pack(pady=10, anchor="w", padx=20)
# <--- AJOUT : Fonction de bascule pour le Switch
def toggle_zero_usb(self): def toggle_zero_usb(self):
self.zero_usb_mode = self.zero_usb_switch.get() self.zero_usb_mode = self.zero_usb_switch.get()
etat = "ACTIVÉ" if self.zero_usb_mode else "DÉSACTIVÉ" etat = "ACTIVÉ" if self.zero_usb_mode else "DÉSACTIVÉ"
send_desktop_notification("Paramètre de Sécurité", f"Mode Zero USB {etat}") self.send_desktop_notification("Paramètre de Sécurité", f"Mode Zero USB {etat}")
def toggle_gaming_mode(self):
self.gaming_mode = self.gaming_switch.get()
if self.gaming_mode:
# Envoi d'une dernière notification avant de se taire
self.send_desktop_notification("🎮 Mode Gaming Activé", "Notifications muettes. L'antivirus tourne en basse priorité.", is_critical=True)
else:
self.send_desktop_notification("🎮 Mode Gaming Désactivé", "Les ressources système et notifications sont restaurées.")
# --- FONCTION D'AUTHENTIFICATION GLOBALE --- # --- FONCTION D'AUTHENTIFICATION GLOBALE ---
def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis pour cette action.\nEntrez votre mot de passe session :"): def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis pour cette action.\nEntrez votre mot de passe session :"):
@@ -253,7 +278,7 @@ class Antivirus7LnA(ctk.CTk):
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.scan_console.grid(row=4, column=0, columnspan=3, pady=20, sticky="nsew") self.scan_console.grid(row=4, 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", f"{self.get_time_prefix()}[*] Moteur de détection V10.1 prêt...\n", "info") self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V10.3 prêt...\n", "info")
def run_rootkit_scan(self): def run_rootkit_scan(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start()) self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start())
@@ -361,9 +386,15 @@ class Antivirus7LnA(ctk.CTk):
console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n") console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
if console == self.scan_console: self.scan_progress.start() if console == self.scan_console: self.scan_progress.start()
try: try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] cmd = []
# Si le Mode Gaming est actif, on utilise 'nice' pour baisser la priorité CPU du scan (sur Linux, nice 15 est très bas)
if self.gaming_mode:
cmd.extend(['nice', '-n', '15'])
cmd.extend(['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'])
if is_dir: cmd.append('-r') if is_dir: cmd.append('-r')
cmd.append(path) cmd.append(path)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
infected = 0 infected = 0
for line in process.stdout: for line in process.stdout:
@@ -377,7 +408,7 @@ class Antivirus7LnA(ctk.CTk):
console.see("end") console.see("end")
process.wait() process.wait()
if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success") if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success")
else: send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s).", is_critical=True) else: self.send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s).", is_critical=True)
except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger") except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally: finally:
if console == self.scan_console: self.scan_progress.stop() if console == self.scan_console: self.scan_progress.stop()
@@ -460,7 +491,7 @@ class Antivirus7LnA(ctk.CTk):
self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés sur votre WiFi.\n", "info") self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés sur votre WiFi.\n", "info")
if count > 15: if count > 15:
send_desktop_notification("WiFi Guard", f"Attention : Beaucoup d'appareils ({count}) sont connectés à votre réseau.", is_critical=True) self.send_desktop_notification("WiFi Guard", f"Attention : Beaucoup d'appareils ({count}) sont connectés à votre réseau.", is_critical=True)
except Exception as e: except Exception as e:
self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger") self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger")
@@ -565,6 +596,100 @@ class Antivirus7LnA(ctk.CTk):
messagebox.showinfo("Succès", "Fichier détruit.") messagebox.showinfo("Succès", "Fichier détruit.")
except: pass except: pass
# ==========================================
# --- VUE : COFFRE-FORT (NOUVEAU) ---
# ==========================================
def init_vault_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["vault"] = frame
ctk.CTkLabel(frame, text="🔐 Coffre-fort AES-256", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
# Panel de connexion
self.vault_login_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
self.vault_login_frame.grid(row=1, column=0, sticky="ew", ipady=20)
ctk.CTkLabel(self.vault_login_frame, text="Entrez le mot de passe maître :", font=ctk.CTkFont(size=16)).pack(pady=(20, 10))
self.vault_pwd_entry = ctk.CTkEntry(self.vault_login_frame, show="*", width=300)
self.vault_pwd_entry.pack(pady=10)
ctk.CTkButton(self.vault_login_frame, text="Déverrouiller / Créer", fg_color="#2563EB", hover_color="#1D4ED8", command=self.unlock_vault).pack(pady=10)
ctk.CTkLabel(self.vault_login_frame, text="Note: Si le coffre n'existe pas, ce mot de passe sera utilisé pour le créer.", text_color="#9CA3AF").pack()
# Panel de l'éditeur (caché par défaut)
self.vault_editor_frame = ctk.CTkFrame(frame, fg_color="transparent")
header_editor = ctk.CTkFrame(self.vault_editor_frame, fg_color="transparent")
header_editor.pack(fill="x", pady=(0, 10))
ctk.CTkLabel(header_editor, text="Vos notes sécurisées :", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(side="left")
ctk.CTkButton(header_editor, text="🔒 Sauvegarder et Verrouiller", fg_color="#059669", hover_color="#047857", command=self.save_vault).pack(side="right")
self.vault_textbox = ctk.CTkTextbox(self.vault_editor_frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10)
self.vault_textbox.pack(fill="both", expand=True)
self.current_vault_pwd = None
self.current_vault_salt = None
def _derive_key(self, password, salt):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000,
)
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
def unlock_vault(self):
pwd = self.vault_pwd_entry.get()
if not pwd: return
if os.path.exists(VAULT_FILE):
try:
with open(VAULT_FILE, "rb") as f: data = f.read()
salt = data[:16]
encrypted = data[16:]
key = self._derive_key(pwd, salt)
fernet = Fernet(key)
decrypted = fernet.decrypt(encrypted).decode()
# Succès !
self.current_vault_pwd = pwd
self.current_vault_salt = salt
self.show_vault_editor(decrypted)
except Exception:
messagebox.showerror("Accès Refusé", "Mot de passe incorrect ou fichier corrompu.")
else:
# Nouveau coffre
self.current_vault_pwd = pwd
self.current_vault_salt = os.urandom(16)
self.show_vault_editor("=== VOTRE COFFRE EST VIDE ===\n\nÉcrivez vos identifiants ou notes secrètes ici...")
def show_vault_editor(self, content):
self.vault_login_frame.grid_forget()
self.vault_editor_frame.grid(row=2, column=0, sticky="nsew")
self.vault_pwd_entry.delete(0, 'end')
self.vault_textbox.delete("0.0", "end")
self.vault_textbox.insert("end", content)
def save_vault(self):
data = self.vault_textbox.get("0.0", "end")
key = self._derive_key(self.current_vault_pwd, self.current_vault_salt)
fernet = Fernet(key)
encrypted = fernet.encrypt(data.encode())
with open(VAULT_FILE, "wb") as f_out:
f_out.write(self.current_vault_salt + encrypted)
self.current_vault_pwd = None
self.current_vault_salt = None
self.vault_textbox.delete("0.0", "end")
self.vault_editor_frame.grid_forget()
self.vault_login_frame.grid(row=1, column=0, sticky="ew", ipady=20)
self.send_desktop_notification("Coffre-fort", "Fichier chiffré avec succès et verrouillé.")
# ----- PLANIFICATION ----- # ----- PLANIFICATION -----
def init_schedule_view(self): def init_schedule_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")