# Ataques Adversariais (Adversarial Attacks)

### 📑 **Índice**

1. Fundamentos dos Ataques Adversariais
2. Arquitetura e Mecanismos
3. Taxonomia de Ataques
4. Principais Métodos de Ataque
5. Implementação em Python
6. Ataques em Diferentes Domínios
7. Defesas e Mitigações
8. Ferramentas e Frameworks

***

### 🔍 **Fundamentos dos Ataques Adversariais**

#### **O que são Ataques Adversariais?**

**Ataques Adversariais (Adversarial Attacks)** são técnicas que manipulam entradas de modelos de inteligência artificial (especialmente redes neurais profundas) para **forçá-los a cometer erros** de classificação — geralmente de forma imperceptível para humanos. Pequenas perturbações cuidadosamente calculadas em uma imagem, áudio, texto ou qualquer outro dado podem fazer um modelo:

* Classificar um **panda como um gibão** (ataque não-alvo)
* Classificar um sinal de **"Pare" como "Limite 80 km/h"** (ataque alvo)
* Ativar **comandos de voz não autorizados** em assistentes
* **Bypassar sistemas de reconhecimento facial**

#### **Contexto Histórico**

```yaml
Evolução dos Ataques Adversariais:
  2013: Szegedy et al. descobrem "propriedades intrigantes" de redes neurais
  2014: Goodfellow et al. criam FGSM (Fast Gradient Sign Method)
  2016: Papernot et al. introduzem ataques de caixa preta
  2017: Madry et al. propõem PGD (Projected Gradient Descent)
  2017: Carlini & Wagner desenvolvem C&W attack
  2018: Athalye et al. quebram defesas de obfuscated gradients
  2019: Primeiros ataques físicos bem-sucedidos (sinais de trânsito)
  2021: Ataques adversariais em modelos de linguagem (GPT, BERT)
  2024: Ataques em modelos multimodais (CLIP, DALL-E, LLaVA)

Motivação:
  ✅ Redes neurais são surpreendentemente frágeis
  ✅ Pequenas perturbações podem causar falhas catastróficas
  ✅ Ataques podem ser transferidos entre modelos
  ✅ Sistemas de IA em produção são vulneráveis
```

#### **Conceitos Fundamentais**

| Termo                      | Definição                                                               |
| -------------------------- | ----------------------------------------------------------------------- |
| **Amostra Adversarial**    | Entrada modificada intencionalmente para causar erro no modelo          |
| **Perturbação (ε)**        | Pequena alteração aplicada à entrada original (limitada por norma Lp)   |
| **Norma Lp**               | Medida da magnitude da perturbação (L∞, L2, L0)                         |
| **Ataque de Caixa Branca** | Atacante tem acesso completo ao modelo (arquitetura, pesos, gradientes) |
| **Ataque de Caixa Preta**  | Atacante só pode consultar o modelo (sem acesso interno)                |
| **Ataque de Caixa Cinza**  | Conhecimento parcial (arquitetura, mas sem pesos)                       |
| **Transferabilidade**      | Capacidade de um ataque em um modelo funcionar em outro                 |

#### **Tipos de Normas (Lp)**

```python
#!/usr/bin/env python3
# lp_norms.py

import numpy as np

def l0_norm(perturbation):
    """Número de pixels modificados (Contagem)"""
    return np.sum(perturbation != 0)

def l2_norm(perturbation):
    """Distância Euclidiana"""
    return np.sqrt(np.sum(perturbation ** 2))

def linf_norm(perturbation):
    """Máxima modificação em qualquer pixel (Chebyshev)"""
    return np.max(np.abs(perturbation))

# Exemplo
pert = np.array([0.01, -0.02, 0.03, 0.00, 0.00])
print(f"Norma L0: {l0_norm(pert)} (pixels modificados)")
print(f"Norma L2: {l2_norm(pert):.4f}")
print(f"Norma L∞: {linf_norm(pert):.4f} (máxima alteração)")
```

#### **Princípio de Funcionamento**

```mermaid
graph TD
    subgraph "Imagem Original"
        A[Panda 🐼]
    end
    
    subgraph "Perturbação Adversarial"
        B[Ruído Calculado]
    end
    
    subgraph "Imagem Adversarial"
        C[Gibão 🦍]
    end
    
    subgraph "Rede Neural"
        D[Modelo de Classificação]
    end
    
    A --> D
    A -- "+" --> B
    B --> C
    C --> D
    D -->|"Original: 99% Panda"| E["🐼"]
    D -->|"Adversarial: 99% Gibão"| F["🦍"]
```

***

### 🏗️ **Arquitetura e Mecanismos**

#### **Formulação Matemática**

Dado um modelo $f: \mathbb{R}^d \rightarrow \mathbb{R}^K$ e uma entrada legítima $x$ com rótulo verdadeiro $y$:

**Ataque não-alvo (non-targeted):** $$\min\_{\delta} ||\delta||\_p \quad \text{sujeito a} \quad \arg\max\_i f\_i(x + \delta) \neq y$$

**Ataque alvo (targeted) para classe alvo $t$:** $$\min\_{\delta} ||\delta||\_p \quad \text{sujeito a} \quad \arg\max\_i f\_i(x + \delta) = t$$

**Na prática (aproximação via gradiente):** $$x\_{adv} = x + \epsilon \cdot \text{sign}(\nabla\_x L(f(x), y))$$

#### **Por que Redes Neurais São Vulneráveis?**

```yaml
Razões para Vulnerabilidade:
  
  Alta Dimensionalidade:
    - Imagens têm milhões de dimensões
    - Espaço de perturbação é enorme
    - Mesmo restrições pequenas criam muitas possibilidades
  
  Linearidade Local:
    - Redes neurais são "acidentalmente" lineares em regiões locais
    - Pequenas mudanças na entrada causam mudanças previsíveis na saída
  
  Falta de Robustez Natural:
    - Modelos não são treinados para ser invariantes a ruído adversarial
    - Dados de treino não cobrem o espaço adversarial
  
  Superfície de Decisão Complexa:
    - Fronteiras de decisão são próximas aos pontos de dados
    - Pequeno movimento pode cruzar a fronteira
```

#### **Visualização do Espaço Adversarial**

```mermaid
graph TD
    subgraph "Espaço de Características"
        A[Região do Gato]
        B[Região do Cachorro]
        C[Fronteira de Decisão]
        D[Ponto de Dados - Gato]
        E[Perturbação Adversarial]
        F[Ponto Adversarial - Classificado como Cachorro]
    end
    
    D --> E
    E --> F
    C -.-> A
    C -.-> B
    D -.- C
    F -.- C
```

***

### 🎯 **Taxonomia de Ataques**

#### **Classificação por Objetivo**

| Tipo                               | Descrição                                      | Exemplo                                   |
| ---------------------------------- | ---------------------------------------------- | ----------------------------------------- |
| **Ataque Não-Alvo (Non-targeted)** | Fazer o modelo errar (qualquer classe errada)  | Panda → qualquer coisa que não seja panda |
| **Ataque Alvo (Targeted)**         | Forçar o modelo a prever uma classe específica | Panda → Gibão (especificamente)           |

#### **Classificação por Conhecimento**

| Tipo                         | Conhecimento                   | Aplicabilidade              | Dificuldade |
| ---------------------------- | ------------------------------ | --------------------------- | ----------- |
| **Caixa Branca (White-box)** | Arquitetura, pesos, gradientes | Pesquisa, modelos abertos   | Baixa       |
| **Caixa Preta (Black-box)**  | Apenas entradas e saídas       | APIs, modelos proprietários | Alta        |
| **Caixa Cinza (Gray-box)**   | Arquitetura (sem pesos)        | Transferabilidade           | Média       |

#### **Classificação por Acesso**

| Tipo               | Acesso                   | Exemplo                        |
| ------------------ | ------------------------ | ------------------------------ |
| **Query-based**    | Consulta ao modelo (API) | Boundary attack, HopSkipJump   |
| **Transfer-based** | Modelo substituto        | Ataques transferidos           |
| **Physical**       | Mundo real               | Adesivos em sinais de trânsito |

***

### ⚔️ **Principais Métodos de Ataque**

#### **1. FGSM (Fast Gradient Sign Method) - 2014**

**Criador:** Ian Goodfellow (Google Brain)

**Princípio:** Aplica o sinal do gradiente da função de perda em relação à entrada.

```python
#!/usr/bin/env python3
# fgsm_attack.py

import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision.models import resnet50

class FastGradientSignMethod:
    """
    FGSM - Ataque de Gradiente Rápido
    Um dos ataques mais simples e eficientes
    """
    
    def __init__(self, model, epsilon=0.1, targeted=False):
        self.model = model
        self.epsilon = epsilon
        self.targeted = targeted
    
    def attack(self, image, label):
        """
        Executar ataque FGSM
        
        Args:
            image: Tensor de entrada (batch, channels, height, width)
            label: Rótulo verdadeiro (para não-alvo) ou alvo (para alvo)
        
        Returns:
            Imagem adversarial
        """
        # Garantir que o modelo está em modo de treino (para gradientes)
        self.model.train()
        
        # Configurar gradiente
        image.requires_grad = True
        
        # Forward pass
        output = self.model(image)
        
        # Calcular perda
        if self.targeted:
            loss = nn.CrossEntropyLoss()(output, label)
            # Para ataque alvo, queremos MAXIMIZAR a perda da classe alvo?
            # Na verdade, minimizamos perda para classe errada
        else:
            loss = nn.CrossEntropyLoss()(output, label)
        
        # Backward pass
        self.model.zero_grad()
        loss.backward()
        
        # Calcular perturbação
        perturbation = self.epsilon * image.grad.sign()
        
        # Aplicar perturbação
        if self.targeted:
            adversarial = image - perturbation  # Mover em direção à classe alvo
        else:
            adversarial = image + perturbation  # Mover para longe da classe verdadeira
        
        # Garantir que os valores estejam no intervalo [0, 1]
        adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial
    
    def batch_attack(self, images, labels):
        """Aplicar ataque em lote"""
        adversarials = []
        for img, lbl in zip(images, labels):
            adv = self.attack(img.unsqueeze(0), lbl.unsqueeze(0))
            adversarials.append(adv)
        return torch.cat(adversarials, dim=0)

# Exemplo de uso
if __name__ == "__main__":
    model = resnet50(pretrained=True)
    model.eval()
    
    # Criar tensor de exemplo (imagem 224x224)
    image = torch.randn(1, 3, 224, 224)
    label = torch.tensor([207])  # Golden Retriever
    
    attacker = FastGradientSignMethod(model, epsilon=0.03)
    adversarial = attacker.attack(image, label)
    
    print(f"Original shape: {image.shape}")
    print(f"Adversarial shape: {adversarial.shape}")
    print(f"Perturbação máxima: {torch.max(torch.abs(adversarial - image)).item():.4f}")
```

#### **2. PGD (Projected Gradient Descent) - 2017**

**Criadores:** Madry et al. (Google Brain / MIT)

**Princípio:** Versão iterativa do FGSM, considerada um dos ataques mais fortes.

