Сколько весит секрет? Считаем стеганографическую емкость контейнеров на Python

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр! При работе со стеганографией первый и самый важный вопрос, который возникает перед пользователем: «А мой файл вообще поместится в эту картинку?». Попытка спрятать 10-мегабайтный архив в иконку размером 64x64 пикселя обречена на провал. Именно поэтому оценка стеганографической емкости контейнера — это краеугольный камень любой операции по сокрытию данных.

Емкость — это не просто размер файла. Это сложное понятие, которое кардинально меняется в зависимости от формата контейнера (PNG, JPEG, DOCX) и метода сокрытия (LSB, DCT и др.).

Сегодня на примере кода из нашего проекта ChameleonLab мы подробно разберем, как вычисляется емкость для разных типов файлов, и напишем соответствующие функции на Python.

Основы основ — LSB-емкость в растровых изображениях (PNG, BMP)

Это самый простой и интуитивно понятный случай. Метод LSB (Least Significant Bit) позволяет заменять наименее значимые биты в каждом байте цвета пикселя.

Математика

Расчет здесь — простая арифметика:

  1. Считаем общее число байт: Изображение состоит из пикселей, каждый пиксель — из каналов (обычно 3: Red, Green, Blue). Общее число байт, доступных для модификации, это ширина × высота × 3.

  2. Умножаем на n_bits: n_bits — это количество "младших" бит, которые мы решаем использовать в каждом байте (от 1 до 8). Общая емкость в битах = (ширина × высота × 3) × n_bits.

  3. Переводим в байты: Делим общее число бит на 8.

  4. Вычитаем накладные расходы: Всегда нужно оставить немного места (10-20 байт) для служебной информации: сигнатуры, длины сообщения, флага шифрования и т.д.

Код

Эта логика реализована в функции calculate_capacity в нашем файле steganography_core.py:

# steganography_core.py
import numpy as np

