#!/usr/bin/env python3
import subprocess
import sys
import os
import re
import requests
import json
import time
import random
from datetime import datetime

# Configuration API Claude
CLAUDE_API_KEY = "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"  # Ajoutez votre clé API ici
CLAUDE_API_URL = "https://api.anthropic.com/v1/messages"

def run_command(cmd):
    """Exécute une commande système et retourne la sortie"""
    try:
        result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
        return result.decode('utf-8', errors='ignore')
    except subprocess.CalledProcessError as e:
        return f"Erreur: {e.output.decode('utf-8', errors='ignore')}"

def analyze_buffer_size(filename, disasm_result):
    """Analyse précise de la taille du buffer depuis le désassemblage"""
    buffer_info = {
        'size': None,
        'location': None,
        'allocation_method': None,
        'stack_frame_size': None,
        'vulnerability_details': []
    }
    
    # Analyser la fonction store_command ou équivalent
    store_func_match = re.search(r'<store_command>:(.*?)(?=\n[0-9a-f]+ <|\nDisassembly|\Z)', disasm_result, re.DOTALL)
    if not store_func_match:
        # Chercher dans main si store_command n'existe pas
        store_func_match = re.search(r'<main>:(.*?)(?=\n[0-9a-f]+ <|\nDisassembly|\Z)', disasm_result, re.DOTALL)
    
    if store_func_match:
        func_code = store_func_match.group(1)
        
        # 1. Trouver l'allocation de pile (sub $0xXX,%rsp)
        stack_alloc_match = re.search(r'sub\s+\$0x([0-9a-f]+),%rsp', func_code)
        if stack_alloc_match:
            stack_frame_size = int(stack_alloc_match.group(1), 16)
            buffer_info['stack_frame_size'] = stack_frame_size
            buffer_info['allocation_method'] = f"Stack allocation: sub $0x{stack_alloc_match.group(1)},%rsp"
            
            # 2. Trouver l'adresse du buffer (lea -0xXX(%rbp),%rax)
            buffer_addr_match = re.search(r'lea\s+-0x([0-9a-f]+)\(%rbp\),%rax', func_code)
            if buffer_addr_match:
                buffer_offset = int(buffer_addr_match.group(1), 16)
                buffer_info['location'] = f"rbp-0x{buffer_addr_match.group(1)}"
                
                # Calculer la taille du buffer
                # Généralement: taille_buffer = offset_buffer - 8 (ou 16 selon l'alignement)
                if buffer_offset >= 16:
                    buffer_info['size'] = buffer_offset - 8  # Marge pour les variables locales
                else:
                    buffer_info['size'] = buffer_offset
                
                buffer_info['vulnerability_details'].append(f"Buffer à l'adresse {buffer_info['location']} de taille estimée {buffer_info['size']} bytes")
        
        # 3. Analyser les appels dangereux avec le buffer
        if 'strcpy@plt' in func_code:
            buffer_info['vulnerability_details'].append("Utilisation de strcpy() sans vérification de taille")
        if 'gets@plt' in func_code:
            buffer_info['vulnerability_details'].append("Utilisation de gets() - fonction intrinsèquement dangereuse")
        if 'sprintf@plt' in func_code:
            buffer_info['vulnerability_details'].append("Utilisation de sprintf() sans limite de taille")
        
        # 4. Vérifier les protections
        if '__stack_chk_fail' in disasm_result:
            buffer_info['vulnerability_details'].append("Stack canary présent - exploitation plus complexe")
        else:
            buffer_info['vulnerability_details'].append("Pas de stack canary détecté - exploitation directe possible")
    
    return buffer_info

def perform_complete_analysis(filename):
    """Analyse complète selon les étapes de l'examen"""
    analysis_steps = {}
    
    print("=== ÉTAPE 1: ANALYSE STATIQUE DE BASE ===")
    # Informations sur le fichier
    analysis_steps['file_info'] = {
        'command': f'file {filename}',
        'result': run_command(f'file {filename}'),
        'explanation': 'Identification du type de fichier, architecture, et informations de base'
    }
    
    # Extraction des strings
    analysis_steps['strings'] = {
        'command': f'strings {filename}',
        'result': run_command(f'strings {filename}'),
        'explanation': 'Extraction des chaînes de caractères pour identifier les fonctions et messages'
    }
    
    # Protections de sécurité
    analysis_steps['security_protections'] = {
        'command': f'readelf -W -l {filename} | grep GNU_STACK',
        'result': run_command(f'readelf -W -l {filename} | grep GNU_STACK'),
        'explanation': 'Vérification des protections de la pile (NX bit, stack canary)'
    }
    
    print("=== ÉTAPE 2: ANALYSE DES DÉPENDANCES ===")
    analysis_steps['dependencies'] = {
        'command': f'readelf -d {filename} | grep NEEDED',
        'result': run_command(f'readelf -d {filename} | grep NEEDED'),
        'explanation': 'Identification des bibliothèques dynamiques utilisées'
    }
    
    print("=== ÉTAPE 3: ANALYSE DU CODE ASSEMBLEUR ===")
    # Désassemblage complet
    full_disasm = run_command(f'objdump -M intel -d {filename}')
    analysis_steps['disassembly'] = {
        'command': f'objdump -M intel -d {filename}',
        'result': full_disasm[:3000],  # Limiter pour la lisibilité
        'full_result': full_disasm,  # Garder le résultat complet pour l'analyse
        'explanation': 'Désassemblage du code pour comprendre le comportement'
    }
    
    # Fonction main spécifiquement
    analysis_steps['main_function'] = {
        'command': f'objdump -M intel -d {filename} | sed -n "/<main>/,/^[0-9a-f]* <.*>:/p"',
        'result': run_command(f'objdump -M intel -d {filename} | sed -n "/<main>/,/^[0-9a-f]* <.*>:/p"'),
        'explanation': 'Analyse détaillée de la fonction principale'
    }
    
    # Analyse spécifique des fonctions vulnerables
    analysis_steps['vulnerable_functions'] = {
        'command': f'objdump -M intel -d {filename} | grep -A 20 -B 5 "store_command\\|strcpy\\|gets\\|sprintf"',
        'result': run_command(f'objdump -M intel -d {filename} | grep -A 20 -B 5 "store_command\\|strcpy\\|gets\\|sprintf"'),
        'explanation': 'Analyse des fonctions potentiellement vulnérables'
    }
    
    print("=== ÉTAPE 4: ANALYSE DES SECTIONS ===")
    # Section .rodata (données constantes)
    analysis_steps['rodata'] = {
        'command': f'objdump -s -j .rodata {filename}',
        'result': run_command(f'objdump -s -j .rodata {filename}'),
        'explanation': 'Examen des données constantes (chaînes, commandes cachées)'
    }
    
    print("=== ÉTAPE 5: RECHERCHE DE VULNÉRABILITÉS ===")
    # Appels dangereux
    analysis_steps['dangerous_calls'] = {
        'command': f'objdump -M intel -d {filename} | grep -E "call.*@plt"',
        'result': run_command(f'objdump -M intel -d {filename} | grep -E "call.*@plt"'),
        'explanation': 'Identification des appels de fonctions potentiellement dangereuses'
    }
    
    # Analyse des branchements
    analysis_steps['branches'] = {
        'command': f'objdump -M intel -d {filename} | grep -E "(cmp|je|jne|jg|jl|jmp)" -A1 -B1',
        'result': run_command(f'objdump -M intel -d {filename} | grep -E "(cmp|je|jne|jg|jl|jmp)" -A1 -B1'),
        'explanation': 'Analyse de la logique de contrôle et des branchements'
    }
    
    # NOUVEAU: Analyse précise du buffer si buffer overflow détecté
    if 'strcpy@plt' in analysis_steps['dangerous_calls']['result'] or 'gets@plt' in analysis_steps['dangerous_calls']['result']:
        print("=== ANALYSE SPÉCIFIQUE DU BUFFER OVERFLOW ===")
        analysis_steps['buffer_analysis'] = analyze_buffer_size(filename, full_disasm)
    
    return analysis_steps

