Steganografia LSB

Kiedyś pisałem swoje pierwszy post na hubie. A ten post był poświęcony bardzo ciekawemu problemowi, a mianowicie steganografii. Oczywiście rozwiązania zaproponowanego w tym starym temacie nie można nazwać steganografią w prawdziwym tego słowa znaczeniu. To tylko gra z formatami plików, ale mimo to całkiem interesująca gra.

Dzisiaj spróbujemy pogrzebać trochę głębiej i przyjrzeć się algorytmowi LSB. Zainteresowanych zapraszamy pod kat. (Pod cięciem znajduje się ruch: około megabajta.)

Na początek konieczne jest dokonanie krótkiego wprowadzenia. Wszyscy wiedzą, że celem kryptografii jest uniemożliwienie odczytania tajnych informacji. Oczywiście kryptografia ma swoje zastosowania, ale istnieje inne podejście do ochrony danych. Nie musimy szyfrować informacji, ale udajemy, że ich nie mamy. Właśnie po to wynaleziono steganografię. Wikipedia zapewnia, że ​​„steganografia (od greckiego στεγανοσ – ukryty i greckiego γραφω – piszę, dosłownie „tajne pisanie”) to nauka o ukrytym przekazywaniu informacji, polegająca na utrzymywaniu w tajemnicy samego faktu przekazania.

Oczywiście nikt nie zabrania łączenia metod kryptograficznych i steganograficznych. Co więcej, w praktyce tak robią, ale naszym zadaniem jest zrozumienie podstaw. Jeśli dokładnie przestudiujesz artykuł na Wikipedii, dowiesz się, że algorytmy steganograficzne obejmują tzw. kontener i wiadomość. Kontener to każda informacja, która pomaga ukryć naszą tajną wiadomość.

W naszym przypadku kontenerem będzie obraz w formacie BMP. Najpierw przyjrzyjmy się strukturze tego pliku. Plik można podzielić na 4 części: nagłówek pliku, nagłówek obrazu, paletę i sam obraz. Dla naszych celów musimy jedynie wiedzieć, co jest napisane w nagłówku.

Pierwsze dwa bajty nagłówka to sygnatura BM, następnie wielkość pliku w bajtach zapisana jest w podwójnym słowie, kolejne 4 bajty są zarezerwowane i muszą zawierać zera, a na koniec kolejne podwójne słowo zawiera przesunięcie od początku plik do rzeczywistych bajtów obrazu. W 24-bitowym pliku bmp każdy piksel jest zakodowany w trzech bajtach BGR.

Teraz wiemy, jak dostać się do obrazu, pozostaje tylko zrozumieć, w jaki sposób możemy zapisać tam potrzebne nam informacje. Do tego będziemy potrzebować metody LSB. Istota metody jest następująca: w bajtach odpowiedzialnych za kodowanie kolorów zastępujemy najmniej znaczące bity. Powiedzmy, że jeśli następny bajt naszej tajnej wiadomości to 11001011, a bajty na obrazku to... 11101100 01001110 01111100 0101100111..., to kodowanie będzie wyglądać następująco. Podzielimy bajt tajnej wiadomości na 4 dwubitowe części: 11, 00, 10, 11 i zastąpimy mniej znaczące bity obrazu powstałymi fragmentami: ...11101111 01001100 01111110 0101100111…. Taka wymiana jest na ogół niezauważalna dla ludzkiego oka. Co więcej, wiele starszych urządzeń wyjściowych nie będzie nawet w stanie wyświetlić tak drobnych zmian.

Oczywiste jest, że można zmienić nie tylko 2 najmniej znaczące bity, ale dowolną ich liczbę. Istnieje następujący schemat: im więcej bitów zmienimy, tym więcej informacji możemy ukryć i tym więcej zakłóceń spowoduje to w oryginalnym obrazie. Dla przykładu oto dwa obrazy:

Steganografia LSB
Steganografia LSB

Mimo moich największych wysiłków nie udało mi się dostrzec między nimi różnicy, niemniej jednak na drugim obrazie, przy użyciu opisanej metody, ukryty jest wiersz Lewisa Carrolla „Polowanie na Snarka”. Jeśli doczytałeś aż dotąd, prawdopodobnie jesteś zainteresowany poznaniem implementacji. To dość proste, ale od razu ostrzegam, że wszystko odbywa się w Delphi. Są ku temu dwa powody: 1. Uważam, że Delphi jest dobrym językiem; 2. Program ten narodził się w trakcie przygotowywania kursu z podstaw widzenia komputerowego, a goście, dla których uczę tego kursu, nie znają jeszcze niczego innego niż Delphi. Tym, którzy nie są zaznajomieni ze składnią, należy wyjaśnić jedną rzecz: shl x to bitowe przesunięcie w lewo o x, shr x to bitowe przesunięcie w prawo o x.

Zakładamy, że zapisujemy do kontenera tekst przechowywany w postaci ciągu znaków i zastępujemy dwa dolne bajty:
Kod nagrywania:

dla i:=1 do długości(str) wykonaj
    rozpocząć
      l1:=bajt(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.Pozycja:=f.Pozycja-1;
      tmp:=((tmp shr 2) shl 2)+l1;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Pozycja:=f.Pozycja-1;
      tmp:=((tmp shr 2) shl 2)+l2;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Pozycja:=f.Pozycja-1;
      tmp:=((tmp shr 2) shl 2)+l3;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Pozycja:=f.Pozycja-1;
      tmp:=((tmp shr 2) shl 2)+l4;
      f.WriteBuffer(tmp,1);
 
    puszki;

kod do przeczytania:

dla i:=1 do MsgSize do
    rozpocząć
      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+znak(l1+l2+l3+l4);
    puszki;

Cóż, dla naprawdę leniwych - link do programu i jego kodu źródłowego.

Dziękuję.

Źródło: www.habr.com

Dodaj komentarz