
Стеганография на Python | Скрытие файлов или данных в файлах изображений
Этот скрипт прячет файлы внутри изображений и сохраняет изменённое изображение на диск.
Максимальный размер файла, который можно спрятать внутри изображения, зависит от его размеров.
max_file_size = ( высота_изображения * ширина_изображения * 6 / 8 ) байт
Файл '100k words.txt' спрятан в 'original_image.png' под именем 'image_with_100k words.png'. Попробуйте найти разницу между этими двумя изображениями. Вы можете использовать этот модуль, чтобы извлечь '100k words.txt' из 'image_with_100k words.png'.
Оригинальное изображение

Изменённое изображение

В этом изображении спрятан файл на 100 тысяч слов (текстовый файл)!
В основе лежит простой принцип: если изменить LSB (наименьшие значащие биты) каждого пикселя, то изменение будет незначительным и незаметным для глаза.
Итак, этот модуль берёт 2 бита данных из скрываемого файла и заменяет последние 2 бита одного пикселя этими 2 битами, затем переходит к следующему пикселю. Максимальное изменение пикселя может составлять 4 единицы, а диапазон значений в PNG-изображении — (0,255), поэтому такое изменение будет незаметным.
В PNG-изображении каждый пиксель имеет 3 канала: красный, зелёный и синий. (Некоторые PNG-изображения имеют 1 или 4 канала, но эта программа преобразует их в 3 канала). Типичный пиксель в PNG-изображении выглядит так:
a_pixel = (17,32,11) # (КРАСНЫЙ, ЗЕЛЁНЫЙ, СИНИЙ)
Таким образом, мы можем сохранить 3 раза по 2 бита в одном пикселе, то есть 6 бит на пиксель. Отсюда вытекает верхний предел размера файла, который можно спрятать в изображении:
max_file_size = ( высота_изображения * ширина_изображения * 6 / 8 ) байт
Давайте разберёмся на примере. Скрываемые данные:
binary_data = 0b100111
Возьмём первые два бита и заменим ими красный канал нашего 'a_pixel'.
a_pixel = (0b10001, 0b100000, 0b1011) # двоичное представление значений a_pixel
# Изменим красный канал a_pixel
# 0b10001 -> 0b10010 (первые 2 бита — 10)
a_pixel = (0b10010, 0b100000, 0b1011) # изменённый пиксель
Снова возьмём 2 бита из binary_data и заменим последние 2 бита зелёного канала.
a_pixel = (0b10010, 0b100000, 0b1011)
# Изменим зелёный канал a_pixel
# 0b100000 -> 0b100001 (2 бита — 01)
a_pixel = (0b10010, 0b100001, 0b1011) # изменённый пиксель
Проделаем это ещё раз с синим каналом — и мы закончили.
a_pixel = (0b10010, 0b100001, 0b1011)
# Изменим синий канал a_pixel
# 0b1011 -> 0b1011 (2 бита — 11) ; Обратите внимание, что значение не изменилось
a_pixel = (0b10010, 0b100001, 0b1011) # пиксель не изменился в этот раз
Итак, мы спрятали наше 6-битное сообщение в пиксель. Посмотрим на изменения в пикселе:
a_pixel = (0b10001, 0b100000, 0b1011) = (17, 32, 11)
a_pixel_with_data = (0b10010, 0b100001, 0b1011) = (18, 33, 11)
Как видим, изменение даже на уровне пикселя незаметно. Модуль повторяет этот процесс, пока все наши данные не будут сохранены в изображении.

ПРИМЕЧАНИЕ: Шум на фото увеличивается, и если использовать любую программу для редактирования фото и сравнить его с оригиналом, то это изображение будет содержать гораздо больше шума, чем исходное.
Решение этой проблемы: никогда не загружайте/не делитесь оригинальным изображением в интернете.
MIT
Бесплатное ПО, чёрт возьми!