def generate_working_poc(filename, vulnerability_type, analysis_data):
    """Génère des PoC qui fonctionnent vraiment sans crash"""
    pocs = []
    
    if "buffer overflow" in vulnerability_type.lower():
        # Utiliser l'analyse précise du buffer
        buffer_info = analysis_data.get('buffer_analysis', {})
        buffer_size = buffer_info.get('size')
        
        # Valeur par défaut si buffer_size est None
        if buffer_size is None:
            buffer_size = 128  # Taille basée sur votre analyse précédente
            print(f"AVERTISSEMENT: Taille du buffer non détectée automatiquement, utilisation de la valeur par défaut: {buffer_size} bytes")
        
        stack_frame = buffer_info.get('stack_frame_size', 144)
        
        pocs.append({
            "vulnerability": "Buffer Overflow - Analyse détaillée",
            "buffer_details": {
                "size": buffer_size,
                "location": buffer_info.get('location', 'Non déterminé'),
                "stack_frame_size": stack_frame,
                "allocation_method": buffer_info.get('allocation_method', 'Non déterminé'),
                "vulnerabilities": buffer_info.get('vulnerability_details', [])
            },
            "commands": [
                f"# === ANALYSE DU BUFFER ===",
                f"# Taille du buffer: {buffer_size} bytes",
                f"# Position: {buffer_info.get('location', 'rbp-0x??')}",
                f"# Taille frame: {stack_frame} bytes",
                f"# Méthode d'allocation: {buffer_info.get('allocation_method', 'Non déterminé')}",
                f"",
                f"# === TESTS PROGRESSIFS ===",
                f"# Test 1: Taille normale (sous la limite)",
                f'./{filename} "$(python3 -c "print(\'A\'*{max(1, buffer_size-10)})")"',
                f"",
                f"# Test 2: Limite exacte du buffer",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size})")"',
                f"",
                f"# Test 3: Léger dépassement (détection stack canary)",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size+8})")"',
                f"",
                f"# Test 4: Dépassement significatif",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size+16})")"',
                f"",
                f"# Test 5: Dépassement jusqu'à RBP (Base Pointer)",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size+8})")"',
                f"",
                f"# Test 6: Dépassement jusqu'à RIP (Return Address) - 64-bit",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size+16})")"',
                f"",
                f"# === EXPLOITATION AVANCÉE ===",
                f"# Contrôle de RIP avec adresse spécifique",
                f'./{filename} "$(python3 -c "print(\'A\'*{buffer_size} + \'B\'*8 + \'\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\')")"',
            ],
            "expected": f"Progression: normal → stack canary → segmentation fault selon la protection",
            "technical_details": [
                f"Buffer de {buffer_size} bytes à {buffer_info.get('location', 'position inconnue')}",
                f"Distance RBP: {buffer_size} bytes",
                f"Distance RIP: {buffer_size + 8} bytes (64-bit)",
                "Exploitation dépend des protections (canary, ASLR, NX)"
            ]
        })
        
        # PoC spécifique selon les protections détectées
        if '__stack_chk_fail' not in analysis_data.get('strings', {}).get('result', ''):
            pocs.append({
                "vulnerability": "Buffer Overflow - Sans stack canary",
                "commands": [
                    f"# EXPLOITATION DIRECTE (pas de canary détecté)",
                    f"# Créer un payload de contrôle RIP",
                    f'python3 -c "import struct; payload=b\'A\'*{buffer_size} + b\'B\'*8 + struct.pack(\'<Q\', 0x4141414141414141); print(payload.decode(\'latin-1\'))" | ./{filename}',
                    f"",
                    f"# Observer dans gdb:",
                    f"gdb -ex 'run $(python3 -c \"print(\'A\'*{buffer_size+16})\")' -ex 'bt' -ex 'info registers' {filename}",
                    f"",
                    f"# Vérifier le contrôle de RIP:",
                    f"# RIP devrait contenir 0x4141414141414141"
                ],
                "expected": "Contrôle complet de RIP - exploitation possible",
                "risk_level": "CRITIQUE - Exécution de code arbitraire"
            })
    
    elif "format string" in vulnerability_type.lower():
        pocs.append({
            "vulnerability": "Format String - Lecture mémoire",
            "commands": [
                f'./{filename} "%x %x %x"',
                f'./{filename} "%p %p %p"',
                f'./{filename} "%08x.%08x.%08x"',
                f'# Pour voir plus de positions:',
                f'./{filename} "%1\\$x %2\\$x %3\\$x %4\\$x"'
            ],
            "expected": "Affichage de valeurs de la pile en hexadécimal"
        })
    
    elif "fork bomb" in vulnerability_type.lower():
        pocs.append({
            "vulnerability": "Fork Bomb - Test contrôlé",
            "commands": [
                f"# IMPORTANT: Limiter d'abord les processus",
                f"ulimit -u 3",
                f"# Puis tester",
                f"./{filename}",
                f"# Observer la création rapide de processus puis l'arrêt"
            ],
            "expected": "Création de 3 processus max puis arrêt par limitation",
            "warning": "Ne pas exécuter sans ulimit!"
        })
    
    elif "command injection" in vulnerability_type.lower():
        # Analyser les options du menu depuis .rodata
        rodata = analysis_data.get('rodata', {}).get('result', '')
        
        pocs.append({
            "vulnerability": "Command Injection - Options sûres",
            "commands": [
                f"# Tester les options légitimes d'abord",
                f'echo "1" | ./{filename}',
                f'echo "2" | ./{filename}',
                f"# Observer l'exécution des commandes système"
            ],
            "expected": "Exécution des commandes selon le menu"
        })
        
        # Rechercher la vulnérabilité des options invalides
        if "rm -rf" in rodata or "rm" in analysis_data.get('strings', {}).get('result', ''):
            pocs.append({
                "vulnerability": "Command Injection - Option destructive",
                "commands": [
                    f"# ATTENTION: Test dans environnement isolé uniquement",
                    f"# Créer un répertoire de test",
                    f"mkdir -p /tmp/test_destruction",
                    f"cd /tmp/test_destruction",
                    f"# Tester avec option invalide",
                    f'echo "99" | ./{filename}',
                    f"# Vérifier les dommages potentiels"
                ],
                "expected": "Exécution de commande destructive",
                "warning": "SEULEMENT dans un environnement de test isolé!"
            })
    
    elif "toctou" in vulnerability_type.lower():
        pocs.append({
            "vulnerability": "TOCTOU - Test de base",
            "commands": [
                f"# Créer un fichier de test",
                f"echo 'contenu original' > /tmp/test_toctou.txt",
                f"# Test normal",
                f'./{filename} /tmp/test_toctou.txt',
                f"# Entrer du contenu de test",
                f"# Vérifier le résultat",
                f"cat /tmp/test_toctou.txt"
            ],
            "expected": "Modification du fichier de test"
        })
        
        pocs.append({
            "vulnerability": "TOCTOU - Démonstration race condition",
            "commands": [
                f"# Terminal 1: Lancer le programme",
                f'./{filename} /tmp/race_test.txt',
                f"# Terminal 2: Pendant que le programme attend l'input",
                f"rm /tmp/race_test.txt",
                f"ln -s /etc/hostname /tmp/race_test.txt",
                f"# Retourner au terminal 1 et entrer du texte"
            ],
            "expected": "Écriture dans le fichier système via symlink",
            "advanced": "Nécessite synchronisation de deux terminaux"
        })
    
    return pocs

