Сокрытие сообщения в другом сообщении относится к области стеганографии. В этой статье мы будем прятать одну фотографию внутри другой. В результате при передаче такое фото будет выглядеть как обычный снимок, но по факту содержать два. Второй при этом можно будет извлечь при помощи внешнего инструмента.

Используя этот метод, можно прятать в своей художественной работе подпись или сохранять несколько снимков как один в целях экономии памяти на миниатюрных устройствах. Есть ещё один аналогичный подход, с помощью которого игровые ресурсы картриджей Pico-8 включаются в небольшой PNG самой игры*.

*Подробнее в статье «Steganography: decoding Pico-8 cartridges».

▍ Фотографии как матрицы


Чтобы иметь возможность управлять фотографиями и встраивать в них что-либо, сначала нужно разобраться с их возможным представлением и сохранением. Один из вариантов – это представить снимки в виде математических матриц, в которых каждая ячейка выражает пиксель, а её значение – цвет этого пикселя/ячейки.


Например, при такой матрице:


Мы получим такое фото:


Если фотографию и матрицу сопоставить, то можно заметить, чем ближе значение к 255, тем белее пиксель. При этом 0 означает чёрный, а всё между 0 и 255 оттенки серого.

▍ Фотографии как битовые матрицы


Итак, снимки в виде матриц мы представили. Теперь давайте немного поботаним и выразим значение каждой ячейки не в десятичном, а в двоичном виде (обещаю, дальше это пригодится).

Та же фотография с помощью этой матрицы теперь будет выражаться так:


Далее нам нужно выяснить, что произойдёт с фотографией, если изменить младший бит. Для этого я их всё реверсирую и посмотрю, как в итоге будут выглядеть матрица и новое фото:



Можете разглядеть какие-либо изменения? Предполагаю, что нет. Полученный вариант определённо выглядит так же, как оригинал (если только вы не обладатель сверхзрения), поскольку мы изменили лишь младшие биты, имеющие наименьшую значимость. С помощью этого элементарного приёма можно встраивать фотографии одну в другую, не вызывая подозрений в отношении оригинального снимка.

▍ Встраивание фото


Для встраивания фотографии нужно заменить каждый младший бит основного снимка на каждый бит встраиваемого, то есть размер фото, которое мы прячем, должен быть меньше основного.


Но насколько меньше? В видимой фотографии размером n * m есть nm/8 младших бит, то есть внутри неё можно разместить фото размером √nm/8 * √nm/8. Например, при размере видимого снимка 1000 * 1000px мы получаем:


Это означает, что можно скрыть в нём фотографию размером 353 * 353px.

▍ Цветные фото


В цветных снимках каждый пиксель представлен тремя матрицами: красной, зелёной и синей. Вместо этого можно рассматривать такие снимки как одну матрицу с кортежами из трёх значений в диапазоне от 0 до 255.


В этом случае наш подход по встраиванию фотографии также сработает – нужно лишь заменить все младшие биты в каждой матрице каждой ячейки.

▍ PNG, JPEG и сжатие


Вы наверняка знаете, что некоторые форматы фото (например, JPEG) сильно сжимают исходное изображение, используя различные математические техники (например, вейвлет Хаара). Что же происходит со встраиваемым фото в таком случае? Оно с наибольшей вероятностью уничтожается, и при попытке извлечения мы увидим лишь шум. Но есть и другие форматы, например, PNG, которые сохраняют фотографию. Так что можно использовать рассмотренный трюк с ними.

▍ Реализация


Я реализовал этот метод давно, когда ещё учился на бакалавра, и соответствующий код лежит здесь. Это десктопное приложение, в котором для работы с битами фотографий используется Emgu CV.

