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