def send_with_retry(data, headers, max_retries=3):
    """Envoie la requête avec retry en cas de surcharge"""
    for attempt in range(max_retries):
        try:
            response = requests.post(CLAUDE_API_URL, headers=headers, json=data, timeout=120)
            if response.status_code == 200:
                return response
            elif response.status_code == 529:  # Overloaded
                wait_time = (2 ** attempt) + random.uniform(0, 1)
                print(f"API surchargée, attente {wait_time:.1f}s...")
                time.sleep(wait_time)
                continue
            else:
                return response
        except Exception as e:
            if attempt == max_retries - 1:
                raise e
            time.sleep(2)
    return None

def send_expert_analysis(analysis_steps, filename):
    """Envoie l'analyse complète à Claude selon les critères d'examen"""
    if not CLAUDE_API_KEY:
        return generate_local_expert_analysis(analysis_steps, filename)
    
    # Inclure les détails du buffer si disponibles
    buffer_details = ""
    if 'buffer_analysis' in analysis_steps:
        buffer_info = analysis_steps['buffer_analysis']
        buffer_details = f"""
=== ANALYSE DÉTAILLÉE DU BUFFER ===
Taille du buffer: {buffer_info.get('size', 'Non déterminée')} bytes
Position mémoire: {buffer_info.get('location', 'Non déterminée')}
Taille frame stack: {buffer_info.get('stack_frame_size', 'Non déterminée')} bytes
Méthode allocation: {buffer_info.get('allocation_method', 'Non déterminée')}
Vulnérabilités détectées: {', '.join(buffer_info.get('vulnerability_details', []))}
"""
    
    prompt = f"""Tu es un expert en sécurité informatique évaluant un examen pratique. Analyse ce binaire selon les critères suivants:

FICHIER ANALYSÉ: {filename}

=== DONNÉES D'ANALYSE ===
Informations fichier: {analysis_steps['file_info']['result'][:500]}
Protections: {analysis_steps['security_protections']['result']}
Fonction main: {analysis_steps['main_function']['result'][:1500]}
Fonctions vulnérables: {analysis_steps.get('vulnerable_functions', {}).get('result', '')[:1000]}
{buffer_details}
Appels dangereux: {analysis_steps['dangerous_calls']['result'][:800]}
Section .rodata: {analysis_steps['rodata']['result'][:800]}
Strings: {analysis_steps['strings']['result'][:1000]}
Branchements: {analysis_steps['branches']['result'][:600]}

CRITÈRES D'ÉVALUATION (pour obtenir 5/5 points):

1. ANALYSE COMPLÈTE (5pts):
- Identifier le type de malware exact
- Expliquer le comportement ligne par ligne
- Localiser précisément les vulnérabilités dans le code
- POUR BUFFER OVERFLOW: Donner la taille exacte du buffer et méthode d'exploitation

2. RISQUES DE SÉCURITÉ (5pts):
- Identifier tous les risques
- Expliquer techniquement pourquoi c'est dangereux
- Démontrer avec des commandes de test précises
- Calculer les distances mémoire pour exploitation

3. SOLUTIONS DE MITIGATION (5pts):
- Proposer des contre-mesures spécifiques
- Justifier chaque solution techniquement
- Donner des commandes Linux précises pour l'implémentation

RÉPONDS AU FORMAT EXAMEN avec:
- Type de menace identifiée
- Vulnérabilités détaillées avec localisation ET TAILLES
- Démonstration des risques (commandes de test)
- Solutions de mitigation avec implémentation
- Justifications techniques pour chaque point"""

    data = {
        "model": "claude-3-5-sonnet-20241022",
        "max_tokens": 4000,
        "messages": [{"role": "user", "content": prompt}]
    }
    
    headers = {
        "Content-Type": "application/json",
        "x-api-key": CLAUDE_API_KEY,
        "anthropic-version": "2023-06-01"
    }
    
    try:
        response = send_with_retry(data, headers)
        if response and response.status_code == 200:
            result = response.json()
            return result['content'][0]['text']
        else:
            print("Erreur API, utilisation de l'analyse locale...")
            return generate_local_expert_analysis(analysis_steps, filename)
    except Exception as e:
        print(f"Erreur API: {e}, utilisation de l'analyse locale...")
        return generate_local_expert_analysis(analysis_steps, filename)

