Привет, Хабр!

В нашем блоге мы говорим о стеганографии — искусстве сокрытия информации. Встроить секретное сообщение в картинку методом LSB (замены младших значащих бит) достаточно просто. Но как насчет обратной задачи? Как понять, является ли безобидный с виду файл троянским конем, несущим скрытые данные?

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

Сегодня мы не будем прятать. Мы будем охотиться. В этой статье мы с нуля напишем простой, но эффективный стегоанализатор на Python. Мы разберем:

  • Теорию: Почему статистика — главный враг стеганографии.

  • Важный нюанс: Почему классический LSB и JPEG несовместимы.

  • Практику: Реализуем два метода детекции — визуальный и статистический тест «Хи-квадрат».

  • Инструментарий: Соберем все в единый инструмент и научимся интерпретировать его результаты.

Все примеры кода основаны на логике, реализованной в нашем проекте ChameleonLab. Поехали!

Программа "ChameleonLab". Версия 1.5.0.0
Программа "ChameleonLab". Версия 1.5.0.0

Часть 1. Теория: Статистические отпечатки пальцев

Чтобы поймать преступника, криминалисты ищут аномалии: следы, отпечатки, нарушения привычного порядка вещей. В стегоанализе все точно так же. Наша «аномалия» — это нарушение естественной статистики изображения.

  • Шум настоящегоизображения: Младшие биты (LSB) пикселей в настоящей фотографии — это не просто случайный мусор. Они содержат информацию о микротекстурах и шумах матрицы. Этот хаос имеет свою, естественную структуру.

  • «Идеальный» шум стегоконтейнера: Зашифрованные данные, которые мы прячем, неотличимы от случайного шума. Когда мы этой «идеально случайной» последовательностью заменяем LSB пикселей, мы уничтожаем естественные корреляции. Младшие биты становятся слишком случайными. И этот парадокс — идеальный порядок в хаосе — и есть тот самый след, который мы будем искать.

Часть 2. Важное отступление: Почему JPEG — враг LSB

Прежде чем мы перейдем к коду, нужно прояснить критически важный момент. Классический LSB-метод нельзя напрямую применять к JPEG.

Проблема кроется в природе JPEG — это формат сжатия с потерями. Когда вы сохраняете изображение в JPEG, его пиксельные данные проходят через этап квантования, на котором часть информации безвозвратно теряется для уменьшения размера файла.

Если бы мы сначала применили LSB к пикселям, а потом сохранили изображение как JPEG, то алгоритм сжатия почти наверняка изменил бы значения цветов и полностью разрушил бы наше хрупкое LSB-сообщение.

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

Для формата JPEG существуют свои, более сложные методы стеганографии (JSteg, F5), которые работают не с пикселями, а с частотными DCT-коэффициентами. Возможно, мы разберем их в одной из будущих статей.

Часть 3. Метод №1: Визуальный анализ

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

Программа "ChameleonLab".  Анализ бит-планов
Программа "ChameleonLab". Анализ бит-планов

Реализация на Python

Логика идентична той, что используется в ChameleonLab на вкладке «Анализ бит‑планов».

import numpy as np
from PIL import Image

def extract_lsb_plane(image_path: str):
    """
    Извлекает LSB-план (нулевой бит) из изображения.
    """
    try:
        with Image.open(image_path) as img:
            rgb_img = img.convert('RGB')
        
        img_array = np.array(rgb_img, dtype=np.uint8)
        lsb_plane = (img_array & 1) * 255
      
        return Image.fromarray(lsb_plane, 'RGB')
    except Exception as e:
        print(f"Ошибка при обработке файла: {e}")
        return None

Часть 4. Метод №2: Атака «Хи-квадрат»

Это мощный статистический тест, который позволяет оценить, насколько наблюдаемые нами данныесоответствуют теоретическому ожиданию. Проще говоря, мы проверяем, не слишком ли «правильно» и равномерно распределены младшие биты.

Программа "ChameleonLab".  Визуальный анализ
Программа "ChameleonLab". Визуальный анализ

На выходе тест дает нам p-value — вероятность того, что наблюдаемое распределение является случайным.

  • Низкое p-value (0.1-0.5): Нормальная ситуация для фото.

  • Высокое p-value (> 0.95): Тревога! Данные с огромной вероятностью являются идеально случайными, что крайне неестественно для обычного изображения и является сильным признаком стеганографии.

Реализация на Python

Код ниже основан на логике из analysis_utils.py нашего проекта.

from scipy.stats import chi2

def chi_squared_attack(image_path: str):
    """
    Проводит атаку Хи-квадрат на LSB-слой изображения.
    """
    try:
        with Image.open(image_path) as img:
            channel_data = np.array(img.convert('L'), dtype=np.uint8).flatten()

        frequencies = np.bincount(channel_data, minlength=256)
        
        chi2_stat = 0.0
        degrees_of_freedom = 0

        for i in range(0, 256, 2):
            f_even, f_odd = frequencies[i], frequencies[i+1]
            if f_even + f_odd == 0: continue
            
            expected = (f_even + f_odd) / 2.0
            if expected > 0:
                chi2_stat += ((f_even - expected)**2 / expected)
                degrees_of_freedom += 1
        
        p_value = chi2.sf(chi2_stat, degrees_of_freedom - 1)
        return chi2_stat, p_value
    except Exception as e:
        print(f"Ошибка при анализе файла: {e}")
        return None, None

Часть 5. Собираем всё вместе

Объединим наши функции в один инструмент и добавим построение гистограммы LSB.

import matplotlib.pyplot as plt
from pathlib import Path

def analyze_image(image_path: str):
    """
    Проводит комплексный анализ изображения и выводит отчет.
    """
    print(f"--- АНАЛИЗ ФАЙЛА: {image_path} ---")

    # 1. Визуальный анализ LSB
    print("\n[1] Визуальный анализ LSB-плана...")
    lsb_plane_img = extract_lsb_plane(image_path)
    if lsb_plane_img:
        lsb_plane_path = f"lsb_plane_{Path(image_path).stem}.png"
        lsb_plane_img.save(lsb_plane_path)
        print(f"LSB-план сохранен в '{lsb_plane_path}'.")
    
    # 2. Статистический анализ (Хи-квадрат)
    print("\n[2] Статистический анализ (Хи-квадрат)...")
    chi2_stat, p_value = chi_squared_attack(image_path)
    if p_value is not None:
        print(f"  - P-value: {p_value:.4f}")
        if p_value > 0.95:
            print("  - ВЫВОД: С ВЫСОКОЙ ВЕРОЯТНОСТЬЮ ПРИСУТСТВУЕТ LSB-СТЕГАНОГРАФИЯ.")
        else:
            print("  - ВЫВОД: Признаков простого LSB-внедрения не обнаружено.")
            
    # 3. Гистограмма LSB
    print("\n[3] Построение гистограммы LSB...")
    # ... (код для построения гистограммы)
    
    print("\n--- АНАЛИЗ ЗАВЕРШЕН ---")

Заключение

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

Проект постоянно развивается благодаря вам. Идеи, которые мы получаем в нашем Telegram-канале, и оперативные сообщения об ошибках позволяют нам делать каждую версию лучше предыдущей. Спасибо, что остаетесь с нами!

Скачать последнюю версию ChameleonLab для Windows и macOS можно на нашем официальном сайте. Для энтузиастов программа также доступна на популярных торрент-трекерах.

Комментарии (0)


  1. Demmidovich
    21.09.2025 23:13

    Спасибо за интересные статьи. Всегда в разных направления и все читается на одном дыхании. Сделайте больше статей о том, как реализовать встраивание в PDF и когда выйдет версия на Linux?


    1. Lomakn Автор
      21.09.2025 23:13

      Спасибо огромное за теплые слова! Очень рады, что нашлось время и на чтение, и на практику.

      Про PDF — отличная идея, тема действительно глубокая и неочевидная. Мы как раз планируем посвятить ей отдельный большой материал, там есть о чем рассказать.

      Версия под Linux у нас в планах, все упирается в наше железо на котом сделать порт. Виртуальные машины плохо работают с macOS на Intel


  1. JBFW
    21.09.2025 23:13

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

    Так-то тоже интересные технические задачи...


    1. unC0Rr
      21.09.2025 23:13

      Детсадовские методы встраивания и определяются легко. Настоящая стеганография так легко не поддаётся. Вообще, весь смысл стеганографии в том, чтобы нельзя было определить наличие скрытого сообщения с вероятностью, отличной от 0,5. Никакие инструменты не должны менять эту вероятность.


      1. Lomakn Автор
        21.09.2025 23:13

        Спасибо за экспертное мнение, вы абсолютно правы. Эта статья была задумана как разбор самых основ, с которых начинается путь в стегоанализ. Мы намеренно начали с "детсадовских" методов, потому что на их примере проще всего показать базовые принципы обнаружения.

        Ваше замечание про вероятность 0,5 — это как раз "святой Грааль" стеганографии, к которому стремятся все серьезные алгоритмы. Именно об этом — об адаптивных методах, которые стараются не нарушать статистику контейнера — мы и хотим поговорить в следующих, более сложных статьях. Будет здорово, если вы тоже присоединитесь к обсуждению!


        1. JBFW
          21.09.2025 23:13

          Берём какой-нибудь достаточно большой криптоконтейнер, в широком смысле - шифрованный файл. У него будет достаточно неплохо со "случайными данными".

          А целевое сообщение вписываем в середину, также шифрованное, в места, определяемые в процессе внедрения из пароля.

          Тогда внешне это выглядит как криптоконтейнер, который невозможно полностью расшифровать, так как он испорчен, но на самом деле данные в нем не там, где их будут искать...

          Как доказать их наличие? Ввести заведомо верный пароль криптоконтейнера, и предположить что проблема в стеганографии, но для этого пароль надо знать, а он может быть вообще любым, так как никому не нужен.


          1. unC0Rr
            21.09.2025 23:13

            В стеганографии, как и криптографии, действует принцип Кирхгофа: атакующий знает алгоритм встраивания, но не знает ключ. С учётом этого принципа получается невозможно скрыть наличие сообщения, внедрённого данным алгоритмом.


  1. kilfoy
    21.09.2025 23:13

    Стеганография это хорошо, но почему материал "Сложный" когда на деле он элементарный?


    1. Lomakn Автор
      21.09.2025 23:13

      Спасибо за фидбек! Вы правы, для специалиста в этой области LSB-анализ — это действительно базовый уровень. Мы поставили хаб "Сложный", ориентируясь на широкую IT-аудиторию, для которой сами концепции стегоанализа и статистических тестов могут быть в новинку. В будущем постараемся точнее подбирать уровень сложности.


  1. ExternalWayfarer
    21.09.2025 23:13

    Зачем атаковать Python?


    1. Lomakn Автор
      21.09.2025 23:13

      Хороший вопрос! Здесь "на Python" означает "реализованный с помощью языка Python", а не "направленный против него". То есть, мы пишем анализатор, используя Python как инструмент.


      1. petropavel
        21.09.2025 23:13

        Это был намёк, что название можно было придумать попонятнее, такое, которое потом не надо пояснять в комментариях, что оно означает. Ваш Капитан Очевидность.