Actualiser install.sh

This commit is contained in:
2026-03-19 14:13:50 +00:00
parent 236076e3fd
commit ec7dc002d2

View File

@@ -1,681 +1,177 @@
import customtkinter as ctk #!/bin/bash
from tkinter import filedialog, messagebox
import os
import threading
import subprocess # --- Couleurs pour le terminal ---
import urllib.request
import urllib.error GREEN='\033[0;32m'
import time
import datetime BLUE='\033[0;34m'
import glob
import hashlib RED='\033[0;31m'
import string
import secrets NC='\033[0m' # No Color
import json
import platform
import shutil
from watchdog.observers import Observer echo -e "${BLUE}==========================================${NC}"
from watchdog.events import FileSystemEventHandler
echo -e "${GREEN} 🛡️ Installation de 7LnA Security V10.1 Quantum${NC}"
# ==========================================
# CONFIGURATION GLOBALE echo -e "${BLUE}==========================================${NC}\n"
# ==========================================
UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py"
QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements") # Arrêter le script immédiatement si une erreur critique survient
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
set -e
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# ========================================== # 1. Installation des dépendances système
# UTILITAIRES SYSTÈME
# ========================================== echo -e "${BLUE}[*] Étape 1 : Vérification et installation des dépendances système...${NC}"
def send_desktop_notification(title, message, is_critical=False):
try: sudo apt update
urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message]) # Ajout de arp-scan pour le WiFi Guard (V10) en plus du reste
except Exception:
pass sudo apt install -y clamav clamav-daemon rkhunter arp-scan cron python3-venv python3-full python3-tk libnotify-bin wget curl
class RealTimeShieldHandler(FileSystemEventHandler):
def __init__(self, app_instance):
self.app = app_instance # 1.1 Initialisation de rkhunter
def on_created(self, event):
if not event.is_directory: echo -e "\n${BLUE}[*] Étape 1.1 : Initialisation de la base de données anti-rootkit...${NC}"
time.sleep(1.5)
self.app.trigger_realtime_scan(event.src_path) sudo rkhunter --update --quiet || true
# ========================================== sudo rkhunter --propupd --quiet || true
# APPLICATION PRINCIPALE
# ========================================== echo -e "${GREEN}[+] Empreintes système enregistrées.${NC}"
class Antivirus7LnA(ctk.CTk):
def __init__(self):
super().__init__()
self.title("7LnA Security Suite - V10.1 Quantum Edition") # 2. Création du répertoire de l'application
self.geometry("1250x850")
self.minsize(1000, 700) echo -e "\n${BLUE}[*] Étape 2 : Création des dossiers de l'application...${NC}"
os.makedirs(QUARANTINE_DIR, exist_ok=True) APP_DIR="$HOME/.7lna_app"
if not os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "w") as f: json.dump([], f) mkdir -p "$APP_DIR"
self.shield_observer = None
self.shield_active = False
self.zero_usb_mode = False # <--- AJOUT : État du mode Zero USB # Auto-détection du script Python : Copie locale OU téléchargement
self.check_dependencies() if [ -f "7lna.py" ]; then
self.setup_ui()
echo -e "${GREEN}[+] Fichier local 7lna.py détecté. Copie en cours...${NC}"
threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
cp 7lna.py "$APP_DIR/"
def check_dependencies(self):
try: else
subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
self.clamav_installed = True echo -e "${RED}[!] Fichier 7lna.py introuvable localement.${NC}"
except:
self.clamav_installed = False echo -e "${BLUE}[*] Téléchargement automatique depuis le serveur officiel Git...${NC}"
def setup_ui(self): wget -qO "$APP_DIR/7lna.py" "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py"
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1) echo -e "${GREEN}[+] Code source téléchargé avec succès.${NC}"
# --- BARRE LATÉRALE --- fi
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_rowconfigure(10, weight=1)
# 3. Configuration de l'environnement virtuel (venv)
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))
echo -e "\n${BLUE}[*] Étape 3 : Configuration de l'environnement isolé (venv)...${NC}"
self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner") python3 -m venv "$APP_DIR/.venv"
self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit") # Exécution directe via le pip du venv
self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools")
self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine") "$APP_DIR/.venv/bin/pip" install --upgrade pip --quiet
self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule")
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") "$APP_DIR/.venv/bin/pip" install customtkinter watchdog --quiet
self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update")
echo -e "${GREEN}[+] Modules Python installés.${NC}"
self.version_label = ctk.CTkLabel(self.sidebar, text="v10.1 - Quantum", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
self.version_label.grid(row=10, column=0, pady=20, sticky="s")
self.views = {} # 4. Création du script de lancement
self.init_dashboard_view()
self.init_scanner_view() echo -e "\n${BLUE}[*] Étape 4 : Création du lanceur...${NC}"
self.init_realtime_view()
self.init_audit_view() LAUNCHER_PATH="$APP_DIR/launcher.sh"
self.init_tools_view()
self.init_quarantine_view() cat << EOF > "$LAUNCHER_PATH"
self.init_schedule_view()
self.init_history_view() #!/bin/bash
self.init_update_view()
# Exporter le display pour s'assurer que l'interface graphique s'ouvre bien
self.select_view("dashboard")
export DISPLAY=:0
def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent", "$APP_DIR/.venv/bin/python3" "$APP_DIR/7lna.py"
text_color="#D1D5DB", hover_color="#1F2937", font=ctk.CTkFont(size=15), height=40,
command=lambda: self.select_view(view_name)) EOF
btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew")
return btn chmod +x "$LAUNCHER_PATH"
def select_view(self, view_name):
for view in self.views.values(): view.grid_forget()
if view_name in self.views: # 5. Création de l'icône dans le menu des applications
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30)
if view_name == "quarantine": self.refresh_quarantine_list() echo -e "\n${BLUE}[*] Étape 5 : Intégration au menu du système...${NC}"
if view_name == "history": self.refresh_history_list()
DESKTOP_DIR="$HOME/.local/share/applications"
def setup_console_tags(self, console):
console.tag_config("danger", foreground="#EF4444") mkdir -p "$DESKTOP_DIR"
console.tag_config("success", foreground="#10B981")
console.tag_config("warning", foreground="#F59E0B") DESKTOP_FILE="$DESKTOP_DIR/7lna.desktop"
console.tag_config("info", foreground="#3B82F6")
def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ") cat << EOF > "$DESKTOP_FILE"
def log_threat_to_history(self, filepath, threat_name): [Desktop Entry]
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f) Name=7LnA Antivirus V10 Quantum
data.append({"date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "file": filepath, "threat": threat_name})
with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4) Comment=Suite de sécurité totale : ClamAV, Rootkits, OSINT, WiFi Guard
except: pass
Exec=$LAUNCHER_PATH
# --- AUTO-DETECT USB ---
def monitor_usb_drives(self): Icon=security-high
media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return Terminal=false
known_mounts = set(os.listdir(media_dir))
while True: Type=Application
time.sleep(3)
try: Categories=System;Security;Utility;
current_mounts = set(os.listdir(media_dir))
new_mounts = current_mounts - known_mounts Keywords=antivirus;security;clamav;scan;virus;rootkit;cleaner;wifi;network;osint;
for mount in new_mounts:
self.after(0, self.prompt_usb_scan, os.path.join(media_dir, mount), mount) EOF
known_mounts = current_mounts
except: pass chmod +x "$DESKTOP_FILE"
# <--- MODIFICATION : Logique Zero USB intégrée
def prompt_usb_scan(self, path, name):
if self.zero_usb_mode: # Rafraîchir la base de données des applications du bureau
send_desktop_notification("⚡ Zero USB Actif", f"Analyse automatique forcée pour : {name}", is_critical=True)
self.select_view("scanner") update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
else:
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 ?"): echo -e "\n${BLUE}==========================================${NC}"
self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() echo -e "${GREEN} ✅ INSTALLATION TERMINÉE AVEC SUCCÈS !${NC}"
# --- VUE : TABLEAU DE BORD --- echo -e "${BLUE}==========================================${NC}"
def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") echo -e "🛡️ Vous pouvez maintenant lancer ${GREEN}7LnA Antivirus V10 Quantum${NC} depuis votre menu d'applications."
self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) echo -e "💡 ${BLUE}Astuce : Utilisez le bouton 'MaJ Signatures' au premier lancement pour initialiser ClamAV.${NC}\n"
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_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=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")
# <--- AJOUT : Switch pour activer/désactiver le Mode Zero USB
self.zero_usb_switch = ctk.CTkSwitch(frame, text="🛡️ Mode Zero USB (Scan auto des clés USB)",
command=self.toggle_zero_usb,
font=ctk.CTkFont(size=16, weight="bold"),
progress_color="#DC2626") # Rouge pour indiquer un mode "agressif"
self.zero_usb_switch.pack(pady=20, anchor="w", padx=20)
# <--- AJOUT : Fonction de bascule pour le Switch
def toggle_zero_usb(self):
self.zero_usb_mode = self.zero_usb_switch.get()
etat = "ACTIVÉ" if self.zero_usb_mode else "DÉSACTIVÉ"
send_desktop_notification("Paramètre de Sécurité", f"Mode Zero USB {etat}")
# --- 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 :"):
dialog = ctk.CTkToplevel(self)
dialog.title(title)
dialog.geometry("400x230")
ctk.CTkLabel(dialog, text="⚠️ Privilèges Requis", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 5))
ctk.CTkLabel(dialog, text=msg, justify="center").pack(pady=5)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250)
pwd_entry.pack(pady=10)
def on_submit(event=None):
pwd = pwd_entry.get()
dialog.destroy()
if pwd:
callback_func(pwd)
ctk.CTkButton(dialog, text="Confirmer", command=on_submit, fg_color="#DC2626", hover_color="#991B1B").pack(pady=10)
dialog.bind("<Return>", on_submit)
dialog.wait_visibility()
dialog.attributes("-topmost", True)
dialog.grab_set()
pwd_entry.focus_set()
# --- VUE : SCANNER MANUEL ---
def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(4, weight=1)
frame.grid_columnconfigure((0, 1, 2), weight=1)
self.views["scanner"] = frame
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 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, 5), pady=5, sticky="ew")
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=(5, 5), pady=5, sticky="ew")
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt, height=45, fg_color="#059669", hover_color="#047857")
self.btn_db_update.grid(row=1, column=2, padx=(5, 0), pady=5, sticky="ew")
self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Chasse aux Rootkits (rkhunter)", command=self.run_rootkit_scan, height=45, fg_color="#7C3AED", hover_color="#6D28D9")
self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(5, 10), sticky="ew")
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10)
self.scan_progress.grid(row=3, column=0, columnspan=3, pady=(10, 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=4, column=0, columnspan=3, pady=20, sticky="nsew")
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")
def run_rootkit_scan(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start())
def _exec_rootkit(self, pwd):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de rkhunter (cela peut prendre du temps)...\n", "info")
self.scan_progress.start()
try:
cmd = ['sudo', '-S', 'rkhunter', '-c', '--sk', '--report-warnings-only']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
for line in iter(process.stdout.readline, ''):
if "incorrect password" in line.lower() or "try again" in line.lower():
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé par le système.\n", "danger")
process.terminate()
break
else:
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
if process.returncode in [0, 1]:
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success")
except FileNotFoundError:
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé.\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_console.see("end")
def update_virus_db_prompt(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._run_freshclam, args=(pwd,), daemon=True).start(), msg="La mise à jour des signatures ClamAV nécessite\nles droits sudo. Entrez votre mot de passe :")
def _run_freshclam(self, pwd):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de la mise à jour (freshclam)...\n", "info")
self.scan_progress.start()
self.btn_db_update.configure(state="disabled")
try:
cmd = ['sudo', '-S', 'freshclam']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
for line in iter(process.stdout.readline, ''):
if "incorrect password" in line.lower() or "try again" in line.lower():
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé.\n", "danger")
process.terminate()
break
else:
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()}[+] Signatures virales mises à jour avec succès.\n", "success")
except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur freshclam : {e}\n", "danger")
finally:
self.scan_progress.stop()
self.btn_db_update.configure(state="normal")
self.scan_console.see("end")
def start_manual_scan(self, is_dir):
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename()
if path: threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start()
# --- VUE : BOUCLIER TEMPS RÉEL ---
def init_realtime_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["shield"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
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=45)
self.btn_toggle_shield.pack(side="right")
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.setup_console_tags(self.rt_console)
def toggle_shield(self):
if not self.shield_active:
self.shield_observer = Observer()
self.shield_observer.schedule(RealTimeShieldHandler(self), WATCH_FOLDER, recursive=False)
self.shield_observer.start()
self.shield_active = True
self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success")
else:
self.shield_observer.stop()
self.shield_active = False
self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning")
def trigger_realtime_scan(self, filepath):
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()
# --- MOTEUR CLAMAV ---
def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: return
console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
if console == self.scan_console: self.scan_progress.start()
try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
if is_dir: cmd.append('-r')
cmd.append(path)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
infected = 0
for line in process.stdout:
clean_line = line.strip()
if "FOUND" in clean_line:
threat_name = clean_line.split(":")[-1].replace("FOUND", "").strip()
console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger")
infected += 1
self.log_threat_to_history(path, threat_name)
elif clean_line: console.insert("end", f" -> {clean_line}\n")
console.see("end")
process.wait()
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)
except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
if console == self.scan_console: self.scan_progress.stop()
console.see("end")
# --- VUE : AUDIT & PARE-FEU (AVEC WIFI GUARD V10.1) ---
def init_audit_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame
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.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", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="🛡️ Vérifier UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="📡 WiFi Guard (Scanner Intrus)", command=self.run_wifi_guard_prompt, fg_color="#059669", hover_color="#047857", height=40).pack(side="left")
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")
def run_audit_thread(self):
self.audit_console.delete("0.0", "end")
threading.Thread(target=self.perform_audit, daemon=True).start()
def perform_audit(self):
try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass
try:
res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True)
self.audit_console.insert("end", "\n" + "\n".join(res.split('\n')[:11]) + "\n")
except: pass
def check_firewall(self):
self.audit_console.delete("0.0", "end")
try:
res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip()
if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n")
else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n")
except: pass
# ----- LOGIQUE WIFI GUARD -----
def run_wifi_guard_prompt(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_wifi_guard, args=(pwd,), daemon=True).start(), title="WiFi Guard", msg="Le scan réseau profond nécessite les droits sudo.\nEntrez votre mot de passe session :")
def _exec_wifi_guard(self, pwd):
self.audit_console.delete("0.0", "end")
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Démarrage du scan réseau (arp-scan)... \n", "info")
if not shutil.which("arp-scan"):
self.audit_console.insert("end", f"{self.get_time_prefix()}[!] Erreur : arp-scan introuvable.\n", "danger")
return
try:
cmd = ['sudo', '-S', 'arp-scan', '--localnet', '--ignoredups']
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n')
process.stdin.flush()
count = 0
for line in iter(process.stdout.readline, ''):
if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]):
continue
if "incorrect password" in line.lower() or "try again" in line.lower():
self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé.\n", "danger")
break
if line.strip() and (line[0].isdigit()):
self.audit_console.insert("end", f" 📱 Appareil trouvé : {line.strip()}\n", "success")
count += 1
self.audit_console.see("end")
process.wait()
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:
send_desktop_notification("WiFi Guard", f"Attention : Beaucoup d'appareils ({count}) sont connectés à votre réseau.", is_critical=True)
except Exception as e:
self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger")
# --- VUE : OUTILS AVANCÉS (V10) ---
def init_tools_view(self):
frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame
ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
# Vérificateur de Fuites de Données (OSINT)
breach_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
breach_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(breach_card, text="🌐 Vérificateur de Fuites (OSINT)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#A855F7").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(breach_card, text="Vérifiez si votre adresse e-mail a été compromise dans une fuite de données (Dark Web).", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
input_frame = ctk.CTkFrame(breach_card, fg_color="transparent")
input_frame.pack(fill="x", padx=20, pady=5)
self.email_entry = ctk.CTkEntry(input_frame, placeholder_text="Entrez votre e-mail...", width=300)
self.email_entry.pack(side="left", padx=(0, 10))
ctk.CTkButton(input_frame, text="Vérifier l'e-mail", fg_color="#9333EA", hover_color="#7E22CE", command=self.run_breach_check).pack(side="left")
self.breach_result_var = ctk.StringVar(value="")
self.breach_result_label = ctk.CTkLabel(breach_card, textvariable=self.breach_result_var, text_color="#D1D5DB", justify="left")
self.breach_result_label.pack(anchor="w", padx=20, pady=10)
# Nettoyeur
clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
clean_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(clean_card, text="🧹 Nettoyeur Système", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkButton(clean_card, text="Vider le Cache et Corbeille", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20, pady=10)
# Destructeur
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
shred_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkButton(shred_card, text="Détruire un fichier", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20, pady=10)
# --- LOGIQUE OSINT ---
def run_breach_check(self):
email = self.email_entry.get().strip()
if not email or "@" not in email:
self.breach_result_var.set("❌ Veuillez entrer une adresse e-mail valide.")
self.breach_result_label.configure(text_color="#EF4444")
return
self.breach_result_var.set("⏳ Interrogation des bases de données...")
self.breach_result_label.configure(text_color="#3B82F6")
threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start()
def _exec_breach_check(self, email):
try:
url = f"https://api.xposedornot.com/v1/check-email/{email}"
req = urllib.request.Request(url, headers={'User-Agent': '7LnA-Security-Suite'})
try:
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode())
if "breaches" in data and data["breaches"]:
breaches = data["breaches"][0]
breach_count = len(breaches)
breach_names = ", ".join(breaches[:4])
suffix = "..." if breach_count > 4 else ""
self.breach_result_var.set(f"🚨 DANGER : E-mail trouvé dans {breach_count} fuite(s) !\nSources connues : {breach_names}{suffix}\nChangez vos mots de passe immédiatement.")
self.breach_result_label.configure(text_color="#EF4444")
else:
self.breach_result_var.set("✅ Bonne nouvelle : Aucune fuite détectée pour cet e-mail.")
self.breach_result_label.configure(text_color="#10B981")
except urllib.error.HTTPError as e:
if e.code == 404:
self.breach_result_var.set("✅ Bonne nouvelle : Aucune fuite détectée pour cet e-mail.")
self.breach_result_label.configure(text_color="#10B981")
else:
self.breach_result_var.set(f"❌ Erreur serveur lors de la vérification ({e.code}).")
self.breach_result_label.configure(text_color="#F59E0B")
except Exception as e:
self.breach_result_var.set("❌ Impossible de contacter le serveur d'OSINT. Vérifiez votre connexion.")
self.breach_result_label.configure(text_color="#F59E0B")
def run_cleaner(self):
paths_to_clean = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
for path in paths_to_clean:
if os.path.exists(path):
for item in os.listdir(path):
item_path = os.path.join(path, item)
try:
if os.path.isfile(item_path): os.remove(item_path)
elif os.path.isdir(item_path): shutil.rmtree(item_path)
except: pass
messagebox.showinfo("Succès", "Nettoyage terminé.")
def run_shredder(self):
filepath = filedialog.askopenfilename()
if not filepath: return
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
try:
file_size = os.path.getsize(filepath)
for _ in range(3):
with open(filepath, "ba+", buffering=0) as f:
f.seek(0)
f.write(os.urandom(file_size))
os.remove(filepath)
messagebox.showinfo("Succès", "Fichier détruit.")
except: pass
# ----- PLANIFICATION -----
def init_schedule_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["schedule"] = frame
ctk.CTkLabel(frame, text="Planification de Scans", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Automatisez l'analyse via les tâches Cron.", text_color="#9CA3AF").pack(anchor="w", pady=(0, 20))
card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(card, text="Programmer un scan automatique (Dossier Téléchargements)", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 5))
time_frame = ctk.CTkFrame(card, fg_color="transparent")
time_frame.pack(pady=(5, 15))
ctk.CTkLabel(time_frame, text="Heure d'exécution :").pack(side="left", padx=5)
self.cron_h_var = ctk.StringVar(value="12")
self.cron_m_var = ctk.StringVar(value="00")
ctk.CTkComboBox(time_frame, values=[f"{i:02d}" for i in range(24)], variable=self.cron_h_var, width=70).pack(side="left", padx=5)
ctk.CTkLabel(time_frame, text="h").pack(side="left")
ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=70).pack(side="left", padx=5)
ctk.CTkButton(card, text="✅ Activer le Scan Quotidien", fg_color="#2563EB", hover_color="#1D4ED8", command=self.setup_cronjob).pack(pady=5)
ctk.CTkButton(card, text="❌ Désactiver la planification", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=5)
def setup_cronjob(self):
h = self.cron_h_var.get()
m = self.cron_m_var.get()
cron_command = f"{int(m)} {int(h)} * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1"
try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -')
messagebox.showinfo("Succès", f"Scan configuré tous les jours à {h}:{m}.")
except: messagebox.showerror("Erreur", "Impossible de configurer Cron.")
def remove_cronjob(self):
try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
messagebox.showinfo("Succès", "Planification désactivée.")
except: pass
# --- VUE : HISTORIQUE ---
def init_history_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["history"] = frame
ctk.CTkLabel(frame, text="📜 Registre des Menaces", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.history_console.grid(row=1, column=0, sticky="nsew")
def refresh_history_list(self):
self.history_console.delete("0.0", "end")
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
if not data: self.history_console.insert("end", "\n ✅ Historique vierge.\n")
else:
for entry in reversed(data):
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n")
except: pass
# --- 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:
for f in files: self.quarantine_listbox.insert("end", f" -> {os.path.basename(f)}\n")
def empty_quarantine(self):
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if files and 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 ---
def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
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_soft.pack(anchor="w")
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)
def run_software_update(self):
self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", "[*] Négociation avec serveur...\n")
try:
urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__))
self.update_console.insert("end", "[+] Mise à jour réussie !\n")
messagebox.showinfo("Succès", "Redémarrage requis.")
self.destroy()
except:
self.update_console.insert("end", "[-] Échec.\n")
self.btn_update_soft.configure(state="normal")
if __name__ == "__main__":
app = Antivirus7LnA()
app.mainloop()