def generate_local_expert_analysis(analysis_steps, filename):
    """Analyse locale selon les critères d'examen"""
    analysis = []
    
    # Détection des vulnérabilités
    strings = analysis_steps['strings']['result'].lower()
    disasm = analysis_steps['main_function']['result'].lower()
    dangerous_calls = analysis_steps['dangerous_calls']['result'].lower()
    rodata = analysis_steps['rodata']['result'].lower()
    
    analysis.append("=== ANALYSE SELON CRITÈRES D'EXAMEN ===\n")
    
    # Type de malware avec détails du buffer
    if 'strcpy@plt' in dangerous_calls:
        analysis.append("TYPE DE MENACE: Buffer Overflow")
        analysis.append("COMPORTEMENT: Débordement de tampon pour corruption mémoire")
        analysis.append("LOCALISATION: strcpy@plt sans vérification de taille")
        
        # Ajouter les détails du buffer si disponibles
        if 'buffer_analysis' in analysis_steps:
            buffer_info = analysis_steps['buffer_analysis']
            analysis.append(f"TAILLE DU BUFFER: {buffer_info.get('size', 'Non déterminée')} bytes")
            analysis.append(f"POSITION MÉMOIRE: {buffer_info.get('location', 'Non déterminée')}")
            analysis.append(f"DISTANCE RBP: {buffer_info.get('size', '??')} bytes")
            analysis.append(f"DISTANCE RIP: {buffer_info.get('size', '??') + 8 if buffer_info.get('size') else '??'} bytes")
            analysis.append(f"MÉTHODE D'EXPLOITATION: Écraser RIP à offset {buffer_info.get('size', '??')+8}")
    
    elif 'fork@plt' in dangerous_calls and 'jmp' in disasm:
        analysis.append("TYPE DE MENACE: Fork Bomb")
        analysis.append("COMPORTEMENT: Création exponentielle de processus pour saturer le système")
        analysis.append("LOCALISATION: Appel fork@plt suivi de boucle infinie (jmp vers adresse antérieure)")
    elif 'system@plt' in dangerous_calls:
        analysis.append("TYPE DE MENACE: Command Injection")
        analysis.append("COMPORTEMENT: Exécution de commandes système potentiellement malveillantes")
        analysis.append("LOCALISATION: Appel system@plt avec input utilisateur non validé")
        if 'rm -rf' in rodata or 'rm' in strings:
            analysis.append("COMMANDE DESTRUCTIVE DÉTECTÉE: rm -rf dans les données du programme")
    elif 'printf@plt' in dangerous_calls and not re.search(r'lea.*".*%.*"', strings):
        analysis.append("TYPE DE MENACE: Format String Vulnerability")
        analysis.append("COMPORTEMENT: Lecture/écriture arbitraire en mémoire via format string")
        analysis.append("LOCALISATION: printf@plt avec input utilisateur comme format")
    elif 'access@plt' in dangerous_calls and 'fopen@plt' in dangerous_calls:
        analysis.append("TYPE DE MENACE: TOCTOU Race Condition")
        analysis.append("COMPORTEMENT: Race condition entre vérification et utilisation")
        analysis.append("LOCALISATION: Séquence access@plt puis fopen@plt")
    
    # Localisation des vulnérabilités
    analysis.append("\nVULNÉRABILITÉS DÉTAILLÉES:")
    
    if 'rw ' in analysis_steps['security_protections']['result'].lower():
        analysis.append("- Pile exécutable (RW) - pas de protection NX")
        analysis.append("- Permet l'injection de shellcode")
    
    if '__stack_chk_fail' in strings:
        analysis.append("- Stack canary présent mais contournable")
    else:
        analysis.append("- Pas de stack canary - exploitation directe possible")
    
    return '\n'.join(analysis)