```python
#!/usr/bin/env python3
# pgd_attack.py

import torch
import torch.nn as nn

class ProjectedGradientDescent:
    """
    PGD - Projected Gradient Descent Attack
    Ataque iterativo de múltiplos passos
    """
    
    def __init__(self, model, epsilon=0.1, alpha=0.01, num_iter=40, random_start=True):
        self.model = model
        self.epsilon = epsilon
        self.alpha = alpha
        self.num_iter = num_iter
        self.random_start = random_start
    
    def attack(self, image, label):
        """
        Executar ataque PGD
        """
        # Inicialização aleatória dentro da bola L∞
        if self.random_start:
            delta = torch.zeros_like(image).uniform_(-self.epsilon, self.epsilon)
            adversarial = image + delta
            adversarial = torch.clamp(adversarial, 0, 1)
        else:
            adversarial = image.clone().detach()
        
        for _ in range(self.num_iter):
            adversarial.requires_grad = True
            
            # Forward pass
            output = self.model(adversarial)
            loss = nn.CrossEntropyLoss()(output, label)
            
            # Backward pass
            self.model.zero_grad()
            loss.backward()
            
            # Atualizar com gradiente ascendente
            with torch.no_grad():
                perturbation = self.alpha * adversarial.grad.sign()
                adversarial = adversarial + perturbation
                
                # Projetar de volta para a bola L∞
                eta = torch.clamp(adversarial - image, -self.epsilon, self.epsilon)
                adversarial = image + eta
                adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial

class PGDWithEarlyStopping(ProjectedGradientDescent):
    """
    PGD com parada antecipada (para eficiência)
    """
    
    def attack(self, image, label):
        adversarial = image.clone().detach()
        
        for i in range(self.num_iter):
            adversarial.requires_grad = True
            output = self.model(adversarial)
            
            # Verificar se já é adversarial
            pred = torch.argmax(output, dim=1)
            if pred != label:
                print(f"Parada antecipada na iteração {i}")
                break
            
            loss = nn.CrossEntropyLoss()(output, label)
            self.model.zero_grad()
            loss.backward()
            
            with torch.no_grad():
                perturbation = self.alpha * adversarial.grad.sign()
                adversarial = adversarial + perturbation
                eta = torch.clamp(adversarial - image, -self.epsilon, self.epsilon)
                adversarial = image + eta
                adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial
```

#### **3. C\&W (Carlini & Wagner Attack) - 2017**

**Princípio:** Otimização baseada em L2, considerada um dos ataques mais fortes.

```python
#!/usr/bin/env python3
# cw_attack.py

import torch
import torch.nn as nn
import numpy as np

class CarliniWagnerAttack:
    """
    Carlini & Wagner L2 Attack
    Um dos ataques mais poderosos, mas computacionalmente caro
    """
    
    def __init__(self, model, targeted=True, confidence=0, learning_rate=0.01, num_iter=1000):
        self.model = model
        self.targeted = targeted
        self.confidence = confidence
        self.learning_rate = learning_rate
        self.num_iter = num_iter
    
    def attack(self, image, target):
        """
        Executar ataque C&W
        """
        # Variável de otimização no espaço tanh (garante limites)
        w = torch.arctanh(image * 2 - 1).clone().detach().requires_grad_(True)
        best_adv = image.clone()
        best_dist = float('inf')
        
        optimizer = torch.optim.Adam([w], lr=self.learning_rate)
        
        for i in range(self.num_iter):
            # Converter de volta para espaço de imagem
            adv = (torch.tanh(w) + 1) / 2
            
            # Forward pass
            output = self.model(adv)
            
            # Calcular perda
            if self.targeted:
                # Queremos maximizar a probabilidade da classe alvo
                target_one_hot = torch.zeros_like(output)
                target_one_hot.scatter_(1, target.unsqueeze(1), 1)
                real = torch.sum(target_one_hot * output)
                other = torch.max((1 - target_one_hot) * output - target_one_hot * 10000)
                loss = torch.clamp(other - real + self.confidence, min=0)
            else:
                # Queremos minimizar a probabilidade da classe correta
                correct_logits = output.gather(1, target.unsqueeze(1)).squeeze()
                max_other = torch.max(output * (1 - torch.eye(output.shape[1])[target].to(output.device)), dim=1)[0]
                loss = torch.clamp(correct_logits - max_other + self.confidence, min=0)
            
            # Adicionar termo de distância L2
            l2_dist = torch.norm((adv - image).view(adv.shape[0], -1), dim=1)
            total_loss = loss + l2_dist
            
            # Otimizar
            optimizer.zero_grad()
            total_loss.backward()
            optimizer.step()
            
            # Atualizar melhor adversarial
            with torch.no_grad():
                pred = torch.argmax(self.model(adv), dim=1)
                if (self.targeted and pred == target) or (not self.targeted and pred != target):
                    dist = l2_dist.item()
                    if dist < best_dist:
                        best_dist = dist
                        best_adv = adv.clone()
            
            if i % 100 == 0:
                print(f"Iteração {i}: loss={total_loss.item():.4f}, dist={l2_dist.item():.4f}")
        
        return best_adv
```

#### **4. JSMA (Jacobian-based Saliency Map Attack) - 2016**

**Princípio:** Modifica os pixels mais "salientes" (que mais influenciam a saída).

