Адаптивная Стеганография: Искусство прятаться в шуме

Адаптивная Стеганография: Искусство прятаться в шуме

Классический LSB-метод встраивает данные последовательно или случайно, но он не учитывает контент изображения. Это делает его уязвимым: изменение гладкого голубого неба (где все пиксели одинаковы) заметно статистически. Ответом на это стала Адаптивная Стеганография.


Идея: Прячем там, где не видно

Адаптивные алгоритмы (такие как HUGO, WOW, S-UNIWARD) работают по принципу «минимума искажений». Прежде чем спрятать бит, алгоритм анализирует изображение и строит карту сложности.

  • Гладкие области (Smooth): Низкая сложность. Изменения здесь сильно искажают статистику. Алгоритм их избегает.
  • Текстуры и границы (Edges/Noise): Высокая сложность. Здесь пиксели и так сильно отличаются друг от друга. Изменение LSB в листве деревьев или на границе объектов статистически неотличимо от естественного шума камеры.

Таким образом, адаптивная стеганография прячет данные только в «шумных» частях картинки, делая их невидимыми для RS-анализа и других простых атак.


Пример кода: Адаптивное встраивание по границам (Python)

Мы напишем упрощенный пример, который вычисляет карту границ (градиент) и прячет данные только в пикселях с высокой вариацией.

import cv2
import numpy as np

def to_bin(data):
    return ''.join(format(ord(i), '08b') for i in data)

def hide_adaptive(image_path, secret, output_path, threshold=30):
    """
    Встраивает данные только в области границ (высокая дисперсия).
    threshold: порог 'шумности', выше которого можно прятать.
    """
    img = cv2.imread(image_path)
    if img is None:
        return "Ошибка открытия"
    
    # 1. Вычисляем карту границ (используем градиент Собеля)
    # Преобразуем в оттенки серого для анализа сложности
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # dx и dy - изменения яркости по X и Y
    grad_x = cv2.Sobel(gray, _64F, 1, 0, ksize=3)
    grad_y = cv2.Sobel(gray, _64F, 0, 1, ksize=3)
    
    # Общая величина градиента в каждой точке
    magnitude = cv2.magnitude(grad_x, grad_y)
    
    # 2. Подготовка данных
    binary_secret = to_bin(secret + "[END]")
    data_idx = 0
    data_len = len(binary_secret)
    
    height, width, channels = img.shape
    capacity = 0
    
    # 3. Встраивание
    for y in range(height):
        for x in range(width):
            # Проверяем, достаточно ли 'шумный' этот пиксель
            # magnitude[y, x] показывает силу границы в этой точке
            if magnitude[y, x] > threshold:
                capacity += 1
                if data_idx < data_len:
                    # Прячем в синий канал (0)
                    current_bit = int(binary_secret[data_idx])
                    img[y, x, 0] = (img[y, x, 0] & 254) | current_bit
                    data_idx += 1
    
    cv2.imwrite(output_path, img)
    
    if data_idx < data_len:
        return f"Ошибка: Не хватило места. Спрятано {data_idx} из {data_len} бит. Попробуйте уменьшить порог."
        
    return f"Успешно. Использовано пикселей-границ: {capacity}. Файл: {output_path}"

# # Пример использования
# # Данные будут спрятаны только в контурах объектов, оставляя небо/фоны чистыми.
# print(hide_adaptive('landscape.jpg', 'Секрет в лесу', 'adaptive_stego.png'))

Извлечение данных из такого контейнера требует повторения процесса: получатель должен вычислить ту же самую карту границ (градиент), чтобы понять, какие именно пиксели читать. Это создает проблему синхронизации, так как само встраивание слегка меняет пиксели и может изменить карту границ. Профессиональные алгоритмы (WOW) используют специальные функции «стоимости», устойчивые к изменениям LSB.

Поделиться: