Привет, Хабр! Представьте себе сценарий: вы — аналитик в области цифровой криминалистики. Вам на стол попадает, казалось бы, совершенно безобидное фото из отпуска, изъятое у объекта расследования. На первый взгляд — обычный пейзаж. Анализ пикселей не даёт ничего — нет никаких признаков классической стеганографии. Но интуиция подсказывает: что-то здесь не так.
Что, если секрет спрятан не в самом изображении, а в его «паспорте» — служебных метаданных? Именно здесь, в полях EXIF, и начинается наша охота на цифровых призраков. Сегодня мы погрузимся в мир стегоанализа и научимся вскрывать тайны, которые могут хранить в себе метаданные фотографий.

Где обитает EXIF?
Хотя мы фокусируемся на JPEG, важно понимать, что EXIF — это не эксклюзивная особенность этого формата. Этот стандарт метаданных можно встретить и в других типах файлов, что делает навыки его анализа еще более ценными. EXIF-данные поддерживают:
Изображения:
JPEG
,TIFF
,HEIC/HEIF
(формат Apple),WebP
иPNG
(хотя в PNG для этого есть и свои, более нативные форматы метаданных).RAW-форматы:
CR2
(Canon),NEF
(Nikon),ARW
(Sony) и другие форматы несжатых изображений с камер.Аудио:
WAV
файлы также могут содержать EXIF-информацию, например, для описания устройства записи.
Теперь, когда мы знаем, где искать, давайте разберемся, что искать.

Что мы ищем?
Стегоанализ — это не поиск конкретного текста. Это поиск аномалий. Наша задача — найти то, что выделяется на фоне обычных, стандартных метаданных, которые создаёт камера или фоторедактор. Существует несколько подходов, от самых простых до более изощрённых. Давайте рассмотрим их по порядку с примерами на Python.
Для работы с EXIF нам понадобится библиотека piexif
.
import piexif
import numpy as np
def get_exif_dict(filepath: str):
"""Извлекает EXIF-данные из файла в виде словаря."""
try:
exif_dict = piexif.load(filepath)
return exif_dict
except Exception:
return None
def get_tag_content(exif_dict, ifd_name, tag_name):
"""Получает содержимое конкретного тега."""
if not exif_dict or ifd_name not in exif_dict:
return None
# Находим код тега по его имени
tag_code = piexif.TAGS[ifd_name][tag_name]["code"]
if tag_code in exif_dict[ifd_name]:
return exif_dict[ifd_name][tag_code]
return None
Метод 1: Визуальный осмотр (The Naive Approach)
Самый первый и очевидный шаг — просто посмотреть на все EXIF-теги и их содержимое. Что должно нас насторожить?
Аномально длинные строки: В поле
Artist
(Автор) илиModel
(Модель камеры) вряд ли будет находиться текст объёмом в несколько килобайт.Бессмысленный набор символов: Если в поле
Copyright
вы видите что-то вродеLOREMIPSUMDOLORSITAMET...
, это нормально. А вот если тамaG9sYSBtdW5kbw==
, это уже похоже на Base64.Нестандартные теги: EXIF поддерживает пользовательские теги. Их наличие — не всегда признак стеганографии, но повод присмотреться.
Давайте посмотрим, как это выглядит. Допустим, у нас есть изображение stego_image.jpg
, в UserComment
которого спрятаны данные.
# stego_image.jpg - изображение с внедренными данными
exif_data = get_exif_dict("stego_image.jpg")
if exif_data:
user_comment = get_tag_content(exif_data, "Exif", "UserComment")
if user_comment:
# Первые 8 байт в этом теге обычно указывают кодировку, их можно пропустить
print(user_comment[8:])
# Вывод: b'CHLB\x01\x80\x04\x95\xd5\x02\x00\x00...'
Даже без глубокого анализа мы видим странные байты, начинающиеся с CHLB
. Это наша "кроличья нора".
Метод 2: Статистический анализ (Энтропия)
Обычный текст (например, "My Photo") имеет низкую энтропию — символы в нём предсказуемы. А вот зашифрованные или сжатые данные, наоборот, стремятся к максимальной энтропии, так как каждый байт в них выглядит случайным. Это наш главный козырь.
Мы можем посчитать энтропию для содержимого каждого EXIF-тега. Если она аномально высокая (близкая к 8.0 для байтовых данных), это — серьёзный "красный флаг".
Вот функция для расчёта энтропии Шеннона, похожая на ту, что используется в analysis_
utils.py
нашего проекта:
def calculate_entropy(data: bytes) -> float:
"""Расчет энтропии Шеннона для байтовой строки."""
if not data:
return 0.0
# Считаем количество каждого байта
byte_counts = np.bincount(np.frombuffer(data, dtype=np.uint8), minlength=256)
# Убираем нулевые значения, чтобы избежать ошибки логарифмирования
counts = byte_counts[byte_counts > 0]
# Рассчитываем вероятности
probabilities = counts / len(data)
# Считаем энтропию
entropy = -np.sum(probabilities * np.log2(probabilities))
return float(entropy)
Теперь применим это на практике:
# Допустим, мы извлекли два тега
normal_comment = b"This is a normal comment."
# Это base64 от "Hello, Habr!" с небольшим "шумом"
suspicious_comment = b"SGVsbG8sIEhhYnIhSGVsbG8sIEhhYnIhSGVsbG8sIEhhYnIh"
print(f"Энтропия обычного комментария: {calculate_entropy(normal_comment):.4f}")
# Вывод: Энтропия обычного комментария: 4.1454
print(f"Энтропия подозрительного комментария: {calculate_entropy(suspicious_comment):.4f}")
# Вывод: Энтропия подозрительного комментария: 5.6582
Разница очевидна. Если бы suspicious_comment
был зашифрован, его энтропия была бы ещё выше (ближе к 7.5-8.0).
Метод 3: Поиск сигнатур
Многие стеганографические инструменты оставляют "подписи" или "магические числа", чтобы потом найти свои же данные. Например, в нашем проекте Chameleon мы используем сигнатуру CHLB
() или CHAMELEON
().
Поиск таких сигнатур — очень эффективный способ обнаружения. Кроме прямых сигнатур, можно искать и косвенные признаки:
Заголовки файлов: Наличие в EXIF-теге байт
PK
(заголовок ZIP-архива) или7z
— явный признак сокрытия файла.Base64-подобные структуры: Длинные строки из символов
[A-Za-z0-9+/]
с символами=
в конце.
def find_signatures(filepath: str):
"""Ищет известные сигнатуры в EXIF-тегах."""
exif_data = get_exif_dict(filepath)
if not exif_data:
print("EXIF не найден.")
return
known_signatures = {
b'CHLB': "Chameleon Stego Signature",
b'PK\x03\x04': "ZIP Archive Header"
}
for ifd in exif_data:
if ifd == "thumbnail":
continue
for tag, content in exif_data[ifd].items():
if isinstance(content, bytes):
for sig, name in known_signatures.items():
if sig in content:
tag_name = piexif.TAGS[ifd].get(tag, {"name": "Unknown"})["name"]
print(f"Найдена сигнатура! '{name}' в теге '{tag_name}'")
# find_signatures("stego_image.jpg")
# Вывод: Найдена сигнатура! 'Chameleon Stego Signature' в теге 'UserComment'
Метод 4: Анализ "чёрного ящика" — MakerNote
Тег MakerNote
— это мечта для стеганографа и головная боль для аналитика. Каждый производитель камер (Canon, Nikon, Sony) записывает туда служебную информацию в своём собственном, часто бинарном и недокументированном формате.
-
Почему это идеальное место для сокрытия?
Большой размер: Тег может содержать десятки килобайт данных.
Игнорируется большинством программ: Стандартные просмотрщики его не показывают.
Высокая энтропия по умолчанию: Данные в нём уже бинарные, и добавление зашифрованного блока не вызовет подозрений на основе одной лишь энтропии.
Как его анализировать? Прямой анализ сложен. Самый надёжный метод — сравнительный. Если у вас есть два фото, сделанных одной и той же камерой с одинаковыми настройками, но в одном из них вы подозреваете стеганограмму, можно сравнить их теги
MakerNote
. Значительное расхождение в размере — веский повод для беспокойства.
Практическое применение: ChameleonLab
Все описанные выше подходы — не просто теория. Чтение EXIF-данных, статистический анализ и поиск сигнатур реализованы в нашем проекте ChameleonLab. Это кросс-платформенная образовательная лаборатория для стеганографии, которая работает на Windows и macOS. Она полностью бесплатная и создана для того, чтобы каждый мог наглядно изучить методы обнаружения данных.
Официальный сайт проекта: https://chalab.ru/
Заключение
Стегоанализ EXIF — это увлекательный процесс, похожий на расследование. Он наглядно показывает, что для сокрытия данных не всегда нужно изменять пиксели самого изображения. Эффективный поиск в метаданных — это комбинация методов:
Начинаем с простого: Визуально осматриваем теги на предмет очевидных аномалий.
Углубляемся в математику: Проверяем энтропию содержимого тегов. Высокие значения — наш главный индикатор.
Ищем отпечатки пальцев: Ищем известные сигнатуры и заголовки файлов.
Заглядываем в тёмные углы: Не забываем про
MakerNote
, хоть это и сложная задача.
Создание автоматизированного инструмента, который бы проходил по всем этим шагам и выдавал отчёт о "подозрительности" файла — отличная задача для программиста, интересующегося информационной безопасностью. Надеюсь, это руководство станет для вас хорошей отправной точкой в мире охоты на цифровых призраков!
Следите за нашими экспериментами и обновлениями проекта на нашем официальном сайте и Telegram-канале!
Комментарии (0)
berez
26.09.2025 12:37MakerNote в большинстве случаев - это как бы "exif внутри exif", только со своими вендор-специфичными тегами: https://exiv2.org/tags-nikon.html
Поэтому проверить валидность MakerNotes более-менее реально. Если вместо IFD там лежит некий бинарный блоб - это сигнальчик, что что-то там нехорошо.
А вот в thumbnail можно запихать довольно крупный кусок бинарных данных. Там, конечно, в большинстве случаев лежит превьюшка в виде мелкого jpeg, но вместо нее можно положить и скрытые данные (до 64 кб).
qw1
Наивные кошки-мышки. Я вот добавил в конец jpeg-файла, перед маркером EOI кучу рандомных данных, и всё выглядит хорошо: структура файла осталась корректной, никаких лишних тегов нет. И обнаружить это можно лишь написав свой декодер, который увидит, что после декодирования всей картинки остались "лишние" байты в блоке SOS.
Аналогично можно вставить что угодно в конец Huffman-таблиц, и без полного разбора этих таблиц "криминалист" ничего не найдёт.