Когда-то давно я написал свой
Сегодня мы попробуем копнуть чуть-чуть глубже и рассмотрим алгоритм LSB. Если вам интересно, милости прошу под кат. (Под катом трафик: около мегабайта.)
Прежде всего, необходимо сделать небольшое вступление. Всем известно, что предназначение криптографии – сделать невозможным чтение секретной информации. Разумеется, криптография имеет свои области применения, но есть и другой подход к защите данных. Можно не шифровать информацию, а сделать вид, что у нас её нет. Именно для этого и придумана стеганография. Википедия уверяет нас что, «стеганография (от греч. στεγανοσ — скрытый и греч. γραφω — пишу, буквально «тайнопись») — это наука о скрытой передаче информации путём сохранения в тайне самого факта передачи.
Конечно же, никто не запрещает совмещать криптографические и стеганографические методы. Более того, на практике так и делают, но наша задача разобраться с основами. Если внимательно изучить статью с Википедии, можно узнать, что в алгоритмах стеганографии фигурирует т.н. контейнер и сообщение. Контейнер — это любая информация, помогающая скрыть наше секретное сообщение.
В нашем случае контейнером будет выступать картинка в формате BMP. Для начала рассмотрим структуру этого файла. Файл условно можно разбить на 4 части: заголовок файла, заголовок изображения, палитру и само изображение. Для наших целей надо знать только то, что записано в заголовке.
Первые два байта заголовка – это сигнатура BM, далее в двойном слове записан размер файла в байтах, следующие 4 байта зарезервированы и должны содержать нули и, наконец, в ещё одном двойном слове записано смещение от начала файла, до собственно байтов изображения. В 24-битном bmp-файле каждый пиксел кодируются тремя байтами BGR.
Теперь мы знаем, как добраться до изображения, осталось понять, как туда можно записать необходимую нам информацию. Для этого нам и пригодится метод LSB. Суть метода заключается в следующем: мы заменяем младшие биты в байтах, отвечающих за кодирование цвета. Допустим, если очередной байт нашего секретного сообщения – 11001011, а байты в изображении –…11101100 01001110 01111100 0101100111…, то кодирование будет выглядеть так. Мы разобьём байт секретного сообщения на 4 двухбитовые части: 11, 00, 10, 11, и заменим полученными фрагментами младшие биты изображения: …11101111 01001100 01111110 0101100111…. Такая замена в общем случае не заметна человеческому глазу. Более того, многие старые устройства вывода, даже не смогут отобразить такие незначительные перемены.
Понятно, что можно менять не только 2 младших бита, но и любое их количество. Тут есть следующая закономерность: чем большее количество бит мы меняем, тем больший объём информации мы можем спрятать, и тем большие помехи в исходном изображении это вызовет. Для примера вот вам два изображения:
При всём желании я так и не сумел увидеть разницы между ними, а тем не менее во втором изображении с помощью описанного метода спрятана поэма Льюиса Кэролла «Охота на Снарка». Если вы дочитали до этого момента, то вам наверняка интересно узнать и о реализации. Она довольна проста, но сразу предупрежу, что сделано всё на 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