```python
#!/usr/bin/env python3
# jsma_attack.py

import torch
import numpy as np

class JacobianSaliencyMapAttack:
    """
    JSMA - Jacobian-based Saliency Map Attack
    Ataque de saliência baseado no Jacobiano
    """
    
    def __init__(self, model, theta=0.1, gamma=1.0):
        self.model = model
        self.theta = theta  # Taxa de modificação por iteração
        self.gamma = gamma  # Parâmetro de saliência
    
    def compute_jacobian(self, image, target_class):
        """
        Calcular matriz Jacobiana para a classe alvo
        """
        image.requires_grad = True
        output = self.model(image)
        
        self.model.zero_grad()
        output[0, target_class].backward()
        
        return image.grad.clone()
    
    def compute_saliency_map(self, grad, target_class, other_classes):
        """
        Calcular mapa de saliência
        """
        # Saliency = positivo para classe alvo, negativo para outras
        saliency = grad.clone()
        
        # Penalizar pixels que aumentam outras classes
        for other in other_classes:
            other_grad = self.compute_jacobian(image, other)
            saliency -= self.gamma * other_grad
        
        return saliency
    
    def attack(self, image, target_class, max_iter=100):
        """
        Executar ataque JSMA
        """
        adversarial = image.clone()
        
        # Lista de outras classes
        num_classes = self.model(image).shape[1]
        other_classes = [c for c in range(num_classes) if c != target_class]
        
        for _ in range(max_iter):
            grad = self.compute_jacobian(adversarial, target_class)
            saliency = self.compute_saliency_map(grad, target_class, other_classes)
            
            # Encontrar pixel mais saliente
            saliency_flat = saliency.view(-1)
            best_pixel = torch.argmax(saliency_flat)
            
            # Modificar pixel
            adv_flat = adversarial.view(-1)
            adv_flat[best_pixel] += self.theta
            adv_flat = torch.clamp(adv_flat, 0, 1)
            adversarial = adv_flat.view(adversarial.shape)
            
            # Verificar sucesso
            pred = torch.argmax(self.model(adversarial), dim=1)
            if pred == target_class:
                break
        
        return adversarial
```

#### **5. Ataque de Transferabilidade**

```python
#!/usr/bin/env python3
# transfer_attack.py

import torch
import torch.nn as nn
from torchvision.models import resnet50, vgg16, alexnet

class TransferAttack:
    """
    Ataque transferido - gera adversarial em um modelo substituto
    e testa em modelos alvo
    """
    
    def __init__(self, substitute_model, epsilon=0.1):
        self.substitute_model = substitute_model
        self.epsilon = epsilon
    
    def generate_transferable(self, image, label, ensemble_models=None):
        """
        Gerar adversarial transferível usando ensemble de modelos
        """
        if ensemble_models is None:
            ensemble_models = [self.substitute_model]
        
        # Média dos gradientes de múltiplos modelos
        total_grad = None
        
        for model in ensemble_models:
            model.train()
            image.requires_grad = True
            
            output = model(image)
            loss = nn.CrossEntropyLoss()(output, label)
            
            model.zero_grad()
            loss.backward()
            
            if total_grad is None:
                total_grad = image.grad.clone()
            else:
                total_grad += image.grad
        
        # Média dos gradientes
        avg_grad = total_grad / len(ensemble_models)
        
        # Gerar adversarial
        perturbation = self.epsilon * avg_grad.sign()
        adversarial = image + perturbation
        adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial
    
    def test_transferability(self, adversarial, target_models, labels):
        """
        Testar transferabilidade para modelos alvo
        """
        results = {}
        
        for name, model in target_models.items():
            model.eval()
            with torch.no_grad():
                output = model(adversarial)
                pred = torch.argmax(output, dim=1)
                success = (pred != labels).float().mean().item()
                results[name] = success
        
        return results

# Exemplo de ensemble para transferabilidade
def create_ensemble():
    models = {
        'resnet50': resnet50(pretrained=True),
        'vgg16': vgg16(pretrained=True),
        'alexnet': alexnet(pretrained=True)
    }
    
    for model in models.values():
        model.eval()
    
    return models
```

#### **6. Boundary Attack (Caixa Preta)**

```python
#!/usr/bin/env python3
# boundary_attack.py

import torch
import numpy as np

class BoundaryAttack:
    """
    Boundary Attack - Ataque de caixa preta
    Apenas consulta o modelo, sem acesso a gradientes
    """
    
    def __init__(self, model, epsilon=0.1, max_iter=1000):
        self.model = model
        self.epsilon = epsilon
        self.max_iter = max_iter
    
    def query_model(self, image):
        """Consultar modelo (caixa preta)"""
        with torch.no_grad():
            output = self.model(image)
            return torch.argmax(output, dim=1)
    
    def attack(self, image, target_class=None):
        """
        Executar ataque Boundary
        """
        original_class = self.query_model(image).item()
        
        if target_class is None:
            # Ataque não-alvo: qualquer classe diferente
            target_class = (original_class + 1) % 1000
        
        # Inicializar com ruído grande
        noise = torch.randn_like(image) * self.epsilon
        adversarial = image + noise
        adversarial = torch.clamp(adversarial, 0, 1)
        
        # Buscar ponto na fronteira
        while self.query_model(adversarial) != target_class:
            noise = torch.randn_like(image) * self.epsilon * 2
            adversarial = image + noise
            adversarial = torch.clamp(adversarial, 0, 1)
        
        # Otimização da fronteira
        for i in range(self.max_iter):
            # Amostrar direção aleatória
            direction = torch.randn_like(adversarial)
            direction = direction / torch.norm(direction)
            
            # Tentar reduzir perturbação
            candidate = adversarial + self.epsilon * direction
            candidate = torch.clamp(candidate, 0, 1)
            
            # Verificar se ainda é adversarial
            if self.query_model(candidate) == target_class:
                adversarial = candidate
            
            # Reduzir epsilon gradualmente
            self.epsilon *= 0.995
        
        return adversarial
```

***

### 💻 **Implementação em Python**

#### **Framework Completo de Ataques**