def calculate_capacity(carrier_bytes: np.ndarray, n_bits: int) -> int:
    """
    Вычисляет максимальную стеганографическую емкость контейнера в байтах для LSB.
    """
    if n_bits < 1 or n_bits > 8:
        raise ValueError("n_bits должен быть в диапазоне 1..8")

    # carrier_bytes.size — это и есть (ширина * высота * каналы) для NumPy массива
    total_bits = int(carrier_bytes.size) * int(n_bits)
    
    # Делим на 8, чтобы получить байты, и вычитаем небольшой запас на заголовок
    return (total_bits // 8) - 10

Например, для картинки 1920x1080 (Full HD) с 3 каналами (RGB) и n_bits = 2:

  • 1920 1080 3 = 6 220 800 байт данных.

  • 6 220 800 * 2 = 12 441 600 доступных бит.

  • 12 441 600 / 8 = 1 555 200 байт.

  • 1 555 200 - 10 ≈ 1.55 мегабайт полезной нагрузки.

Программа "ChameleonLab". Количество бит: 1
Программа "ChameleonLab". Количество бит: 1
Программа "ChameleonLab". Количество бит: 8
Программа "ChameleonLab". Количество бит: 8

Продвинутый уровень — емкость в JPEG (DCT-метод)

С JPEG все гораздо сложнее. Это формат сжатия с потерями, и прямой LSB-анализ пикселей здесь не работает — при сохранении файла все ваши изменения будут уничтожены.

Встраивание происходит в коэффициенты ДКП (DCT) — это числовые представления частотных компонент изображения. Мы можем прятать по 1 биту в LSB каждого такого коэффициента.

Логика и ограничения

  1. Только AC-коэффициенты: В каждом блоке 8x8 есть один DC-коэффициент (основная яркость) и 63 AC-коэффициента (детали). DC-коэффициент очень важен для изображения, и его изменение сильно заметно, поэтому мы его не трогаем.

  2. Проблема нулей: Если значение коэффициента равно 1 или -1, изменение его LSB превратит его в 0 или -2. Нулевые AC-коэффициенты часто отбрасываются при сжатии, что может повредить всю цепочку скрытых данных.

  3. Итог: Надежная емкость JPEG — это количество AC-коэффициентов, абсолютное значение которых больше 1.

Код

Для работы с JPEG мы используем библиотеку jpegio. Наша функция calculate_capacity_dct из файла steganography_dct.py реализует описанную логику:

# steganography_dct.py
try:
    import jpegio
except ImportError:
    jpegio = None

def calculate_capacity_dct(carrier_path: str) -> int:
    """
    Рассчитывает емкость, считая все ненулевые AC-коэффициенты, 
    изменение которых не приведет к 0.
    """
    if jpegio is None:
        raise ImportError("Библиотека 'jpegio' не найдена.")
        
    try:
        jpeg = jpegio.read(carrier_path)
    except Exception:
        return 0
    
    available_coeffs = 0
    # Считаем коэффициенты только в канале яркости (Y) - он первый
    y_coeffs = jpeg.coef_arrays[0]
    
    # Проходим по каждому блоку 8x8
    for block_idx in range(y_coeffs.shape[0] * y_coeffs.shape[1] // 64):
        block = y_coeffs.reshape(-1, 64)[block_idx]
        # Проходим по AC-коэффициентам (индексы с 1 по 63)
        for i in range(1, 64):
            # Считаем только те, что не превратятся в 0 при изменении LSB
            if abs(block[i]) > 1:
                available_coeffs += 1
    
    # Каждый такой коэффициент может хранить 1 бит. Делим на 8 и вычитаем запас.
    return available_coeffs // 8 - 20

Особый случай — емкость в офисных документах (DOCX, ODF)

Документы .docx, .xlsx, .odf и т.п. — это, по сути, ZIP-архивы. Метод стеганографии здесь заключается не в изменении существующего контента, а в добавлении нового файла внутрь этой архивной структуры.

Это означает, что у таких документов нет фиксированной стеганографической емкости. Предел ограничен лишь файловой системой и здравым смыслом — ведь файл .docx размером 500 МБ вызовет гораздо больше подозрений, чем обычный. В нашем приложении для таких форматов расчет емкости не выводится, а сообщается, что можно встроить файл практически любого размера.

Практическое применение — зачем все это нужно?

В интерфейсе «ChameleonLab» расчет емкости происходит автоматически при выборе файла-контейнера. Программа немедленно сообщает пользователю, сколько места доступно, и сравнивает это с размером выбранного секрета. Это позволяет избежать ошибок и сразу понять, какой метод и какие настройки (n_bits) лучше использовать для конкретной задачи.

Заключение

Мы увидели, что стеганографическая емкость — это не одна простая формула, а целый набор подходов, зависящих от формата файла-контейнера.

  • Для PNG/BMP это простая арифметика на основе пикселей и n_bits.

  • Для JPEG это сложный подсчет "безопасных" DCT-коэффициентов.

  • Для DOCX/ODF понятие емкости практически отсутствует.

Понимание этих различий — ключ к эффективному и безопасному сокрытию данных.

Последнюю версию программы «Steganographia» от ChameleonLab для Windows и macOS можно скачать на нашем официальном сайте. https://chalab.ru

А чтобы быть в курсе обновлений, обсуждать новые функции и общаться с единомышленниками, присоединяйтесь к нашему Telegram-каналу: https://t.me/ChameleonLab

Источник: https://habr.com/ru/articles/943742/


Интересные статьи

Интересные статьи

Перед вами обновлённая коллекция вредных советов для C++ программистов, которая превратилась в целую электронную книгу. Всего их 60, и каждый сопровождается пояснением, почему на самом деле ему не с...
Во второй части статьи про bitrix кластер мы будет рассматривать настройку percona xtradb cluster mysql 5.7, настройка арбитра, настройка удобного dashboard.Если вы пропустили предыдущие части: Первая...
В интернет-магазинах, в том числе сделанных на готовых решениях 1C-Битрикс, часто неправильно реализован функционал быстрого заказа «Купить в 1 клик».
В статье описаны необходимые параметры сервера для оптимальной работы сайта на платформе 1С-Битрикс.
Очередной выпуск рассказывает про морок функционального программирования, солидный Swift для солидных программистов, дорожную карту Android-разработки, стабильность модного приложения, путаницу п...