Files
7LnA_Antivirus_Linux_Free_C…/7lna.py
2026-05-22 13:06:19 +00:00

675 lines
38 KiB
Python

import customtkinter as ctk
from tkinter import filedialog, messagebox
import os, threading, subprocess, urllib.request, urllib.error, time, datetime, glob, json, platform, shutil
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# ==========================================
# CONFIGURATION GLOBALE
# ==========================================
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")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
# Palette High-Tech 2026
BG_MAIN = "#0B0F19" # Fond ultra sombre
BG_CARD = "#161E2E" # Fond des cartes
BG_SIDEBAR = "#090B10" # Fond barre latérale
ACCENT_BLUE = "#3B82F6" # Bleu électrique
ACCENT_VIOLET = "#8B5CF6" # Violet néon (Zero USB)
TEXT_MAIN = "#F3F4F6"
TEXT_MUTED = "#9CA3AF"
ctk.set_appearance_mode("dark")
# ==========================================
# 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 Antivirus', title, message])
except Exception:
pass
class RealTimeShieldHandler(FileSystemEventHandler):
def __init__(self, app_instance):
self.app = app_instance
def on_created(self, event):
if not event.is_directory:
time.sleep(1.5)
self.app.trigger_realtime_scan(event.src_path)
# ==========================================
# APPLICATION PRINCIPALE
# ==========================================
class Antivirus7LnA(ctk.CTk):
def __init__(self):
super().__init__()
self.title("7lna Antivirus")
self.geometry("1250x850")
self.minsize(1050, 750)
self.configure(fg_color=BG_MAIN)
os.makedirs(QUARANTINE_DIR, exist_ok=True)
if not os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "w") as f: json.dump([], f)
self.shield_observer = None
self.shield_active = False
self.zero_usb_mode = False
self.check_dependencies()
self.setup_ui()
threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
def check_dependencies(self):
try:
subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
self.clamav_installed = True
except:
self.clamav_installed = False
# ==========================================
# INTERFACE GRAPHIQUE (DESIGN 2026)
# ==========================================
def setup_ui(self):
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
# --- BARRE LATÉRALE ---
self.sidebar = ctk.CTkFrame(self, width=280, corner_radius=0, fg_color=BG_SIDEBAR)
self.sidebar.grid(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(10, weight=1)
# Logo
logo_frame = ctk.CTkFrame(self.sidebar, fg_color="transparent")
logo_frame.grid(row=0, column=0, pady=(40, 30), sticky="ew")
ctk.CTkLabel(logo_frame, text="🛡️ 7lna", font=ctk.CTkFont(size=36, weight="bold"), text_color=ACCENT_BLUE).pack()
ctk.CTkLabel(logo_frame, text="A N T I V I R U S", font=ctk.CTkFont(size=12, weight="bold"), text_color=ACCENT_VIOLET).pack()
# Navigation
self.nav_buttons = {}
self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.create_nav_button("🔍 Scanner Système", 2, "scanner")
self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.create_nav_button("🌐 Audit & Réseau", 4, "audit")
self.create_nav_button("🧰 Boîte à Outils", 5, "tools")
self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.create_nav_button("📅 Automatisation", 7, "schedule")
self.create_nav_button("📜 Rapports", 8, "history")
# Bouton Mise à jour (Mise en évidence)
self.btn_update = ctk.CTkButton(self.sidebar, text="🔄 Rechercher MAJ", command=lambda: self.select_view("update"),
fg_color="#1E3A8A", hover_color=ACCENT_BLUE, font=ctk.CTkFont(size=14, weight="bold"), height=45)
self.btn_update.grid(row=9, column=0, padx=20, pady=20, sticky="ew")
self.version_label = ctk.CTkLabel(self.sidebar, text="V10.1 Quantum Edition", text_color=TEXT_MUTED, font=ctk.CTkFont(size=11))
self.version_label.grid(row=10, column=0, pady=20, sticky="s")
# --- VUES ---
self.views = {}
self.init_dashboard_view()
self.init_scanner_view()
self.init_realtime_view()
self.init_audit_view()
self.init_tools_view()
self.init_quarantine_view()
self.init_schedule_view()
self.init_history_view()
self.init_update_view()
self.select_view("dashboard")
def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent",
text_color=TEXT_MUTED, hover_color=BG_CARD, font=ctk.CTkFont(size=15, weight="bold"), height=45,
command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=15, pady=4, sticky="ew")
self.nav_buttons[view_name] = btn
def select_view(self, view_name):
# Réinitialiser les couleurs des boutons
for name, btn in self.nav_buttons.items():
btn.configure(fg_color="transparent", text_color=TEXT_MUTED)
# Activer le bouton cliqué (s'il existe dans le dictionnaire)
if view_name in self.nav_buttons:
self.nav_buttons[view_name].configure(fg_color=BG_CARD, text_color=TEXT_MAIN)
# Afficher la vue
for view in self.views.values(): view.grid_forget()
if view_name in self.views:
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=40, pady=40)
if view_name == "quarantine": self.refresh_quarantine_list()
if view_name == "history": self.refresh_history_list()
def setup_console_tags(self, console):
console.tag_config("danger", foreground="#EF4444")
console.tag_config("success", foreground="#10B981")
console.tag_config("warning", foreground="#F59E0B")
console.tag_config("info", foreground=ACCENT_BLUE)
console.tag_config("violet", foreground=ACCENT_VIOLET)
def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ")
def log_threat_to_history(self, filepath, threat_name):
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
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)
except: pass
# ==========================================
# LOGIQUE USB & MOTEUR
# ==========================================
def monitor_usb_drives(self):
media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return
known_mounts = set(os.listdir(media_dir))
while True:
time.sleep(3)
try:
current_mounts = set(os.listdir(media_dir))
new_mounts = current_mounts - known_mounts
for mount in new_mounts:
self.after(0, self.prompt_usb_scan, os.path.join(media_dir, mount), mount)
known_mounts = current_mounts
except: pass
def prompt_usb_scan(self, path, name):
if self.zero_usb_mode:
send_desktop_notification("⚡ Zero USB Actif", f"Analyse auto forcée : {name}", is_critical=True)
self.select_view("scanner")
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[⚡ ZERO USB] Scan automatique déclenché.\n", "violet")
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 détecté :\n{name}\n\nVoulez-vous l'analyser ?"):
self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis.\nEntrez votre mot de passe :"):
dialog = ctk.CTkToplevel(self)
dialog.title(title)
dialog.geometry("400x250")
dialog.configure(fg_color=BG_CARD)
ctk.CTkLabel(dialog, text="⚠️ Élévation de Privilèges", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 10))
ctk.CTkLabel(dialog, text=msg, justify="center", text_color=TEXT_MUTED).pack(pady=5)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250, height=40, border_color=ACCENT_BLUE)
pwd_entry.pack(pady=15)
def on_submit(event=None):
pwd = pwd_entry.get()
dialog.destroy()
if pwd: callback_func(pwd)
ctk.CTkButton(dialog, text="Autoriser", command=on_submit, fg_color=ACCENT_BLUE, hover_color="#2563EB", height=40).pack(pady=5)
dialog.bind("<Return>", on_submit)
dialog.wait_visibility()
dialog.attributes("-topmost", True)
dialog.grab_set()
pwd_entry.focus_set()
# ==========================================
# VUES (DASHBOARD, SCANNER, ETC.)
# ==========================================
def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_columnconfigure((0, 1), weight=1)
self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="Vue d'ensemble", font=ctk.CTkFont(size=38, weight="bold"), text_color=TEXT_MAIN).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 30))
# Carte Statut
status_color = "#064E3B" if self.clamav_installed else "#7F1D1D"
status_card = ctk.CTkFrame(frame, fg_color=status_color, corner_radius=15)
status_card.grid(row=1, column=0, sticky="nsew", padx=(0, 10), pady=10, ipady=30)
ctk.CTkLabel(status_card, text="Moteur Antivirus", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5))
status_text = "OPÉRATIONNEL" if self.clamav_installed else "HORS LIGNE"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=28, weight="bold"), text_color="white").pack()
# Carte Système
sys_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
sys_card.grid(row=1, column=1, sticky="nsew", padx=(10, 0), pady=10, ipady=30)
ctk.CTkLabel(sys_card, text="Environnement", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5))
sys_info = f"{platform.system()} {platform.release()}"
ctk.CTkLabel(sys_card, text=sys_info, font=ctk.CTkFont(size=22, weight="bold"), text_color=ACCENT_BLUE).pack()
ctk.CTkLabel(sys_card, text=f"Session : {os.getlogin()}", font=ctk.CTkFont(size=14), text_color=TEXT_MUTED).pack(pady=5)
# Carte Zero USB
usb_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15, border_width=2, border_color=ACCENT_VIOLET)
usb_card.grid(row=2, column=0, columnspan=2, sticky="nsew", pady=20, ipady=20)
ctk.CTkLabel(usb_card, text="Protection des Périphériques", font=ctk.CTkFont(size=20, weight="bold")).pack(pady=(20,10))
self.zero_usb_switch = ctk.CTkSwitch(usb_card, text="Activer le Mode Zero USB (Scan Auto)",
command=self.toggle_zero_usb, font=ctk.CTkFont(size=16, weight="bold"),
progress_color=ACCENT_VIOLET, button_color="#FFFFFF")
self.zero_usb_switch.pack(pady=10)
def toggle_zero_usb(self):
self.zero_usb_mode = self.zero_usb_switch.get()
etat = "ARMÉ" if self.zero_usb_mode else "DÉSACTIVÉ"
send_desktop_notification("Bouclier Quantum", f"Mode Zero USB {etat}")
def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["scanner"] = frame
# 1. En-tête
ctk.CTkLabel(frame, text="Analyse Système", font=ctk.CTkFont(size=38, weight="bold"), text_color=TEXT_MAIN).pack(anchor="w", pady=(0, 20))
# 2. Le Panneau de Contrôle (Une carte qui regroupe les actions)
control_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
control_card.pack(fill="x", pady=(0, 20), ipady=10)
control_card.grid_columnconfigure((0, 1, 2), weight=1)
# Boutons avec des couleurs profondes et professionnelles
self.btn_scan_f = ctk.CTkButton(control_card, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False),
height=45, fg_color="#1E3A8A", hover_color=ACCENT_BLUE, font=ctk.CTkFont(weight="bold"))
self.btn_scan_f.grid(row=0, column=0, padx=15, pady=15, sticky="ew")
self.btn_scan_d = ctk.CTkButton(control_card, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True),
height=45, fg_color="#1E3A8A", hover_color=ACCENT_BLUE, font=ctk.CTkFont(weight="bold"))
self.btn_scan_d.grid(row=0, column=1, padx=15, pady=15, sticky="ew")
self.btn_db_update = ctk.CTkButton(control_card, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt,
height=45, fg_color="#064E3B", hover_color="#059669", font=ctk.CTkFont(weight="bold"))
self.btn_db_update.grid(row=0, column=2, padx=15, pady=15, sticky="ew")
self.btn_rootkit = ctk.CTkButton(control_card, text="🕵️ Chasse aux Rootkits (rkhunter)", command=self.run_rootkit_scan,
height=45, fg_color="#4C1D95", hover_color=ACCENT_VIOLET, font=ctk.CTkFont(weight="bold"))
self.btn_rootkit.grid(row=1, column=0, columnspan=3, padx=15, pady=(0, 15), sticky="ew")
# 3. La barre de progression (Subtile, juste en dessous des contrôles)
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=4, progress_color=ACCENT_BLUE, fg_color="transparent")
self.scan_progress.pack(fill="x", pady=(0, 15))
self.scan_progress.set(0)
# 4. Le Terminal Géant (Prend tout l'espace restant)
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD,
corner_radius=15, border_width=1, border_color="#374151")
self.scan_console.pack(fill="both", expand=True)
self.setup_console_tags(self.scan_console)
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur d'analyse prêt.\n", "info")
# (Toutes tes méthodes internes restent identiques ici pour garantir le fonctionnement)
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", "violet")
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é.\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 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 nécessite les droits sudo :")
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()
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, ''):
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 mises à jour.\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()
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()
def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: return
console.insert("end", f"\n{self.get_time_prefix()}[*] Analyse de cible : {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()}[+] Cible 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")
# (J'ai appliqué le design "Carte" à toutes les autres vues pour un rendu homogène)
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, 30))
ctk.CTkLabel(header, text="Bouclier Temps Réel", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left")
self.btn_toggle_shield = ctk.CTkButton(header, text="Activer 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=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
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="Désactiver", 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="Activer 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()
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="Réseau & Pare-feu", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30))
btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 20))
ctk.CTkButton(btn_frame, text="⚙️ Audit Processus", command=lambda: threading.Thread(target=self.perform_audit, daemon=True).start(), height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="🛡️ Statut UFW", command=self.check_firewall, height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="📡 WiFi Guard", command=self.run_wifi_guard_prompt, height=45, fg_color=ACCENT_BLUE, hover_color="#2563EB").pack(side="left")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
self.audit_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.audit_console)
def perform_audit(self):
self.audit_console.delete("0.0", "end")
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", "success")
else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "warning")
except: pass
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="Scan réseau (sudo) :")
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 arp-scan... \n", "info")
if not shutil.which("arp-scan"):
self.audit_console.insert("end", f"{self.get_time_prefix()}[!] 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 : {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.\n", "info")
except Exception as e: self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger")
def init_tools_view(self):
frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame
ctk.CTkLabel(frame, text="Outils Avancés", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
# OSINT Card
breach_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
breach_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(breach_card, text="🌐 OSINT : Vérificateur de Fuites", font=ctk.CTkFont(size=20, weight="bold"), text_color=ACCENT_VIOLET).pack(anchor="w", padx=20, pady=(10,5))
ctk.CTkLabel(breach_card, text="Le Dark Web contient-il votre adresse e-mail ?", text_color=TEXT_MUTED).pack(anchor="w", padx=20, pady=(0, 15))
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="exemple@domaine.com", width=350, height=45)
self.email_entry.pack(side="left", padx=(0, 15))
ctk.CTkButton(input_frame, text="Rechercher", fg_color=ACCENT_VIOLET, hover_color="#7C3AED", height=45, 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, font=ctk.CTkFont(size=14), justify="left")
self.breach_result_label.pack(anchor="w", padx=20, pady=15)
# Nettoyeur
clean_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
clean_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(clean_card, text="🧹 Optimisation", font=ctk.CTkFont(size=20, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,15))
ctk.CTkButton(clean_card, text="Purger le Cache Système", fg_color="#059669", hover_color="#047857", height=45, command=self.run_cleaner).pack(anchor="w", padx=20)
# Destructeur
shred_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
shred_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(shred_card, text="🔥 Suppression Militaire", font=ctk.CTkFont(size=20, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,15))
ctk.CTkButton(shred_card, text="Détruire un fichier définitivement", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.run_shredder).pack(anchor="w", padx=20)
def run_breach_check(self):
email = self.email_entry.get().strip()
if not email or "@" not in email:
self.breach_result_var.set("❌ Adresse e-mail invalide.")
self.breach_result_label.configure(text_color="#EF4444")
return
self.breach_result_var.set("⏳ Interrogation des serveurs mondiaux...")
self.breach_result_label.configure(text_color=ACCENT_BLUE)
threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start()
def _exec_breach_check(self, email):
try:
req = urllib.request.Request(f"https://api.xposedornot.com/v1/check-email/{email}", headers={'User-Agent': '7lna-Antivirus'})
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]
b_str = ", ".join(breaches[:4]) + ("..." if len(breaches) > 4 else "")
self.breach_result_var.set(f"🚨 DANGER : {len(breaches)} fuites trouvées !\nSources : {b_str}")
self.breach_result_label.configure(text_color="#EF4444")
else:
self.breach_result_var.set("✅ Identité sécurisée. Aucune fuite détectée.")
self.breach_result_label.configure(text_color="#10B981")
except urllib.error.HTTPError as e:
if e.code == 404:
self.breach_result_var.set("✅ Identité sécurisée. Aucune fuite détectée.")
self.breach_result_label.configure(text_color="#10B981")
else:
self.breach_result_var.set(f"❌ Erreur API ({e.code}).")
except: self.breach_result_var.set("❌ Impossible de joindre les serveurs.")
def run_cleaner(self):
paths = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
for p in paths:
if os.path.exists(p):
for i in os.listdir(p):
item = os.path.join(p, i)
try: shutil.rmtree(item) if os.path.isdir(item) else os.remove(item)
except: pass
messagebox.showinfo("Optimisation", "Fichiers temporaires purgés.")
def run_shredder(self):
filepath = filedialog.askopenfilename()
if filepath and messagebox.askyesno("Destruction", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
try:
size = os.path.getsize(filepath)
for _ in range(3):
with open(filepath, "ba+", buffering=0) as f:
f.seek(0); f.write(os.urandom(size))
os.remove(filepath)
messagebox.showinfo("Succès", "Fichier détruit.")
except: pass
def init_schedule_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["schedule"] = frame
ctk.CTkLabel(frame, text="Automatisation Cron", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
card.pack(fill="x", ipady=20)
ctk.CTkLabel(card, text="Scan Journalier (Dossier Téléchargements)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15))
time_frame = ctk.CTkFrame(card, fg_color="transparent")
time_frame.pack(pady=10)
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=80, height=40).pack(side="left", padx=5)
ctk.CTkLabel(time_frame, text=":").pack(side="left")
ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=80, height=40).pack(side="left", padx=5)
ctk.CTkButton(card, text="Activer la planification", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.setup_cronjob).pack(pady=(20,10))
ctk.CTkButton(card, text="Désactiver", fg_color="transparent", border_width=1, border_color="#EF4444", text_color="#EF4444", hover_color="#7F1D1D", height=45, command=self.remove_cronjob).pack()
def setup_cronjob(self):
cmd = f"{int(self.cron_m_var.get())} {int(self.cron_h_var.get())} * * * /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 "{cmd}") | crontab -')
messagebox.showinfo("Cron", "Scan quotidien configuré.")
except: pass
def remove_cronjob(self):
try: os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
except: pass
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 Sécurité", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30))
self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
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 ✅ Aucun incident enregistré.\n")
else:
for e in reversed(data): self.history_console.insert("end", f"[{e['date']}] ☠️ {e['threat']}\n -> Fichier : {e['file']}\n\n")
except: pass
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, 30))
ctk.CTkLabel(header, text="Zone de Quarantaine", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="Purger Tout", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.empty_quarantine).pack(side="right")
self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151")
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 ✅ Zone propre.\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", "Détruire définitivement les virus ?"):
for f in files:
try: os.remove(f)
except: pass
self.refresh_quarantine_list()
def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Logicielle", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
card.pack(fill="x", ipady=20)
ctk.CTkLabel(card, text="Synchronisation Serveur (git.7ka1.com)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15))
self.btn_update_soft = ctk.CTkButton(card, text="Télécharger la dernière version", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.run_software_update)
self.btn_update_soft.pack(pady=10)
self.update_console = ctk.CTkTextbox(card, height=150, font=ctk.CTkFont(family="Consolas", size=13), fg_color=BG_MAIN, corner_radius=10)
self.update_console.pack(fill="x", padx=20, pady=15)
def run_software_update(self):
self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", "[*] Connexion au serveur Git...\n")
def fetch():
try:
urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__))
self.update_console.insert("end", "[+] Code source téléchargé et appliqué !\n")
messagebox.showinfo("Mise à jour", "Redémarrage de l'application requis.")
os._exit(0) # Force la fermeture propre pour relancer via le menu
except:
self.update_console.insert("end", "[-] Échec de la connexion.\n")
self.btn_update_soft.configure(state="normal")
threading.Thread(target=fetch).start()
if __name__ == "__main__":
app = Antivirus7LnA()
app.mainloop()