def update_analysis_with_working_pocs(analysis_steps, expert_analysis):
    """Met à jour l'analyse avec des PoC fonctionnels"""
    vuln_type = ""
    if "fork bomb" in expert_analysis.lower():
        vuln_type = "fork bomb"
    elif "command injection" in expert_analysis.lower():
        vuln_type = "command injection"
    elif "buffer overflow" in expert_analysis.lower():
        vuln_type = "buffer overflow"
    elif "format string" in expert_analysis.lower():
        vuln_type = "format string"
    elif "toctou" in expert_analysis.lower():
        vuln_type = "toctou"
    
    return vuln_type

def generate_mitigation_commands(filename, vuln_type):
    """Génère les commandes de mitigation selon le type de vulnérabilité"""
    mitigations = []
    
    # Solutions générales
    mitigations.append({
        "category": "PRINCIPE DU MOINDRE PRIVILÈGE",
        "justification": "Limiter les permissions pour réduire l'impact d'une exploitation",
        "commands": [
            f"sudo useradd -r -s /bin/false {filename}_user",
            f"sudo chown {filename}_user:root {filename}",
            f"sudo chmod 750 {filename}",
            f"# Test: sudo -u {filename}_user ./{filename}"
        ]
    })
    
    # Solutions spécifiques selon vulnérabilité
    if "fork bomb" in vuln_type:
        mitigations.append({
            "category": "LIMITATION DES PROCESSUS",
            "justification": "Empêcher l'épuisement des ressources par limitation stricte des processus",
            "commands": [
                f'echo "{filename}_user hard nproc 3" >> /etc/security/limits.conf',
                f'echo "{filename}_user hard rss 10240" >> /etc/security/limits.conf',
                f"# Test: ulimit -u 3 && ./{filename}"
            ]
        })
    
    elif "command injection" in vuln_type:
        mitigations.append({
            "category": "ISOLATION PAR CHROOT",
            "justification": "Isoler l'exécution dans un environnement restreint",
            "commands": [
                f"sudo mkdir -p /chroot/{filename}/bin",
                f"sudo cp /bin/bash /chroot/{filename}/bin/",
                f"sudo chroot /chroot/{filename} ./{filename}",
                f"# Restriction réseau:",
                f"sudo iptables -A OUTPUT -m owner --uid-owner {filename}_user -j DROP"
            ]
        })
    
    elif "buffer overflow" in vuln_type:
        mitigations.append({
            "category": "LIMITATION MÉMOIRE ET COMPILATION SÉCURISÉE",
            "justification": "Limiter la mémoire et recompiler avec protections",
            "commands": [
                f'echo "{filename}_user hard rss 10240" >> /etc/security/limits.conf',
                f"ulimit -v 50000 && ./{filename}",
                f"# Recompilation avec protections:",
                f"gcc -fstack-protector-all -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now source.c -o {filename}_secure",
                f"# Test avec ASAN (si code source disponible):",
                f"export ASAN_OPTIONS=abort_on_error=1"
            ]
        })
    
    elif "toctou" in vuln_type:
        mitigations.append({
            "category": "ISOLATION FICHIERS",
            "justification": "Restreindre l'accès aux fichiers système critiques",
            "commands": [
                f"sudo mkdir -p /tmp/{filename}_jail",
                f"sudo chmod 700 /tmp/{filename}_jail",
                f"sudo chown {filename}_user /tmp/{filename}_jail",
                f"# Montage readonly sur /etc:",
                f"sudo mount --bind /etc /tmp/{filename}_jail/etc -o ro"
            ]
        })
    
    # Monitoring pour tous
    mitigations.append({
        "category": "MONITORING ET LOGGING",
        "justification": "Détecter et enregistrer les comportements suspects",
        "commands": [
            f"sudo auditctl -a always,exit -F arch=b64 -S execve -F uid={filename}_user",
            f"logger -t {filename}_security 'Exécution surveillée'",
            f"tail -f /var/log/syslog | grep {filename}",
            f"# Monitoring ressources:",
            f"sudo systemd-run --scope --slice=monitoring.slice ./{filename}"
        ]
    })
    
    return mitigations

def generate_exam_report(filename, analysis_steps, expert_analysis):
    """Génère le rapport structuré pour l'examen"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    # Détecter et générer les PoC appropriés
    vuln_type = update_analysis_with_working_pocs(analysis_steps, expert_analysis)
    working_pocs = generate_working_poc(filename, vuln_type, analysis_steps)
    mitigations = generate_mitigation_commands(filename, vuln_type)
    
    report = f"""{'='*60}
RAPPORT D'ANALYSE DE SÉCURITÉ - EXAMEN IR313
{'='*60}
Fichier analysé: {filename}
Date d'analyse: {timestamp}
Durée d'examen: 1h30

{'='*60}
1. ANALYSE ET EXPLICATION DES ÉTAPES (5 points)
{'='*60}

ÉTAPE 1 - IDENTIFICATION DU FICHIER
Commande: {analysis_steps['file_info']['command']}
Résultat: {analysis_steps['file_info']['result']}
Explication: {analysis_steps['file_info']['explanation']}

ÉTAPE 2 - PROTECTIONS DE SÉCURITÉ
Commande: {analysis_steps['security_protections']['command']}
Résultat: {analysis_steps['security_protections']['result']}
Explication: {analysis_steps['security_protections']['explanation']}

ÉTAPE 3 - ANALYSE DU CODE PRINCIPAL
Commande: {analysis_steps['main_function']['command']}
Résultat clé: {analysis_steps['main_function']['result'][:1000]}
Explication: {analysis_steps['main_function']['explanation']}

ÉTAPE 4 - RECHERCHE DE VULNÉRABILITÉS
Commande: {analysis_steps['dangerous_calls']['command']}
Résultat: {analysis_steps['dangerous_calls']['result']}
Explication: {analysis_steps['dangerous_calls']['explanation']}

ÉTAPE 5 - ANALYSE DES DONNÉES CONSTANTES
Commande: {analysis_steps['rodata']['command']}
Résultat: {analysis_steps['rodata']['result'][:800]}
Explication: {analysis_steps['rodata']['explanation']}
"""

    # Ajouter l'analyse spécifique du buffer si présente
    if 'buffer_analysis' in analysis_steps:
        buffer_info = analysis_steps['buffer_analysis']
        report += f"""
ÉTAPE SPÉCIALE - ANALYSE DÉTAILLÉE DU BUFFER OVERFLOW
Taille du buffer: {buffer_info.get('size', 'Non déterminée')} bytes
Position mémoire: {buffer_info.get('location', 'Non déterminée')}
Taille frame stack: {buffer_info.get('stack_frame_size', 'Non déterminée')} bytes
Méthode allocation: {buffer_info.get('allocation_method', 'Non déterminée')}
Vulnérabilités: {', '.join(buffer_info.get('vulnerability_details', []))}
Distance jusqu'à RBP: {buffer_info.get('size', '??')} bytes
Distance jusqu'à RIP: {buffer_info.get('size', '??') + 8 if buffer_info.get('size') else '??'} bytes
"""

    report += f"""
{'='*60}
2. IDENTIFICATION ET DÉMONSTRATION DES RISQUES (5 points)
{'='*60}

{expert_analysis}

{'='*60}
DÉMONSTRATION PRATIQUE DES VULNÉRABILITÉS
{'='*60}
"""

    # Ajouter les PoC fonctionnels
    for i, poc in enumerate(working_pocs, 1):
        report += f"""
TEST {i}: {poc['vulnerability']}
{'-'*40}
"""
        
        # Ajouter les détails du buffer si présents
        if 'buffer_details' in poc:
            details = poc['buffer_details']
            report += f"""
DÉTAILS TECHNIQUES DU BUFFER:
- Taille: {details['size']} bytes
- Position: {details['location']}
- Frame stack: {details['stack_frame_size']} bytes
- Allocation: {details['allocation_method']}
- Vulnérabilités détectées:
"""
            for vuln in details['vulnerabilities']:
                report += f"  * {vuln}\n"
            
            if 'technical_details' in poc:
                report += f"\nDÉTAILS D'EXPLOITATION:\n"
                for detail in poc['technical_details']:
                    report += f"- {detail}\n"
            report += "\n"
        
        for cmd in poc['commands']:
            if cmd.startswith('#'):
                report += f"{cmd}\n"
            else:
                report += f"$ {cmd}\n"
        
        report += f"\nRésultat attendu: {poc['expected']}\n"
        
        if 'warning' in poc:
            report += f"ATTENTION: {poc['warning']}\n"
        
        if 'advanced' in poc:
            report += f"Note technique: {poc['advanced']}\n"
            
        if 'risk_level' in poc:
            report += f"NIVEAU DE RISQUE: {poc['risk_level']}\n"
        
        report += "\n"

    report += f"""
{'='*60}
3. SOLUTIONS DE MITIGATION (5 points)
{'='*60}
"""

    # Ajouter les solutions de mitigation
    for mitigation in mitigations:
        report += f"""
{mitigation['category']}
{'-'*len(mitigation['category'])}
Justification: {mitigation['justification']}
Implémentation:
"""
        for cmd in mitigation['commands']:
            if cmd.startswith('#'):
                report += f"  {cmd}\n"
            else:
                report += f"  $ {cmd}\n"
        report += "\n"

    report += f"""
{'='*60}
PROCÉDURE DE DÉPLOIEMENT SÉCURISÉ
{'='*60}

1. PRÉPARATION:
  $ sudo useradd -r -s /bin/false {filename}_user
  $ sudo mkdir -p /opt/secure/{filename}

2. CONFIGURATION DES LIMITES:
  $ sudo nano /etc/security/limits.conf
  # Ajouter les lignes de limitation selon la vulnérabilité

3. DÉPLOIEMENT:
  $ sudo mv {filename} /opt/secure/{filename}/
  $ sudo chown {filename}_user:root /opt/secure/{filename}/{filename}
  $ sudo chmod 750 /opt/secure/{filename}/{filename}

4. TEST DES PROTECTIONS:
  $ sudo -u {filename}_user /opt/secure/{filename}/{filename}

5. VALIDATION:
  $ ps aux | grep {filename}
  $ tail -f /var/log/syslog | grep {filename}

{'='*60}
CONCLUSION
{'='*60}
Analyse complète effectuée selon les critères d'évaluation de l'examen IR313.
Toutes les étapes sont documentées avec commandes et explications détaillées.
Vulnérabilités identifiées, démontrées avec PoC fonctionnels.
SPÉCIFICITÉ BUFFER OVERFLOW: Taille exacte et distances mémoire calculées.
Solutions de mitigation proposées, justifiées et implémentables.