Комментарии (28)


  1. Aleksandr-JS-Developer
    06.05.2022 12:50
    +3

    Всё гениальное просто


    1. unC0Rr
      06.05.2022 14:16
      +9

      Это простейший метод стеганографии, не достигающий всех целей, стоящих перед стеганографией: не обеспечивается невозможность определить наличие встроенной информации и нечитаемость информации без знания секретного ключа.


      1. 1dNDN
        06.05.2022 15:25
        +1

        А разве нельзя зашифровать картинку Б и результат уже записывать в младшие биты картинки А? Нельзя же достоверно отличить зашифрованную информацию от просто случайного набора битов


        1. unC0Rr
          06.05.2022 17:02
          +1

          Можно, но шум в младших битах сам по себе выдаёт наличие скрытой информации, поскольку в нормальной картинке младшие биты не похожи на шум.


          1. tmnhy
            06.05.2022 17:18

            Как определить, что младшие биты "шумят"?


            1. unC0Rr
              06.05.2022 17:30

              По высокой энтропии и отсутствию корреляции с более старшими битами.


              1. Sheti
                06.05.2022 17:40
                +2

                Я в школе проект делал именно с этим методом замены информации. Реально различимые на глаз искажения появляются при замене половины (4 бит) информации о цвете. А учитывая, что нормальное шифрование делает выходной файл очень похожий на случайный шум, то сомнительно, что можно будет сказать, что там есть дополнительная информация, а не шум матрицы фотоаппарата.


                1. unC0Rr
                  07.05.2022 14:10

                  Дело, естественно, не в различимости на глаз. В стеганографии как и в криптографии действует принцип того, что атакующий знает применённый алгоритм. Если мы говорим о шумах матрицы или о специальном зашумлении, применённом фотоаппаратом, то нужно сразу закладываться на то, что алгоритм неприменим к другим видам изображений, как то рендерам, рисункам и т.п.


          1. vanxant
            06.05.2022 23:19
            +2

            Ерунду пишете, в младших битах как раз шум. Более того, если говорить про фоточки, то приличные аппараты и редакторы специально добавляют в младшие биты белый или нормальный шум, так оно выглядит естественнее после шумоподавления и шарпенов.


  1. saipr
    06.05.2022 13:09
    -4

    PNG, JPEG и сжатие

    А в SVG-изображения не пытились прятать?


  1. vanxant
    06.05.2022 14:10

    Это где это вейвлет Хаара используется?

    Он простой и понятный, поэтому его пихают во все учебники, как нормальное распределение в теорвере. Но на этом его преимущества заканчиваются.


  1. Static_electro
    06.05.2022 14:48
    +8

    Но насколько меньше? В видимой фотографии размером n * m есть nm/8 младших бит, то есть внутри неё можно разместить фото размером √nm/8 * √nm/8

    эээ... Разве не проще сказать, что раз в каждом байте прячем единственный бит -> в восемь раз меньше?


  1. anonymous
    00.00.0000 00:00


  1. papani
    06.05.2022 15:15
    +1

    не знаю, может кому пригодится...

    Сохранять данные (любые) в JPEG можно еще так:
    Разбить данные на части и сохранять каждую из этих частей в Application-блоках. Например, выбрать себе App4-блок и писать таким образом:
    $FF$E4 (маркер начала App4-блока), затем длину блока данных (2 байта если не ошибась), затем собственный идентификатор, например "MYPICTURE" (чтобы никто не догадался), ну а потом кусок данных. В программе-читалке парсим все это дело, ищем App4-блоки, убеждаемся, что это наши блоки (см. идентификатор) на и склеиваем данные обратно.

    Формат, естественно, можно усложнить. Блоки эти видны, если открыть в HEX-редакторе, поэтому не знаю, можно ли это назвать "спрятать фото в другом".


    1. berez
      06.05.2022 15:37
      +2

      Ну формально-то да, стеганография получается — спрятали же. С другой стороны, наличие в жопеге левых блоков легко детектируется автоматически.
      То есть спрятать картинку от жены получится легко, а вот от кровавого кейджиби — нет. Наоборот, наличие в графическом файле большого количества неграфической информации моментально привлечет внимание.

      А так идею можно развить — например, прятать информацию в дополнительных потоках MKV. А можно еще проще (если прячем от жены) — переименовать jpeg в dll или exe и хранить где-нибудь в Program Files. ????


      1. papani
        06.05.2022 15:58
        +1

        А жена читает хабр, видит это сообщение, открывает такую переименованную dll, находит $FF$D8 в начале файла и у нее возникают вопросики...=)


        1. YDR
          06.05.2022 16:52
          +6

          Жены айтишников сейчас такие,да: сразу замечают лишнюю dll-ку в system32.


          1. khajiit
            07.05.2022 18:50
            +6

            А нечего прятать лишние dll'ки в system32 на freebsd )


  1. commanderxo
    06.05.2022 15:51
    +8

    При манипуляции битов новая картинка получится другой, хотя и очень похожей на взгляд. Помню лет десять назад попадалась стеганография в каком-то текстовом формате (SVG?), когда вместо скажем «42» в некоторых местах писалось «042». Распаковщик игнорирует лидирующие нули в десятичной записи чисел и выдаёт абсолютно идентичную картинку.


  1. hard2018
    06.05.2022 19:35
    +2

    для работы с битами фотографий используется Emgu CV.

    Это да, современным программистам для битовых манипуляций нужны библиотеки.

    Хотяя... это лучше, чем изобретать собственную, ни с чем не совместимую штуку


  1. aakhamef
    06.05.2022 21:38
    +3

    RarJpeg. Почему никто не вспомнил его?


  1. binary_animal
    07.05.2022 18:20
    +1

    Вы наверняка знаете, что некоторые форматы фото (например, JPEG) сильно сжимают исходное изображение, используя различные математические техники (например, вейвлет Хаара). Что же происходит со встраиваемым фото в таком случае? Оно с наибольшей вероятностью уничтожается, и при попытке извлечения мы увидим лишь шум.

    А вот это как раз самое интересное - попробовать порассуждать над тем, как можно было бы реализовать хранение скрытого изображения так, чтобы при lossy-сжатии оно испытывало уровень деградации детализации линейно пропорциональный уровню деградации изображения-контейнера и при этом умудрялось бы оставаться скрытым для глаза наблюдателя.


    Хочется высказать несколько очевидных мыслей на эту тему:

    • во-первых яркость содержит больше информации, чем цвет (в терминах человеческого восприятия). Цвет при сжатии всегда деградирует быстрее, поэтому логично предположить, что скрытое изображение, как минимум его основу, по которой оно будет восстанавливаться в информации о цвете лучше не хранить.

    • во-вторых чем групнее геометрические детали в изображении, тем более они устойчивы к деградации, поэтому хранить именно в них дополнительную информацию будет наиболее логично с т.з. надежности.

    • в-третьих чем больше плотность детализации в определенной области изображения, тем больше туда можно запихнуть скрытой информации.

    Исходя из этих нехитрых наблюдений, можно попробовать сделать что-то такое: выбрать радиус окрестности, построить по нему карту спектральной плотности изображения, не забывая отрезать высокие частоты. Дальше закодировать скрытое изображение в виде деформации спектра заранее известным нам образом (здесь может быть большое поле для размылений над механикой реализации такого способа). Понятно, что большого относительного объема "памяти" ожидать от такого способа хранения не стоит, но зато он будет обладать усточивостью к lossy-сжатию, что зачастую значительно приоритетнее.

    Второй способ - сделать все то же самое определенным образом натренированной сверточной нейросетью и внутренняя логика подсказывает, что что-то подобное уже наверняка давно реализовано.


    1. unC0Rr
      07.05.2022 21:24

      В случае JPEG данные скрываются аналогичным образом в малозначимых коэффициентах матриц, получаемых после дискретного косинусного преобразования. В результате подмены бита (или нескольких), изображение искажается на всём квадрате 8x8, но на глаз всё равно не заметно.


    1. vanxant
      07.05.2022 23:07
      +3

      Да забудьте вы про нейросетки, это тупое хайпожорство.

      На самом деле для jpeg-a и подобных форматов есть 3 уровня стеганографии:

      1. Передать сообщение от Алисы к Бобу внутри картинки так, чтобы оно не обнаруживалось "гражданским" софтом;

      2. Передать сообщение через облачную инфраструктуру, условно Whatsapp, которая в процессе передачи ресайзит и пережимает изображения вообще в другой формат (условно webp)

      3. Передать сообщение от Гитлера Паулюсу так, чтобы его не перехватил Моссад.

      Решения, соответственно, зависят:

      1. Можно создать свой кастомный тег, или не создавать, а просто выделить под него область в файле. Весь софт и то и другое проигнорирует, ну есть байты и есть. Это by design так. Точно так же браузеры игнорируют неизвестные теги или конструкции CSS.

      2. Здесь придётся покурить косинус-преобразование и его конкретные реализации. Соотношение открытых и секретных данных 1:8 конечно не получится, а вот что-то типа 1:1000 легко. Никаких нейросеток, это суровый, но точный матан.

      3. Если вас пасёт Моссад, у меня для вас плохие новости: вам, вероятно, не до стеганографии.


      1. binary_animal
        08.05.2022 04:52
        +1

        Здесь я, следуя вашей терминологии, подразумеваю скорей всего второй уровень, но в чуть более широком смысле слова. Например, условные инопланетяне, прилетели на условную планету с пещерой, на стенках которой обнаружили визуальные неоднородности, котоые носят явные признаки семиотической природы и допустим сфоткали их на условную камеру. Полученной информации им как-то должно хватить, чтобы обнаружить в анализируемом изображении дополнительную/скрытую упорядоченность, проявляющую родственные признаки.

        Цель использования нейросети в таком широком контексте выглядит чуть более прозрачно: она дает возможность поднять уровень абстракции анализа до такого, при котором не будет необходимости опираться на столь узкоспециализированную терминологическую конкретику как косинус-преобразование и суровый но точный матан. Ну примерно как нет в ней необходимости, чтобы уловить скрытый смысл в тексте этого комментария.


      1. Rumidu
        08.05.2022 23:23

        "Передать сообщение от Гитлера Паулюсу так, чтобы его не перехватил Моссад." т.е. сделать невыполнимое (с учётом того, что Моссад создана лет через шесть после смерти Гитлера.


        1. vanxant
          09.05.2022 02:23

          Похоже, мне надо в каждом комменте указывать тег <ирония> (


  1. Kyushu
    07.05.2022 19:06
    +1

    Вы наверняка знаете, что некоторые форматы фото (например, JPEG) сильно сжимают исходное изображение. ... Но есть и другие форматы, например, PNG, которые сохраняют фотографию.

    Вообще говоря, принято говорить о сжатии с потерями и сжатии без потерь.