Привет, Хабр! Представьте себе сценарий: вы — аналитик в области цифровой криминалистики. Вам на стол попадает, казалось бы, совершенно безобидное фото из отпуска, изъятое у объекта расследования. На первый взгляд — обычный пейзаж. Анализ пикселей не даёт ничего — нет никаких признаков классической стеганографии. Но интуиция подсказывает: что-то здесь не так.
Что, если секрет спрятан не в самом изображении, а в его «паспорте» — служебных метаданных? Именно здесь, в полях 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. Она полностью бесплатная и создана для того, чтобы каждый мог наглядно изучить методы обнаружения данных.
Официальный сайт проекта: chalab.ru
Заключение
Стегоанализ EXIF — это увлекательный процесс, похожий на расследование. Он наглядно показывает, что для сокрытия данных не всегда нужно изменять пиксели самого изображения. Эффективный поиск в метаданных — это комбинация методов:
Начинаем с простого: Визуально осматриваем теги на предмет очевидных аномалий.
Углубляемся в математику: Проверяем энтропию содержимого тегов. Высокие значения — наш главный индикатор.
Ищем отпечатки пальцев: Ищем известные сигнатуры и заголовки файлов.
Заглядываем в тёмные углы: Не забываем про
MakerNote, хоть это и сложная задача.
Создание автоматизированного инструмента, который бы проходил по всем этим шагам и выдавал отчёт о «подозрительности» файла — отличная задача для программиста, интересующегося информационной безопасностью. Надеюсь, это руководство станет для вас хорошей отправной точкой в мире охоты на цифровых призраков!
Следите за нашими экспериментами и обновлениями проекта на нашем официальном сайте и Telegram-канале!
