Привет, Хабр!
В нашем блоге мы говорим о стеганографии — искусстве сокрытия информации. Встроить секретное сообщение в картинку методом 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 можно на нашем официальном сайте. Для энтузиастов программа также доступна на популярных торрент-трекерах.
Комментарии (0)
JBFW
21.09.2025 23:13Технически - очень интересно, не технически - ну, хм, "определяем VPN-трафик", "блокируем запрещенные сайты", "идентификация человека по походке"...
Так-то тоже интересные технические задачи...
unC0Rr
21.09.2025 23:13Детсадовские методы встраивания и определяются легко. Настоящая стеганография так легко не поддаётся. Вообще, весь смысл стеганографии в том, чтобы нельзя было определить наличие скрытого сообщения с вероятностью, отличной от 0,5. Никакие инструменты не должны менять эту вероятность.
Lomakn Автор
21.09.2025 23:13Спасибо за экспертное мнение, вы абсолютно правы. Эта статья была задумана как разбор самых основ, с которых начинается путь в стегоанализ. Мы намеренно начали с "детсадовских" методов, потому что на их примере проще всего показать базовые принципы обнаружения.
Ваше замечание про вероятность 0,5 — это как раз "святой Грааль" стеганографии, к которому стремятся все серьезные алгоритмы. Именно об этом — об адаптивных методах, которые стараются не нарушать статистику контейнера — мы и хотим поговорить в следующих, более сложных статьях. Будет здорово, если вы тоже присоединитесь к обсуждению!
JBFW
21.09.2025 23:13Берём какой-нибудь достаточно большой криптоконтейнер, в широком смысле - шифрованный файл. У него будет достаточно неплохо со "случайными данными".
А целевое сообщение вписываем в середину, также шифрованное, в места, определяемые в процессе внедрения из пароля.
Тогда внешне это выглядит как криптоконтейнер, который невозможно полностью расшифровать, так как он испорчен, но на самом деле данные в нем не там, где их будут искать...
Как доказать их наличие? Ввести заведомо верный пароль криптоконтейнера, и предположить что проблема в стеганографии, но для этого пароль надо знать, а он может быть вообще любым, так как никому не нужен.
unC0Rr
21.09.2025 23:13В стеганографии, как и криптографии, действует принцип Кирхгофа: атакующий знает алгоритм встраивания, но не знает ключ. С учётом этого принципа получается невозможно скрыть наличие сообщения, внедрённого данным алгоритмом.
kilfoy
21.09.2025 23:13Стеганография это хорошо, но почему материал "Сложный" когда на деле он элементарный?
Lomakn Автор
21.09.2025 23:13Спасибо за фидбек! Вы правы, для специалиста в этой области LSB-анализ — это действительно базовый уровень. Мы поставили хаб "Сложный", ориентируясь на широкую IT-аудиторию, для которой сами концепции стегоанализа и статистических тестов могут быть в новинку. В будущем постараемся точнее подбирать уровень сложности.
ExternalWayfarer
21.09.2025 23:13Зачем атаковать Python?
Lomakn Автор
21.09.2025 23:13Хороший вопрос! Здесь "на Python" означает "реализованный с помощью языка Python", а не "направленный против него". То есть, мы пишем анализатор, используя Python как инструмент.
petropavel
21.09.2025 23:13Это был намёк, что название можно было придумать попонятнее, такое, которое потом не надо пояснять в комментариях, что оно означает. Ваш Капитан Очевидность.
Demmidovich
Спасибо за интересные статьи. Всегда в разных направления и все читается на одном дыхании. Сделайте больше статей о том, как реализовать встраивание в PDF и когда выйдет версия на Linux?
Lomakn Автор
Спасибо огромное за теплые слова! Очень рады, что нашлось время и на чтение, и на практику.
Про PDF — отличная идея, тема действительно глубокая и неочевидная. Мы как раз планируем посвятить ей отдельный большой материал, там есть о чем рассказать.
Версия под Linux у нас в планах, все упирается в наше железо на котом сделать порт. Виртуальные машины плохо работают с macOS на Intel