Прежде, чем начнется техническая часть статьи, предлагаю Вам немного развлечься и поиграть в старую игру «Найди N-цать отличий». Вот две картинки.
Первая
Вторая
Задание: найдите 10 различий в этих картинках :)
Собственно, даже если бы я дал Вам сравнить 2 картинки по-проще, то все-равно невооруженным глазом определить различия было бы практически невозможно. А различаются картинки тем, что в первой записан текст, который гласит: «Привет от webdelphi.ru». Причем записан этот текст кусками по всей картинке.
Спрашивается: а нафига мне это надо?
Я не дизайнер и не художник, так что картинки мне защищать авторским правом явно не светит. Просто решил немного разобраться для себя с вопросами по работе с нетипизированными файлами. Вот и получилась небольшая программка, которая пишет в jpg-файл произвольные данные таким образом, чтобы минимально видоизменять первоначальное изображение. Где эту программу сможете использовать Вы я не имею понятия, но буду очень признателен, если Вы поделитесь со мной способами её применения.
А теперь, собственно, разберемся как эта программа действует. Во-первых JPEG как и любой другой формат файлов не является беспорядочным набором байт, а имеет чёткую спецификацию. В соответствие с которой, файл имеет свой заголовок и, в зависимости от файла один или несколько JPG-потоков, причем в файле может присутствовать: 1 поток (сама картинка) 2 потока (картинка и миниатюра), в случае 3-х потоков уже следует задуматься над тем, чтобы «почистить» картинку, т.к. обычно третьим потоком идет бесполезная инфа, записаная фотошопом и пр.
Так вот, если не нарушать заголовка, то в картинку в принципе, можно записать сколь угодно дополнительных данных. Например, моя программа действует следующим образом:
- Открывается файл с картинкой и из него копируется заголовок в новый файл
- Следом за заголовком вписывается ключ для расшифровки вписываемого текста
- Далее, в зависимости от потребностей, в файл с определенными промежутками вписываются части текста.
- Новый файл сохраняется с новым именем.
Теперь более детально, а точнее на языке Delphi. Во-первых, ключ представлен следующим образом:
type
TCryptoKey = record
Step : byte;//шаг записи строки
length : word;//длина части строки для записи
TextLength: integer; //общая длина строки
TextParts: byte;//количество частей текста
end;
Зная ключ, можно без проблем восстановить все части текста и склеить их в первоначальный вид.
Сама процедура записи текста в картинку реализована следующим образом:
procedure PictCrypt(SourceFile, TargetFile: TFileName; CryptoKey:TCryptoKey; Text:String); var FS, FT: File; i,j,part:integer; buf2: byte; doWT: boolean; //флаг разрешения записи текста ch:string[1]; begin //получаем части текста для шифровки TextParts:=GetTextParts(Text, CryptoKey); doWT:=false; AssignFile(FS,SourceFile); AssignFile(FT, TargetFile); Reset(FS,1); Rewrite(FT,1); for I := 0 to FileSize(FS)-1 do begin if i=jpg_header+1 then begin //записываем в файл ключик BlockWrite(FT, CryptoKey,Sizeof(CryptoKey)); doWT:=true; part:=0; //элемент массива строк с которого начать писать end; if (doWT)and ((FilePos(FT) mod CryptoKey.Step)=0)and(part<=Length(TextParts)-1) then begin for j:=1 to Length(TextParts[part]) do begin ch:=(copy(TextParts[part],1,1)); delete(TextParts[part],1,1); BlockWrite(FT, ch ,sizeof(Ch)); end; part:=part+1; end; BlockRead(FS, buf2, Sizeof(buf2)); BlockWrite(FT, buf2,Sizeof(buf2)); end; CloseFile(FT); CloseFile(FS); end;
После того, как текст записан в картинку, его можно прочитать обратной функцией:
function PictDeCrypt(SourceFile: TFileName):string; var i,j,k:integer; buf: TCryptoKey; FS: File; pt: string[1]; begin AssignFile(FS,SourceFile); reset(FS,1); for I := 0 to FileSize(FS)-1 do begin Seek(FS,jpg_header+1); //смещаемся на позицию начала ключа BlockRead(FS, buf,Sizeof(buf)); CryptoKey:=buf; //восстанавливаем длины подстрок, записаных в файле LengthTextParts:=GetLengths(CryptoKey); end; for I:=FilePos(FS) to FileSize(FS)-1 do begin Seek(FS, i); if ((i mod CryptoKey.Step)=0) then begin if i>=jpg_header+sizeof(buf)+1 then begin for k:=0 to Length(LengthTextParts)-1 do begin for j:=1 to LengthTextParts[k] do begin BlockRead(FS, pt ,Sizeof(pt)); Result:=Result+pt; end; Seek(FS,FilePos(FS)+CryptoKey.Step-sizeof(pt)); end; break; end; end; end; CloseFile(FS); end;
При этом, вначале расшифровки по известному ключу восстанавливаются длины кусков текста, записанных в картинке. Восстановление длин оcуществляется в функции:
function GetLengths(CryptoKey: TCryptoKey):TLengthTextParts; var i:integer; begin //восстанавливаем длины частей текста записанных в картинку SetLength(Result, CryptoKey.TextParts); for I:=1 to CryptoKey.TextParts-1 do Result[i-1]:=CryptoKey.length; {в последнюю часть дописываем остаток} Result[Length(Result)-1]:=CryptoKey.length+(CryptoKey.TextLength-CryptoKey.length*CryptoKey.TextParts); end;
Сама программа выглядит следующим образом:
Все достаточно просто: грузим картинку, пишем текст для записи, определяем количество частей, на которые следует разбить текст, определяем ключ и шифруем.
В итоге, после записи текста программа проверит своб работу и выведет в закладке «Техническая часть» проверяемые данные:
Если результат проверки Вас устраивает (текст читается без потерь) значит все замечательно, иначе процедуру записи следует повторить, изменив данные, например количество частей текста или размер самой строки. По предварительным данным, в картинку можно записывать до 1500 символов текста, но при этом на картинке появляется «рябь».
Так как сейчас я предоставляю Вам только наброски программы или, выражаясь более официально, бетта-версию :), то надеюсь на Ваше участие в развитии программы, указание ошибок, недочетов и пр.
Кстати, если кто-то заинтересуется самим алгоритмом, то в принципе можно организовать запись любых данных, например массивов чисел и пр.
Помню где-то год назад попадалась мне на глаза статья с подобным алгоритмом шифрования текста, но хоть убейте не могу её отыскать в Интернете.
Есть md5, думаю ты и так об этом знаешь)
Кстати говоря, есть программы, которые позволяют внести (записать) внутрь картинки мультемедиа файл (например музыку).
Знаю, что есть, но самому-то написать интереснее :)
Сори. Но как сохранить зашифрованную картинку? У меня не получается.
В статье процедура procedure PictCrypt(SourceFile, TargetFile: TFileName; CryptoKey:TCryptoKey; Text:String);
как раз и записывает в TargetFille зашифрованную картинку.
Этот код можно использовать в виде зашиты логотипов или рекламы. Типо есть прога в ней используется файл картинки с логотипом фирмы, в файле зашифрован код. А пользователь не может взять и заменить картинку своей со своим логотипом так как в ней нет кода, и прога отказывается запускатся. :)
Логично :)
народ, берёте jpeg файл, добавляете в его конец архив (склеиваете файлы) и всё….переименовав jpg в zip — получаете архив, переименовав zip обратно в jpg — картинку…
такой вопрос а если наобород принимать зашифрованную картинку, как текст читать??????
Видимо надо выполнить операцию полностью противоположную операции шифрования.
А можно исходный код перекинуть(dpr+pas+dfm)?
А если картинку редактировали, после того как в неё записан текст, скажем в фотошопе ???
Как это отобразится на тексте ?
зачем эт..
Добрый день.
Подскажите, пожалуйста, в чем секрет:
при использовании программы на вашем файле все, который предложен в начале статьи все проходит идеально — картинки практически идентичны. Но при попытке использовать другие файлы того же формата JPG программа либо выдает ошибку jpeg #51 либо искажает файл достаточно значительно (от появления артефактов на изображении до превращения его в кашу из пикселей).
Написал программку перегоняющую jpg в bmp и обратно в jpg стандартными функциями Delphi, ошибка #51 больше не возникает, но проблема с искажением изображения осталась.
кто нибудь делал это на языке С++???
А проект можно целиком глянуть?
Вот я тоже пишу прогу, но пока не умею работать с изображениями. Как мне например изменить определенный байт изображения?
Что за part<= ?
В if (doWT)and ((FilePos(FT) mod CryptoKey.Step)=0)and(part<=Length(TextParts)-1) then
Можно весть код программы?
Вот весь бы код глянуть… :(
Здравствуйте. Я планирую таким способом шифровать пароли. Это логично?