```python
#!/usr/bin/env python3
# adversarial_framework.py

import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision.models import resnet50
import numpy as np
from typing import Tuple, Optional, List

class AdversarialFramework:
    """
    Framework unificado para ataques adversariais
    """
    
    def __init__(self, model, device='cuda'):
        self.model = model.to(device)
        self.device = device
    
    def fgsm(self, image: torch.Tensor, label: torch.Tensor, 
             epsilon: float = 0.1) -> torch.Tensor:
        """Fast Gradient Sign Method"""
        image.requires_grad = True
        output = self.model(image)
        loss = nn.CrossEntropyLoss()(output, label)
        
        self.model.zero_grad()
        loss.backward()
        
        perturbation = epsilon * image.grad.sign()
        adversarial = image + perturbation
        return torch.clamp(adversarial, 0, 1)
    
    def pgd(self, image: torch.Tensor, label: torch.Tensor,
            epsilon: float = 0.1, alpha: float = 0.01, 
            num_iter: int = 40) -> torch.Tensor:
        """Projected Gradient Descent"""
        adversarial = image.clone()
        
        for _ in range(num_iter):
            adversarial.requires_grad = True
            output = self.model(adversarial)
            loss = nn.CrossEntropyLoss()(output, label)
            
            self.model.zero_grad()
            loss.backward()
            
            with torch.no_grad():
                perturbation = alpha * adversarial.grad.sign()
                adversarial = adversarial + perturbation
                eta = torch.clamp(adversarial - image, -epsilon, epsilon)
                adversarial = image + eta
                adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial
    
    def deepfool(self, image: torch.Tensor, label: torch.Tensor,
                 max_iter: int = 50) -> torch.Tensor:
        """DeepFool Attack - perturbação mínima"""
        adversarial = image.clone()
        num_classes = self.model(image).shape[1]
        
        for _ in range(max_iter):
            adversarial.requires_grad = True
            output = self.model(adversarial)
            
            if torch.argmax(output) != label:
                break
            
            # Calcular gradientes para todas as classes
            grads = []
            for k in range(num_classes):
                self.model.zero_grad()
                output[0, k].backward(retain_graph=True)
                grads.append(adversarial.grad.clone())
                adversarial.grad.zero_()
            
            # Encontrar classe mais próxima
            f_k = output[0].detach()
            f_0 = f_k[label].item()
            
            min_dist = float('inf')
            best_w = None
            best_f = None
            
            for k in range(num_classes):
                if k == label:
                    continue
                w = grads[k] - grads[label]
                f = f_k[k] - f_0
                dist = abs(f) / torch.norm(w)
                
                if dist < min_dist:
                    min_dist = dist
                    best_w = w
                    best_f = f
            
            # Atualizar adversarial
            perturbation = (abs(best_f) / torch.norm(best_w)**2 + 1e-8) * best_w
            adversarial = adversarial + perturbation
            adversarial = torch.clamp(adversarial, 0, 1)
        
        return adversarial
    
    def evaluate_attack(self, attack_name: str, attack_func, 
                        test_loader, epsilon: float = 0.1) -> dict:
        """Avaliar eficácia do ataque"""
        correct = 0
        total = 0
        adversarial_correct = 0
        
        for images, labels in test_loader:
            images, labels = images.to(self.device), labels.to(self.device)
            
            # Predição original
            outputs = self.model(images)
            _, predicted = torch.max(outputs, 1)
            correct += (predicted == labels).sum().item()
            total += labels.size(0)
            
            # Gerar adversariais
            adversarials = []
            for img, lbl in zip(images, labels):
                adv = attack_func(img.unsqueeze(0), lbl.unsqueeze(0), epsilon)
                adversarials.append(adv)
            adversarials = torch.cat(adversarials, dim=0)
            
            # Predição adversarial
            adv_outputs = self.model(adversarials)
            _, adv_predicted = torch.max(adv_outputs, 1)
            adversarial_correct += (adv_predicted == labels).sum().item()
        
        return {
            'attack': attack_name,
            'original_accuracy': correct / total,
            'adversarial_accuracy': adversarial_correct / total,
            'success_rate': 1 - (adversarial_correct / total)
        }
```

***

### 🎨 **Ataques em Diferentes Domínios**

#### **1. Ataques em Imagens (Computer Vision)**

```python
#!/usr/bin/env python3
# image_adversarial.py

import torch
import torchvision.transforms as transforms
from PIL import Image
import requests
from io import BytesIO

class ImageAdversarialAttack:
    """
    Ataques adversariais em imagens
    """
    
    def __init__(self, model, imagenet_labels):
        self.model = model
        self.labels = imagenet_labels
        
        self.preprocess = transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                               std=[0.229, 0.224, 0.225])
        ])
    
    def load_image(self, url_or_path):
        """Carregar imagem de URL ou arquivo"""
        if url_or_path.startswith('http'):
            response = requests.get(url_or_path)
            img = Image.open(BytesIO(response.content))
        else:
            img = Image.open(url_or_path)
        
        return self.preprocess(img).unsqueeze(0)
    
    def visualize_attack(self, original, adversarial, true_label, adv_label):
        """Visualizar ataque e perturbação"""
        import matplotlib.pyplot as plt
        
        perturbation = adversarial - original
        perturbation = perturbation / perturbation.abs().max()  # Normalizar
        
        fig, axes = plt.subplots(1, 3, figsize=(15, 5))
        
        axes[0].imshow(original.squeeze().permute(1, 2, 0).numpy())
        axes[0].set_title(f"Original: {self.labels[true_label]}")
        axes[0].axis('off')
        
        axes[1].imshow(adversarial.squeeze().permute(1, 2, 0).numpy())
        axes[1].set_title(f"Adversarial: {self.labels[adv_label]}")
        axes[1].axis('off')
        
        axes[2].imshow(perturbation.squeeze().permute(1, 2, 0).numpy(), cmap='gray')
        axes[2].set_title("Perturbação (amplificada)")
        axes[2].axis('off')
        
        plt.tight_layout()
        plt.show()
```

#### **2. Ataques em Áudio (Speech Recognition)**

