Здрасте форумчане.вот пока выдалось свободное время, и перед тем когда мне наступит конец в связи с сессией, решил попробовать поделиться информацией по интересной для меня теме-стеганографии.Материалец не претендует на какую то мега-статью, скорее личные наблюдения или что.
тема навеяна этим
/thread375855-miniduke.html
и является продолжением этой
http://bydood.blogspot.com/2013/05/hell-yeah.html
т.к. доселе я не мог вразуметь как же так в мультимедиа заседают другие файлы.оказалось можно.хотя методика создана не для этого,но очевидно что тут применилась.
В общем-то план таков:
1)немного истории,что являет собой стеганография, етц.
2)виды стеганографии( то что применим мы)
3)анализ застенографированных файлов.
Писать будем на делфи, более-менее вменяемые куски кода нашел на делфи и С++
ТЕОРИЯ
Итак стеганография- некий вид науки о сокрытой передаче информации, т.е. утаивании самого факта присутствия сообщения.Если криптография - это шифрование информации, то стеганография скрывает сам факт.обычно в данное время застеганографированная информация выглядит как нечто иное - к примеру файлы мультимедиа (изображения,звук,видео, даже исполняемые файлы).
некоторые примеры стеганографии в допотопном мире можно прочесть на вики (довольно интересно я вам скажу )
современная стеганография подразделяется на несколько таких вот видов:
1)
компьютерная стеганография: как правило сокрытие в зарезервированных полях файлов,пустых местах дисков,флешек етц, особенности файловых систем.
(как пример- формат jpg имеет т.н. потоки данных (выделяет потоки для информации) т.е. можно создать отдельный поток в который записать сообщение- однако недостатки состоят в том что jpeg использует сжатие, во вторых объем файла увеличивается,в третьих производя манипуляции с изображением-можно угробить информацию)
2)
современный наиболее распространенный вариант - внедрение информации в объекты вроде изображений,аудио,видео… Вызывает некоторые искажения в файлах-контейнерах, однако , как правило, не подвластно человеческому взгляду.
Существует несколько алгоритмов, но самое простое и распростаненное для изображений - изменение последнего бита.
суть методики в замене последнего бита файла-контейнера на биты нашей информации.
рассмотрим на примере битмапа - у него там на одну точку приходится 3 байта- red , green, blue. и согласитесь что если будут точки допустим 135,58,44 и точки 136,58,45 то взгляд среднего (да любого) человека не отличит визуально.
Попробуем написать разного рода код для сокрытия информации в: секторе, битмапе,тексте.
1)СЕКТОР.
заранее определим нужные нам сектора, это могут быть также сектора заполненные подставными файлами, строки могут быть зашифрованы каким то не сложным алгоритмом.Подробно останавливаться не буду, т.к. тут все просто:
открываем диск:
Код:
CreateFile ('\\.\PhysicalDrive0',GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
(для примера системный диск, ну вообще указываем тот что нам нужен).
идем на нужную позицию:
Код:
SetFilePointer(f,10*512,nil,FILE_BEGIN);
11 сектор в примере.
И запись в общем то как в обычный файл:
Код:
WriteFile(f,somedata,sizeof(somedata),nw,nil);
2)И вот самое интересное - сохраняем в битмап.
Тут напишем уже полноправную программку)
Рассмотрим запись в «лоб» и замену битов.
Заголовок битмапа=14 байтам + ширина*высоту - после этого смещения мы можем писать все что угодно,что бы при этом битмап открывался. но нужно создавать некий разброс если сообщение большое,потому что получается вот такой вот прикол:
видим что изображение искажается. понятно что писать в ряд нельзя, что бы скрыть придется писать по 1-2 байта с определяющим знаком (для считывания) и через несколько байт\десятков байт, так что здесь описывать я подробно не буду.
Заметил,что при дописывании в файле - рисунок не искажается (искажения идут снизу-вверх)
что в целом может подойти как один из наипростейших методов.
напишем тогда код
алгоритм следующий:
1)открыть битмап
2)считать из хидера 2 параметр-размер битмапа (то есть получим нужное смещение)
3)сместиться на полученное смещение
4)записать строку с определенным идентификатором
по считыванию назад
1)открыть битмап
2)считать из хидера 2 параметр
3)читать строку от начала конца (простите за тавтологию) до идентификатора
4)выдрать строку.
имеем след. код:
Код:
type bmpheader=packed record
bftype:word;
bfsize:dword;
bfres1:word;
bfres2:word;
bfoffbits:dword;
end;
var Form1:Tform1;
…………………………………..
var size:bmpheader;
f:hfile;
tmp:dword;
str:pchar;
begin
str:='fuck you bitch sosi kirpich :D';
F := CreateFile(PChar('C:\input.bmp'), GENERIC_READ or GENERIC_WRITE , FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
ReadFile(F, size, sizeof(size), tmp, nil);
setfilepointer(f,size.bfSize,nil,FILE_BEGIN);
WriteFile(f,str^,length(str),tmp,nil);
CloseHandle(F);
вот такой не большой код. для чтения все точно также.
Следующий метод-
замена младших бит.
алгоритм его работы таков:
есть изображение 24 битов. 1 пиксель кодируется 3 каналами (=3 байтам) RGB
меняя наименее значащий бит мы инкрементируем значение байта на 1.
Сама методика считается простой, т.к. должны использоваться форматы данных без потерь.Как недостаток-любые манипуляции с файлом контейнером не допустимы.Как достоинства-простота, кол-во данных для внедрения.
Естественно что обнаружить сокрытое сообщение закодированное таким методом можно только с помощью стегоанализа. если провести небольшие расчеты то можно подсчитать что каждая точка кодируется числом в диапазоне от 0 до 255
т.е. 3 байта на пиксел что составит 256^3 числа цветов. человеческий глаз различает около 4000 цветов\оттенков, так же человеческое зрение воспринимает лучше вариации зеленого цвета , по этому замену лучше проводить в синей или красной компоненте.
окей и сейчас я начну писать как в этих симпотных книжках по делфи =^^=
бросьте на форму: edit, label, 3 кнопочки, изображение, openpicturedialog, savepicturedialog что бы получилось примерно так:
у image cв-ва
proportional и
stretch ставим в
true
в диалогах убираем все кроме битмапки.
и сразу пишем обработчики событий кнопочег
Код:
// загружаем картинку
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenPictureDialog1.Execute then begin
Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
end;
end;
//застеганографируем сообщение
procedure TForm1.Button2Click(Sender: TObject);
begin
steanographthemall(Image1.Picture.Bitmap, sign+(edit1.Text));
if SavePictureDialog1.Execute then
Image1.Picture.SaveToFile(SavePictureDialog1.FileName);
end;
// извлекаем сообщение
procedure TForm1.Button3Click(Sender: TObject);
var
s: string;
begin
if openpicturedialog1.Execute then
image1.Picture.LoadFromFile(openpicturedialog1.FileName);
showmeyourself(Image1.Picture.Bitmap, s);
showmessage(s);
end;
ну и самый замес- процедуры кодировки\раскодировки, но сначала пишем в константах:
Код:
const
sign:string=’dood’;
по ней будем идентифицировать информацию в картинке
процедура стеганографирования (нужные комментарии непосредственно в коде)
Код:
procedure steanographthemall (BMP: TBitmap; Message: string);
var
i,j,psz,fromhead : integer;
rowl : pByteArray;
PB : pByte;
incc: PChar;
bcode,Nbcode,intc,bc: byte;
begin
psz:=3; // разрядность изображения, не проверял сколько может поддерживаться, только делал на 24 битном, попробуйте поиграть
if not Assigned(BMP) then exit;
Message:=sign+Message;
fromhead:=Length(Message)+2;
with BMP do begin
incc:=@Message[1]; // указатель на начало сообщения
BC:=0;
for i:=0 to Height-1 do begin
rowl:=ScanLine[i]; // указатель на строку пикселей
pb:=@rowl[0]; // смещаем 1 бит на пиксель (можно и больше для большей вместимости шифруемой информации, однако это даст заметно сильные искажения видимые не вооруженным глазом
for j:=0 to Width-1 do begin
intc:=Ord(incc^);
asm
mov al,0feh // сбрасываем нулевой бит
mov bcode,al
xor al,al
mov al,bcode
not al
mov nbcode,al //сдвигаем что бы записывался младший бит
mov cl,8
ror Intc,cl
mov cl,BC // проход по битам символа
ror IntC,cl
end;
PB^:=(PB^ and BCode)
or (IntC and NBCode); // 7 бит не изменяем, а изменяем только 0 или 1 в зависимости от того какой бит установлен в данный момент и какой бит в строке
if (BC = 7) then begin // прошел весь символ?
inc(incC); // след. символ
dec(fromhead); // декрементируем число оставшихся
BC:=0; // очищаем счетчик битов
end
else
inc(BC);
if fromhead = 0 then exit;
inc(PB, PSz); // перемещаем указатель буфера битмапа.
end;
end;
end;
end;
для битовых операций применился встроенный ассемблер, как бы для студенческой поделки оно и не требуется, однако под напором окружающей среды мозг начинает сохнуть и мне захотелось его пое*ать.)
процедура извлечения
Код:
procedure showmeyourself(BMP: TBitmap; var msg: string);
var
i,j,psz: integer;
c: Byte;
pb : pByte;
rowl : pByteArray;
bc,bit,bit2,bitc: byte;
begin
psz:=3; // 24bit bitmap
msg:=''; // сообщение пока пусто
if not Assigned(BMP) then exit;
with BMP do begin
c:=0;
BC:=0;
for i:=0 to Height-1 do begin
rowl:=ScanLine[i];
pb:=@rowl[0];
for j:=0 to Width-1 do begin
asm
xor al,al // 1 бит сообщения на пиксель
inc al
mov bit,al
end;
Bit:=PB^ and Bit;
asm
mov al,bit
shr al,0
mov bit2,al
end;
// проверяем установленные биты
Bit2:=Bit2 shl BC; // операции противополжны процедуре выше
BitC:=1;
asm
mov cl,BC
add BitC,cl
end;
C:=C or (BitC and Bit2);
if BC = 7 then // прошли байт
begin
if C <> 0 then //- 0? конец данных
begin
msg:=msg+Chr(C);
if (Length(msg) = 4) then // длинна сигнатуры?
begin
if msg = sign then // да,- сигнатура совпала- да.
begin
msg:='';
end;
end;
end
else if (i + j) > 0 then // если не первый пиксель
exit;
C:=0;
BC:=0; // сбрасываем счетчик битов
end
else
inc(BC); // читаем след. бит.
inc(PB, Psz);
end;
end;
end;
end;
А ВОТ ТУТ ТО И ПРИКОЛ!!!
я решил устроить просто небольшую фишку (если это вообще хоть кому-то интересно) я допустил ошибку в строке расшифровки, а именно вот тут
Код:
Bit2:=Bit2 shl BC; // операции противополжны процедуре выше
BitC:=1;
asm
mov cl,BC
add BitC,cl
end;
ее найти очень просто (подсказка в коменте). кто сможет найти тот сможет кой чего посмотреть,это я укажу в конце статьи.
МЕТОДЫ АНАЛИЗА
как правило если это не такая студенческая поделка, как у меня, которую вскроет любая стегоаналитическая софтина, то методы атаки основаны на нехеровых мат. расчетах что мне объяснить не доступно ибо я не математик.
если же это что то простое то обычно можно применить:
прослушивание-просматривание файла
известен пустой контейнер - например добавить шумы к изображению, проанализировать звуковой спектр в аудиофайле етц…
я провел свое микроисследование этого же метода НЗБ
вот что мы видим:
размер одинаков.
а теперь засовываем в хекс редактор.
как можно наблюдать слева- зашированное, программа меняла последний бит картинки и ставила в соответствие
00 - 0
01 - 1
FF - 1
FE - 0
строчки снизу вверх, а так же в винде по-моему не как у людей RGB , а BGR.
даа забыл указать, объемы скрываемых сообщений зависят от применяемых алгоритмов и обычно бывают ~ 1\5 от объема файла контейнера.
================================================== ==============================================
вот и подошел момент истины что называется.Это такой миниквест если кому интересно(не только ж я себе мозги ебу). с помощью программки по НЗБ в этой картинке
зашифрован адрес на архив.
а с помощью вот этого кода
Код:
program Project1;
{$APPTYPE CONSOLE}
uses sysutils;
var
inn, outt : textfile;
i, j, cnt : integer;
OneSymbol : byte;
sIn, s, sOne : string;
inpt:string = 'C:\1.txt';
otp:string= 'C:\2.txt';
procedure encrypt;
begin
Assign(inn, inpt);
Assign(outt, otp);
WriteLn('input the encode string: ');
Readln(sIn);
Reset(inn);
cnt:=0;
while (Not EOF(inn)) do
begin
Readln(inn);
inc(cnt);
end;
Close(inn);
begin
Reset(inn);
Rewrite(outt);
for i:=1 to Length(sIn) do begin
OneSymbol := ord(sIn[i]);
for j:=1 to 8 do begin
ReadLn(inn, s);
while ( Length(s)>0 ) and (s[Length(s)]=' ') do Delete(s,Length(s),1);
if (OneSymbol and 1) = 1 then s:= s+' ';
WriteLn(outt, s);
OneSymbol := OneSymbol shr 1;
end;
end;
ReadLn(inn, s);
while ( Length(s)>0 ) and (s[Length(s)]=' ') do Delete(s,Length(s),1);
s:= s+' ';
WriteLn(outt, s);
while Not EOF(inn) do begin
ReadLn(inn, s);
WriteLn(outt, s);
end;
Close(outt);
Close(inn);
end;
end;
procedure decrypt;
begin
Assign(inn, inpt);
Assign(outt, otp);
sIn := '';
Reset(outt);
i:= 0;
OneSymbol := 0;
while Not EOF(outt) do begin
Readln(outt, s);
if Length(s)>=2 then
if copy(s,Length(s)-1,2)=' '
then Break;
inc(i);
if (Length(s)>0) and (Copy(s, Length(s),1)=' ') then OneSymbol := OneSymbol or $80;
if i=8 then begin
sIn := sIn + chr(OneSymbol);
i:=0;
OneSymbol := 0;
end
else OneSymbol := OneSymbol shr 1;
end;
WriteLn('detected: ', sIn);
WriteLn;
Readln;
end;
begin
// uncomment what u need
//encrypt;
decrypt;
end.
из архива из текстового файла извлекается пароль к папке вот тяк от.
всем спасибо за внимание. баклажанами не бросаться!)