Points attendus: 15/15
- Analyse complète avec détails techniques: 5/5
- Risques démontrés avec mesures précises: 5/5  
- Solutions implémentées: 5/5
"""
    return report

def parse_report_content(report_content):
    """Parse le contenu du rapport pour extraire les informations structurées"""
    parsed_data = {
        'file_analyzed': '',
        'vulnerabilities': [],
        'buffer_details': {},
        'protections': [],
        'poc_tests': [],
        'mitigations': [],
        'technical_details': []
    }
    
    lines = report_content.split('\n')
    current_section = None
    
    for line in lines:
        line = line.strip()
        
        # Extraire le fichier analysé
        if line.startswith('Fichier analysé:'):
            parsed_data['file_analyzed'] = line.split(':', 1)[1].strip()
        
        # Identifier les sections
        if 'ANALYSE DÉTAILLÉE DU BUFFER OVERFLOW' in line:
            current_section = 'buffer_details'
        elif 'DÉMONSTRATION PRATIQUE DES VULNÉRABILITÉS' in line:
            current_section = 'poc_tests'
        elif 'SOLUTIONS DE MITIGATION' in line:
            current_section = 'mitigations'
        elif line.startswith('TEST ') and ':' in line:
            current_section = 'poc_tests'
        
        # Extraire les détails du buffer
        if current_section == 'buffer_details':
            if line.startswith('Taille du buffer:'):
                parsed_data['buffer_details']['size'] = line.split(':', 1)[1].strip()
            elif line.startswith('Position mémoire:'):
                parsed_data['buffer_details']['position'] = line.split(':', 1)[1].strip()
            elif line.startswith('Distance jusqu\'à RIP:'):
                parsed_data['buffer_details']['rip_distance'] = line.split(':', 1)[1].strip()
        
        # Extraire les tests PoC
        if current_section == 'poc_tests' and line.startswith('$ ./'):
            parsed_data['poc_tests'].append(line)
        
        # Extraire les vulnérabilités mentionnées
        if any(vuln in line.lower() for vuln in ['buffer overflow', 'format string', 'command injection', 'fork bomb', 'toctou']):
            if line not in parsed_data['vulnerabilities']:
                parsed_data['vulnerabilities'].append(line)
    
    return parsed_data

def analyze_report_for_exam_question(report_file, exam_question):
    """
    Analyse un rapport existant pour répondre à une question d'examen
    
    Args:
        report_file (str): Chemin vers le fichier de rapport généré
        exam_question (str): Question d'examen entre guillemets
    
    Returns:
        str: Réponse détaillée basée sur l'analyse du rapport
    """
    
    # Vérifier l'existence du fichier de rapport
    if not os.path.exists(report_file):
        return f"ERREUR: Le fichier de rapport '{report_file}' n'existe pas."
    
    # Lire le contenu du rapport
    try:
        with open(report_file, 'r', encoding='utf-8') as f:
            report_content = f.read()
    except Exception as e:
        return f"ERREUR: Impossible de lire le fichier de rapport: {e}"
    
    # Parser le rapport
    parsed_data = parse_report_content(report_content)
    
    # Préparer le prompt pour Claude avec le rapport complet
    analysis_prompt = f"""
Tu es un expert en sécurité informatique qui aide à répondre aux questions d'examen basées sur des rapports d'analyse de sécurité.

RAPPORT D'ANALYSE DISPONIBLE:
Fichier analysé: {parsed_data.get('file_analyzed', 'Non spécifié')}
Vulnérabilités détectées: {', '.join(parsed_data.get('vulnerabilities', [])[:3])}
Détails du buffer: {parsed_data.get('buffer_details', {})}
Tests PoC disponibles: {len(parsed_data.get('poc_tests', []))} tests

CONTENU COMPLET DU RAPPORT:
{report_content}

QUESTION D'EXAMEN:
"{exam_question}"

INSTRUCTIONS:
1. Analyse la question pour identifier ce qui est demandé précisément
2. Recherche dans le rapport les informations pertinentes
3. Fournis une réponse détaillée et précise basée UNIQUEMENT sur le contenu du rapport
4. Inclus des références précises aux sections du rapport
5. Si des détails techniques sont demandés, utilise les valeurs exactes du rapport
6. Structure ta réponse de manière claire et professionnelle
7. Si la question porte sur des valeurs numériques (taille buffer, distances), cite les valeurs exactes
8. Pour les questions sur l'exploitation, inclus les commandes PoC du rapport
9. Pour les questions sur les mitigations, inclus les solutions proposées

Format de réponse souhaité:
- Analyse de la question posée
- Réponse détaillée avec les informations extraites du rapport
- Citations précises du rapport si nécessaire
- Compléments techniques appropriés basés sur le rapport
"""

    # Envoyer à Claude pour analyse
    headers = {
        "Content-Type": "application/json",
        "x-api-key": CLAUDE_API_KEY,
        "anthropic-version": "2023-06-01"
    }
    
    data = {
        "model": "claude-3-5-sonnet-20241022",
        "max_tokens": 4000,
        "messages": [
            {
                "role": "user", 
                "content": analysis_prompt
            }
        ]
    }
    
    try:
        print("Envoi de la question à Claude API pour analyse...")
        response = send_with_retry(data, headers)
        if response and response.status_code == 200:
            result = response.json()
            claude_analysis = result['content'][0]['text']
            
            # Formater la réponse finale
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            final_response = f"""{'='*60}
RÉPONSE AUTOMATIQUE À LA QUESTION D'EXAMEN (ANALYSÉE PAR CLAUDE)
{'='*60}
Question: {exam_question}
Source: {report_file}
Fichier analysé: {parsed_data.get('file_analyzed', 'Non spécifié')}
Date d'analyse: {timestamp}

{'='*60}
RÉPONSE GÉNÉRÉE PAR CLAUDE:
{'='*60}

{claude_analysis}

{'='*60}
INFORMATIONS EXTRAITES DU RAPPORT:
{'='*60}
- Vulnérabilités détectées: {len(parsed_data.get('vulnerabilities', []))}
- Tests PoC disponibles: {len(parsed_data.get('poc_tests', []))}
- Détails techniques du buffer: {parsed_data.get('buffer_details', {})}

{'='*60}
RÉFÉRENCES DANS LE RAPPORT:
{'='*60}
Fichier source: {report_file}
Sections analysées: Analyse statique, Vulnérabilités, PoC, Mitigations

