LSB стеганография

Когда-то давно я написал свой первый пост на хабре. И посвящён тот пост был весьма интересной проблеме, а именно стеганографии. Конечно, решение, предложенное в том старом топике, нельзя назвать стеганографией в истинном смысле этого слова. Это всего лишь игра с форматами файлов, но, тем не менее, довольно интересная игра.

Сегодня мы попробуем копнуть чуть-чуть глубже и рассмотрим алгоритм LSB. Если вам интересно, милости прошу под кат. (Под катом трафик: около мегабайта.)

Прежде всего, необходимо сделать небольшое вступление. Всем известно, что предназначение криптографии – сделать невозможным чтение секретной информации. Разумеется, криптография имеет свои области применения, но есть и другой подход к защите данных. Можно не шифровать информацию, а сделать вид, что у нас её нет. Именно для этого и придумана стеганография. Википедия уверяет нас что, «стеганография (от греч. στεγανοσ — скрытый и греч. γραφω — пишу, буквально «тайнопись») — это наука о скрытой передаче информации путём сохранения в тайне самого факта передачи.

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

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

Первые два байта заголовка – это сигнатура BM, далее в двойном слове записан размер файла в байтах, следующие 4 байта зарезервированы и должны содержать нули и, наконец, в ещё одном двойном слове записано смещение от начала файла, до собственно байтов изображения. В 24-битном bmp-файле каждый пиксел кодируются тремя байтами BGR.

Теперь мы знаем, как добраться до изображения, осталось понять, как туда можно записать необходимую нам информацию. Для этого нам и пригодится метод LSB. Суть метода заключается в следующем: мы заменяем младшие биты в байтах, отвечающих за кодирование цвета. Допустим, если очередной байт нашего секретного сообщения – 11001011, а байты в изображении –…11101100 01001110 01111100 0101100111…, то кодирование будет выглядеть так. Мы разобьём байт секретного сообщения на 4 двухбитовые части: 11, 00, 10, 11, и заменим полученными фрагментами младшие биты изображения: …11101111 01001100 01111110 0101100111…. Такая замена в общем случае не заметна человеческому глазу. Более того, многие старые устройства вывода, даже не смогут отобразить такие незначительные перемены.

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

LSB стеганография
LSB стеганография

При всём желании я так и не сумел увидеть разницы между ними, а тем не менее во втором изображении с помощью описанного метода спрятана поэма Льюиса Кэролла «Охота на Снарка». Если вы дочитали до этого момента, то вам наверняка интересно узнать и о реализации. Она довольна проста, но сразу предупрежу, что сделано всё на Delphi. Причин этому две: 1. Я считаю Delphi хорошим годным языком; 2. Эта программа родилась, в процессе подготовки курса по основам машинного зрения, а ребята, которым я этот курс читаю, пока ничего кроме Delphi не знают. Для тех, кто не знаком с синтаксисом надо пояснить одну вещь shl x – побитовый сдвиг влево на x, shr x – побитовый сдвиг вправо на x.

Считаем, что мы записываем в контейнер текст, хранящийся в строке и заменяем младшие два байта:
Код для записи:

for i:=1 to length(str) do
    begin
      l1:=byte(str[i]) shr 6;
      l2:=byte(str[i]) shl 2; l2:=l2 shr 6;
      l3:=byte(str[i]) shl 4; l3:=l3 shr 6;
      l4:=byte(str[i]) shl 6; l4:=l4 shr 6;
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l1;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l2;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l3;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l4;
      f.WriteBuffer(tmp,1);
 
    end;

код для считывания:

for i:=1 to MsgSize do
    begin
      f.ReadBuffer(tmp,1);
      l1:=tmp shl 6;
      f.ReadBuffer(tmp,1);
      l2:=tmp shl 6; l2:=l2 shr 2;
      f.ReadBuffer(tmp,1);
      l3:=tmp shl 6; l3:=l3 shr 4;
      f.ReadBuffer(tmp,1);
      l4:=tmp shl 6; l4:=l4 shr 6;
      str:=str+char(l1+l2+l3+l4);
    end;

Ну и для совсем ленивых – ссылка на программу и её исходный код.

Спасибо.

Источник: habr.com