Привет, Хабр!
В нашем блоге мы говорим о стеганографии — искусстве сокрытия информации. Встроить секретное сообщение в картинку методом LSB (замены младших значащих бит) достаточно просто. Но как насчет обратной задачи? Как понять, является ли безобидный с виду файл троянским конем, несущим скрытые данные?
Просто извлечь их не получится — если они зашифрованы, мы получим лишь случайный шум. Нам нужно доказать сам факт наличия скрытого сообщения. Это и есть задача стегоанализа.
Сегодня мы не будем прятать. Мы будем охотиться. В этой статье мы с нуля напишем простой, но эффективный стегоанализатор на Python. Мы разберем:
Теорию: Почему статистика — главный враг стеганографии.
Важный нюанс: Почему классический LSB и JPEG несовместимы.
Практику: Реализуем два метода детекции — визуальный и статистический тест «Хи-квадрат».
Инструментарий: Соберем все в единый инструмент и научимся интерпретировать его результаты.
Все примеры кода основаны на логике, реализованной в нашем проекте ChameleonLab. Поехали!

Часть 1. Теория: Статистические отпечатки пальцев
Чтобы поймать преступника, криминалисты ищут аномалии: следы, отпечатки, нарушения привычного порядка вещей. В стегоанализе все точно так же. Наша «аномалия» — это нарушение естественной статистики изображения.
Шум настоящегоизображения: Младшие биты (LSB) пикселей в настоящей фотографии — это не просто случайный мусор. Они содержат информацию о микротекстурах и шумах матрицы. Этот хаос имеет свою, естественную структуру.
«Идеальный» шум стегоконтейнера: Зашифрованные данные, которые мы прячем, неотличимы от случайного шума. Когда мы этой «идеально случай��ой» последовательностью заменяем LSB пикселей, мы уничтожаем естественные корреляции. Младшие биты становятся слишком случайными. И этот парадокс — идеальный порядок в хаосе — и есть тот самый след, который мы будем искать.
Часть 2. Важное отступление: Почему JPEG — враг LSB
Прежде чем мы перейдем к коду, нужно прояснить критически важный момент. Классический LSB-метод нельзя напрямую применять к JPEG.
Проблема кроется в природе JPEG — это формат сжатия с потерями. Когда вы сохраняете изображение в JPEG, его пиксельные данные проходят через этап квантования, на котором часть информации безвозвратно теряется для уменьшения размера файла.
Если бы мы сначала применили LSB к пикселям, а потом сохранили изображение как JPEG, то алгоритм сжатия почти наверняка изменил бы значения цветов и полностью разрушил бы наше хрупкое LSB-сообщение.
Именно поэтому в ChameleonLab заложена защита от этой ошибки. Если вы открываете JPEG для LSB-встраивания, программа предложит сохранить результат в формате без потерь, например, PNG. Это гарантирует, что измененные биты пикселей сохранятся один в один.
Для формата JPEG существуют свои, более сложные методы стеганографии (JSteg, F5), которые работают не с пикселями, а с частотными DCT-коэффициентами. Возможно, мы разберем их в одной из будущих статей.
Часть 3. Метод №1: Визуальный анализ
Это самый простой метод. Его идея — извлечь LSB-план изображения и посмотреть на него как на отдельную черно-белую картинку. Равномерный "цифровой" шум, похожий на рябь на экране, — верный повод для подозрений.

Реализация на 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: Атака «Хи-квадрат»
Это мощный статистический тест, который позволяет оценить, насколько наблюдаемые нами данныесоответствуют теоретическому ожиданию. Проще говоря, мы проверяем, не слишком ли «правильно» и равномерно распределены младшие биты.

На выходе тест дает нам 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 можно на нашем официальном сайте. Для энтузиастов программа также доступна на популярных торрент-трекерах.