```python
#!/usr/bin/env python3
# audio_adversarial.py

import librosa
import numpy as np
import soundfile as sf

class AudioAdversarialAttack:
    """
    Ataques adversariais em sistemas de reconhecimento de voz
    """
    
    def __init__(self, sample_rate=16000):
        self.sample_rate = sample_rate
    
    def load_audio(self, file_path):
        """Carregar arquivo de áudio"""
        audio, sr = librosa.load(file_path, sr=self.sample_rate)
        return audio, sr
    
    def fgsm_audio(self, audio, gradient, epsilon=0.01):
        """Aplicar FGSM em áudio"""
        perturbation = epsilon * np.sign(gradient)
        adversarial = audio + perturbation
        return np.clip(adversarial, -1, 1)
    
    def add_imperceptible_noise(self, audio, epsilon=0.001):
        """Adicionar ruído imperceptível (simulação)"""
        noise = np.random.normal(0, epsilon, len(audio))
        return audio + noise
    
    def psychoacoustic_mask(self, audio, threshold_db=-60):
        """
        Mascaramento psicoacústico - ruído em frequências imperceptíveis
        """
        # Calcular espectrograma
        D = librosa.stft(audio)
        magnitude = np.abs(D)
        
        # Criar máscara de frequências menos perceptíveis
        mask = magnitude < threshold_db
        D_masked = D * mask
        
        # Reconstruir
        return librosa.istft(D_masked)
```

#### **3. Ataques em Texto (NLP)**

```python
#!/usr/bin/env python3
# text_adversarial.py

import numpy as np
from transformers import AutoTokenizer, AutoModelForSequenceClassification

class TextAdversarialAttack:
    """
    Ataques adversariais em modelos de linguagem
    """
    
    def __init__(self, model_name='distilbert-base-uncased-finetuned-sst-2-english'):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
    
    def hotflip_attack(self, text, target_label, num_flips=5):
        """
        HotFlip - modifica palavras baseado em gradientes
        """
        tokens = self.tokenizer(text, return_tensors='pt')
        input_ids = tokens['input_ids'][0]
        
        for _ in range(num_flips):
            # Calcular gradientes
            input_ids.requires_grad = True
            outputs = self.model(input_ids.unsqueeze(0))
            loss = outputs.loss
            
            self.model.zero_grad()
            loss.backward()
            
            # Encontrar token com maior gradiente
            gradients = input_ids.grad.abs()
            best_pos = torch.argmax(gradients[1:-1]) + 1  # Ignorar [CLS] e [SEP]
            
            # Substituir por token aleatório
            input_ids[best_pos] = torch.randint(0, self.tokenizer.vocab_size, (1,))
        
        return self.tokenizer.decode(input_ids)
    
    def character_substitution(self, text, substitution_rate=0.1):
        """
        Substituir caracteres por similares visualmente
        """
        similar_chars = {
            'a': '@', 'e': '3', 'i': '1', 'o': '0', 's': '$',
            'c': '(', 'l': '1', 't': '+', 'b': '8', 'g': '9'
        }
        
        chars = list(text)
        num_substitutions = int(len(chars) * substitution_rate)
        positions = np.random.choice(len(chars), num_substitutions, replace=False)
        
        for pos in positions:
            if chars[pos].lower() in similar_chars:
                chars[pos] = similar_chars[chars[pos].lower()]
        
        return ''.join(chars)
```

#### **4. Ataques Físicos (Sinais de Trânsito)**

```python
#!/usr/bin/env python3
# physical_adversarial.py

import cv2
import numpy as np

class PhysicalAdversarialAttack:
    """
    Ataques adversariais físicos (adesivos em sinais de trânsito)
    """
    
    def __init__(self, target_class):
        self.target_class = target_class
    
    def generate_sticker(self, image, target_class, size=(100, 100)):
        """
        Gerar adesivo adversarial para colocar em sinal de trânsito
        """
        # Simular transformações do mundo real
        image = self.simulate_weather(image)
        image = self.simulate_distance(image)
        image = self.simulate_rotation(image)
        
        # Calcular gradientes
        # (implementação real requer modelo físico)
        
        # Criar padrão de adesivo
        sticker = np.random.rand(*size, 3)
        return sticker
    
    def simulate_weather(self, image, condition='rain'):
        """Simular condições climáticas"""
        if condition == 'rain':
            noise = np.random.normal(0, 0.05, image.shape)
            return image + noise
        elif condition == 'fog':
            return 0.7 * image + 0.3
        return image
    
    def simulate_distance(self, image, distance_meters=10):
        """Simular distância da câmera"""
        scale = 1 / (distance_meters * 0.1)
        h, w = image.shape[:2]
        new_size = (int(w * scale), int(h * scale))
        return cv2.resize(image, new_size)
    
    def simulate_rotation(self, image, angle=15):
        """Simular rotação da câmera"""
        h, w = image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        return cv2.warpAffine(image, M, (w, h))
```

***

### 🛡️ **Defesas e Mitigações**

#### **1. Treinamento Adversarial (Adversarial Training)**