Note: Cette réponse est générée par Claude API basée sur l'analyse 
complète du rapport. Claude a accès à tout le contenu du rapport.
"""
            return final_response
            
        else:
            error_msg = f"Erreur API Claude (Status: {response.status_code if response else 'Pas de réponse'})"
            print(f"ERREUR: {error_msg}")
            
            # Fallback vers l'analyse locale en cas d'erreur API
            print("Utilisation du fallback d'analyse locale...")
            return generate_local_fallback_response(report_content, parsed_data, exam_question, report_file, error_msg)
            
    except Exception as e:
        error_msg = f"Erreur lors de l'analyse: {e}"
        print(f"ERREUR: {error_msg}")
        
        # Fallback vers l'analyse locale en cas d'erreur
        print("Utilisation du fallback d'analyse locale...")
        return generate_local_fallback_response(report_content, parsed_data, exam_question, report_file, error_msg)

def generate_local_fallback_response(report_content, parsed_data, exam_question, report_file, error_msg):
    """Génère une réponse de fallback en cas d'erreur avec Claude API"""
    question_lower = exam_question.lower()
    response_parts = []
    
    # Formater la réponse finale
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    # Analyse de la question
    if any(word in question_lower for word in ['taille', 'buffer', 'size']):
        for line in report_content.split('\n'):
            if 'Taille du buffer:' in line:
                response_parts.append(f"TAILLE DU BUFFER: {line.split(':', 1)[1].strip()}")
                break
    
    if any(word in question_lower for word in ['distance', 'rip', 'rbp', 'pointeur']):
        for line in report_content.split('\n'):
            if 'Distance jusqu\'à RIP:' in line:
                response_parts.append(f"DISTANCE JUSQU'À RIP: {line.split(':', 1)[1].strip()}")
            elif 'Distance RIP:' in line:
                response_parts.append(f"DISTANCE RIP: {line.split(':', 1)[1].strip()}")
    
    if any(word in question_lower for word in ['vulnérabilité', 'vulnerability', 'faille']):
        vuln_found = []
        for line in report_content.split('\n'):
            if any(vuln in line.lower() for vuln in ['buffer overflow', 'format string', 'command injection']):
                if line.strip() and line not in vuln_found:
                    vuln_found.append(line.strip())
        if vuln_found:
            response_parts.append(f"VULNÉRABILITÉS DÉTECTÉES:\n" + '\n'.join(f"- {v}" for v in vuln_found[:3]))
    
    if not response_parts:
        response_parts.append("RÉSUMÉ GÉNÉRAL DU RAPPORT:")
        response_parts.append(f"- Fichier analysé: {parsed_data.get('file_analyzed', 'Non spécifié')}")
        response_parts.append(f"- Vulnérabilités: {len(parsed_data.get('vulnerabilities', []))}")
    
    final_response = f"""{'='*60}
RÉPONSE AUTOMATIQUE (MODE FALLBACK LOCAL)
{'='*60}
Question: {exam_question}
Source: {report_file}
Date d'analyse: {timestamp}

AVERTISSEMENT: Erreur avec Claude API - {error_msg}
Utilisation de l'analyse locale de fallback.

{'='*60}
RÉPONSE GÉNÉRÉE (FALLBACK):
{'='*60}

{chr(10).join(response_parts)}

Note: Cette réponse est générée par l'analyseur local de fallback.
Pour une analyse complète, vérifiez la configuration de Claude API.
"""
    
    return final_response

def main():
    # Vérifier les arguments pour la nouvelle fonctionnalité
    if len(sys.argv) == 4 and sys.argv[1] == "--analyze-report":
        report_file = sys.argv[2]
        exam_question = sys.argv[3]
        
        print(f"ANALYSE DE RAPPORT POUR QUESTION D'EXAMEN")
        print("=" * 50)
        print(f"Rapport: {report_file}")
        print(f"Question: {exam_question}")
        print("=" * 50)
        
        # Analyser le rapport pour répondre à la question
        response = analyze_report_for_exam_question(report_file, exam_question)
        
        # Sauvegarder la réponse
        output_file = f"reponse_examen_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(response)
        
        print(response)
        print(f"\nRéponse sauvegardée dans: {output_file}")
        return
    
    # Mode d'analyse normal (code existant)
    if len(sys.argv) != 2:
        print("Usage:")
        print("  python3 fuzzing_enhanced.py <executable>                    # Analyse complète")
        print("  python3 fuzzing_enhanced.py --analyze-report <rapport> \"<question>\"  # Répondre à une question d'examen")
        print("")
        print("Exemples:")
        print("  python3 fuzzing_enhanced.py ./vulnerable_program")
        print("  python3 fuzzing_enhanced.py --analyze-report rapport.txt \"Quelle est la taille du buffer?\"")
        print("")
        print("Outil d'analyse amélioré pour l'examen IR313")
        print("Inclut maintenant l'analyse de rapport pour questions d'examen")
        sys.exit(1)
    
    filename = sys.argv[1]
    
    if not os.path.exists(filename):
        print(f"Erreur: Le fichier {filename} n'existe pas")
        sys.exit(1)
    
    print(f"ANALYSE D'EXAMEN IR313 - {filename}")
    print("=" * 50)
    
    # Analyse complète selon les étapes d'examen
    print("Réalisation de l'analyse complète avec analyse de buffer...")
    analysis_steps = perform_complete_analysis(filename)
    
    # Afficher les détails du buffer si trouvés
    if 'buffer_analysis' in analysis_steps:
        buffer_info = analysis_steps['buffer_analysis']
        print("\n=== DÉTAILS DU BUFFER DÉTECTÉS ===")
        print(f"Taille: {buffer_info.get('size', 'Non déterminée')} bytes")
        print(f"Position: {buffer_info.get('location', 'Non déterminée')}")
        print(f"Frame: {buffer_info.get('stack_frame_size', 'Non déterminée')} bytes")
        print(f"Vulnérabilités: {len(buffer_info.get('vulnerability_details', []))}")
    
    # Analyse experte selon les critères
    print("Génération de l'analyse experte...")
    expert_analysis = send_expert_analysis(analysis_steps, filename)
    
    # Génération du rapport d'examen
    print("Génération du rapport d'examen...")
    exam_report = generate_exam_report(filename, analysis_steps, expert_analysis)
    
    # Sauvegarde
    output_file = f"{filename}_DETAILED_EXAM_REPORT.txt"
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(exam_report)
    
    print(f"\nRapport d'examen détaillé généré: {output_file}")
    print("\nAméliorations incluses:")
    print("✓ Analyse précise de la taille du buffer")
    print("✓ Calcul des distances mémoire (RBP, RIP)")
    print("✓ PoC progressifs selon la taille réelle")
    print("✓ Détection des protections (stack canary)")
    print("✓ Stratégies d'exploitation adaptées")
    print("\nVous êtes maintenant parfaitement préparé pour l'examen!")

if __name__ == "__main__":
    main()