```python
#!/usr/bin/env python3
# adversarial_training.py

import torch
import torch.nn as nn
from torch.utils.data import DataLoader

class AdversarialTraining:
    """
    Treinamento adversarial - uma das defesas mais eficazes
    """
    
    def __init__(self, model, train_loader, epsilon=0.1, alpha=0.01):
        self.model = model
        self.train_loader = train_loader
        self.epsilon = epsilon
        self.alpha = alpha
        self.optimizer = torch.optim.Adam(model.parameters())
    
    def generate_adversarial_batch(self, images, labels):
        """Gerar lote adversarial (PGD)"""
        adversarials = images.clone()
        
        for _ in range(7):  # 7 iterações PGD
            adversarials.requires_grad = True
            outputs = self.model(adversarials)
            loss = nn.CrossEntropyLoss()(outputs, labels)
            
            self.model.zero_grad()
            loss.backward()
            
            with torch.no_grad():
                perturbation = self.alpha * adversarials.grad.sign()
                adversarials = adversarials + perturbation
                eta = torch.clamp(adversarials - images, -self.epsilon, self.epsilon)
                adversarials = images + eta
                adversarials = torch.clamp(adversarials, 0, 1)
        
        return adversarials
    
    def train_epoch(self):
        """Treinar uma época com exemplos adversariais"""
        total_loss = 0
        correct = 0
        total = 0
        
        for images, labels in self.train_loader:
            # Gerar exemplos adversariais
            adversarials = self.generate_adversarial_batch(images, labels)
            
            # Combinar dados originais e adversariais
            combined_images = torch.cat([images, adversarials])
            combined_labels = torch.cat([labels, labels])
            
            # Forward pass
            outputs = self.model(combined_images)
            loss = nn.CrossEntropyLoss()(outputs, combined_labels)
            
            # Backward pass
            self.optimizer.zero_grad()
            loss.backward()
            self.optimizer.step()
            
            total_loss += loss.item()
            _, predicted = torch.max(outputs, 1)
            correct += (predicted == combined_labels).sum().item()
            total += combined_labels.size(0)
        
        return total_loss / len(self.train_loader), correct / total
```

#### **2. Defensive Distillation**

```python
#!/usr/bin/env python3
# defensive_distillation.py

class DefensiveDistillation:
    """
    Defensive Distillation - treina modelo com soft labels
    Reduz gradientes e dificulta ataques
    """
    
    def __init__(self, teacher_model, student_model, temperature=20):
        self.teacher = teacher_model
        self.student = student_model
        self.temperature = temperature
    
    def train_student(self, train_loader, epochs=10):
        """Treinar modelo student com soft labels do teacher"""
        optimizer = torch.optim.Adam(self.student.parameters())
        
        for epoch in range(epochs):
            total_loss = 0
            
            for images, _ in train_loader:
                # Obter soft labels do teacher
                with torch.no_grad():
                    teacher_logits = self.teacher(images)
                    soft_labels = torch.softmax(teacher_logits / self.temperature, dim=1)
                
                # Treinar student
                student_logits = self.student(images) / self.temperature
                loss = nn.KLDivLoss()(nn.LogSoftmax(dim=1)(student_logits), soft_labels)
                
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                
                total_loss += loss.item()
            
            print(f"Epoch {epoch+1}: Loss={total_loss/len(train_loader):.4f}")
```

#### **3. Feature Squeezing**

```python
#!/usr/bin/env python3
# feature_squeezing.py

import numpy as np
import cv2

class FeatureSqueezing:
    """
    Feature Squeezing - reduz espaço de entrada para detectar ataques
    """
    
    @staticmethod
    def bit_depth_reduction(image, bits=4):
        """Reduzir profundidade de bits"""
        scale = 2 ** bits
        return np.floor(image * scale) / scale
    
    @staticmethod
    def spatial_smoothing(image, kernel_size=3):
        """Suavização espacial (filtro mediana)"""
        return cv2.medianBlur(image, kernel_size)
    
    @staticmethod
    def detect_adversarial(original_pred, squeezed_pred, threshold=0.1):
        """
        Detectar se imagem é adversarial
        Se a predição muda drasticamente após squeezing, é adversarial
        """
        diff = np.abs(original_pred - squeezed_pred)
        return diff > threshold
    
    @staticmethod
    def ensemble_squeeze(image, squeezers):
        """Aplicar múltiplas técnicas de squeezing"""
        results = []
        for squeezer in squeezers:
            squeezed = squeezer(image)
            results.append(squeezed)
        return results
```

#### **4. Detecção de Adversariais**

```python
#!/usr/bin/env python3
# adversarial_detection.py

import torch
import numpy as np
from sklearn.ensemble import IsolationForest

class AdversarialDetector:
    """
    Detector de amostras adversariais
    """
    
    def __init__(self, model, feature_extractor=None):
        self.model = model
        self.feature_extractor = feature_extractor or self._extract_features
        self.detector = None
    
    def _extract_features(self, image):
        """Extrair características internas do modelo"""
        activations = []
        
        def hook_fn(module, input, output):
            activations.append(output.detach())
        
        # Registrar hooks nas camadas
        hooks = []
        for name, module in self.model.named_modules():
            if isinstance(module, (torch.nn.ReLU, torch.nn.LeakyReLU)):
                hooks.append(module.register_forward_hook(hook_fn))
        
        # Forward pass
        self.model(image)
        
        # Remover hooks
        for hook in hooks:
            hook.remove()
        
        # Concatenar ativações
        features = torch.cat([a.flatten() for a in activations])
        return features.numpy()
    
    def fit(self, clean_images):
        """Treinar detector com imagens limpas"""
        features = []
        for img in clean_images:
            feat = self._extract_features(img.unsqueeze(0))
            features.append(feat)
        
        features = np.array(features)
        self.detector = IsolationForest(contamination=0.1, random_state=42)
        self.detector.fit(features)
    
    def predict(self, image):
        """Classificar se imagem é adversarial"""
        if self.detector is None:
            raise ValueError("Detector não treinado")
        
        features = self._extract_features(image.unsqueeze(0))
        return self.detector.predict([features])[0] == -1  # -1 = anomalia
```

#### **5. Randomização de Entrada**

```python
#!/usr/bin/env python3
# input_randomization.py

import torch
import torch.nn as nn

class RandomizedInputDefense:
    """
    Randomização de entrada - adiciona ruído aleatório antes da inferência
    Dificulta ataques que dependem de gradientes precisos
    """
    
    def __init__(self, model, noise_std=0.001, num_samples=5):
        self.model = model
        self.noise_std = noise_std
        self.num_samples = num_samples
    
    def predict(self, image):
        """
        Predição com randomização
        """
        predictions = []
        
        for _ in range(self.num_samples):
            # Adicionar ruído gaussiano
            noisy = image + torch.randn_like(image) * self.noise_std
            noisy = torch.clamp(noisy, 0, 1)
            
            with torch.no_grad():
                output = self.model(noisy)
                predictions.append(output)
        
        # Média das predições
        avg_output = torch.stack(predictions).mean(dim=0)
        return avg_output
```

***

### 🛠️ **Ferramentas e Frameworks**

#### **Ferramentas de Ataque**

| Ferramenta                               | Descrição                          | Instalação                                   | Uso                        |
| ---------------------------------------- | ---------------------------------- | -------------------------------------------- | -------------------------- |
| **CleverHans**                           | Biblioteca de ataques adversariais | `pip install cleverhans`                     | Pesquisa e educação        |
| **Foolbox**                              | Ataques robustos em PyTorch/TF     | `pip install foolbox`                        | Benchmarking               |
| **Adversarial Robustness Toolbox (ART)** | Framework IBM                      | `pip install adversarial-robustness-toolbox` | Produção                   |
| **TorchAttacks**                         | Coleção de ataques em PyTorch      | `pip install torchattacks`                   | Pesquisa                   |
| **TextAttack**                           | Ataques em NLP                     | `pip install textattack`                     | Processamento de linguagem |

#### **Ferramentas de Defesa**

| Ferramenta                     | Descrição             | Instalação                |
| ------------------------------ | --------------------- | ------------------------- |
| **Adversarial Robustness 360** | Defesas e avaliação   | `pip install art`         |
| **RobustBench**                | Benchmark de robustez | `pip install robustbench` |
| **AutoAttack**                 | Avaliação automática  | `pip install autoattack`  |

#### **Exemplo com Foolbox**

```python
#!/usr/bin/env python3
# foolbox_example.py

import foolbox as fb
import torch
import torchvision.models as models

# Carregar modelo
model = models.resnet18(pretrained=True)
model.eval()

# Criar modelo Foolbox
fmodel = fb.PyTorchModel(model, bounds=(0, 1))

# Carregar imagem de exemplo
image, label = fb.samples.sample_image(fmodel)

# Aplicar ataque FGSM
attack = fb.attacks.LinfFastGradientAttack()
adversarial = attack(fmodel, image, label, epsilons=0.03)

# Avaliar
clean_acc = fmodel.accuracy([image], [label])
adv_acc = fmodel.accuracy([adversarial], [label])

print(f"Acurácia limpa: {clean_acc}")
print(f"Acurácia adversarial: {adv_acc}")
```

***

### 📊 **Comparação de Métodos de Ataque**

| Método       | Força       | Velocidade   | Conhecimento | Transferabilidade | Aplicação          |
| ------------ | ----------- | ------------ | ------------ | ----------------- | ------------------ |
| **FGSM**     | Média       | Muito rápida | Caixa branca | Alta              | Ataques rápidos    |
| **PGD**      | Muito forte | Lenta        | Caixa branca | Média             | Benchmarking       |
| **C\&W**     | Fortíssimo  | Muito lenta  | Caixa branca | Baixa             | Ataques ótimos     |
| **JSMA**     | Forte       | Lenta        | Caixa branca | Baixa             | Ataques esparsos   |
| **DeepFool** | Forte       | Média        | Caixa branca | Média             | Perturbação mínima |
| **Boundary** | Forte       | Muito lenta  | Caixa preta  | N/A               | APIs               |
| **Transfer** | Média       | Rápida       | Caixa preta  | Alta              | Transferabilidade  |

***

### 🎯 **Aplicações em Segurança**

#### **Ataques (Red Team)**

| Cenário                               | Aplicação                                               | Impacto |
| ------------------------------------- | ------------------------------------------------------- | ------- |
| **Bypass de sistemas de vigilância**  | Fazer câmeras não detectarem pessoas                    | Crítico |
| **Fraude em reconhecimento facial**   | Desbloquear smartphone com rosto de outra pessoa        | Alto    |
| **Evasão de filtros de conteúdo**     | Classificar conteúdo impróprio como seguro              | Alto    |
| **Controle de assistentes de voz**    | Ativar comandos não autorizados ("Alexa, abra a porta") | Crítico |
| **Manipulação de sistemas autônomos** | Fazer carro autônomo ignorar sinais de parada           | Crítico |

#### **Defesas (Blue Team)**

| Técnica                    | Eficácia | Custo Computacional | Implementação |
| -------------------------- | -------- | ------------------- | ------------- |
| **Adversarial Training**   | Alta     | Alto                | Treinamento   |
| **Defensive Distillation** | Média    | Médio               | Treinamento   |
| **Feature Squeezing**      | Média    | Baixo               | Inferência    |
| **Ensemble Methods**       | Alta     | Alto                | Inferência    |
| **Input Randomization**    | Média    | Médio               | Inferência    |
| **Certified Defenses**     | Baixa    | Muito alto          | Pesquisa      |

***

### 📚 **Referências e Leitura Recomendada**

| Paper                                                         | Autores           | Ano  | Foco               |
| ------------------------------------------------------------- | ----------------- | ---- | ------------------ |
| Intriguing properties of neural networks                      | Szegedy et al.    | 2013 | Descoberta inicial |
| Explaining and Harnessing Adversarial Examples                | Goodfellow et al. | 2014 | FGSM               |
| Towards Deep Learning Models Resistant to Adversarial Attacks | Madry et al.      | 2017 | PGD                |
| Adversarial Machine Learning at Scale                         | Gilmer et al.     | 2018 | Escala             |
| Obfuscated Gradients Give a False Sense of Security           | Athalye et al.    | 2018 | Crítica de defesas |
| Reliable evaluation of adversarial robustness                 | Carlini et al.    | 2019 | Avaliação          |

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/ia-e-llm/ataques-adversariais-adversarial-attacks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
