Steganografia jest metodą ukrywania informacji tajnej przez osadzenie jej w innym nietajnym elemencie (filmie, grafice, dźwięku). Poprzez zastąpienie bitów danych niepotrzebnych lub nieużywanych bitami wiadomości tajnej. Taka wiadomość może być tekstem, szyfrogramem, zdjęciem, filmem, wszystkim, co chcemy (o ile niepotrzebnych lub nieużywanych bitami jest wystarczająco dużo). W naszym przypadku nasze dane będą zwykłym tekstem, które musimy ukryć, a niewykorzystane dane są najmniej znaczącymi bitami (LSB) w pikselach obrazu.
Czym jest LSB
W celu znalezienia wartości dziesiętnej reprezentacji binarnej sumujemy wszystkie wartości 1, przemnożone przez 2n gdzie n jest pozycją (indeksem), zaczynając od zera z prawej strony. Przykładowo konwersja 01001010 na system dziesiętny zaczniemy od prawej strony:
0*20+1*21+0*22+1*23+0*24+0*25+1*26+0*27=2+8+64= 74
Najmniej znaczącym bitem (LSB) jest pierwszy bit od prawej strony. Jego zmiana na 1 nie zmieni nam znacząco liczby dziesiętnej, jej wartość zostanie zakłócona jedynie o 1:
0*20+1*21+0*22+1*23+0*24+0*25+1*26+0*27=2+8+64= 74 | 1*20+1*21+0*22+1*23+0*24+0*25+1*26+0*27=1+2+8+64= 75
Oznacza to, że w pewnych okolicznościach nasze źródło nie zostanie znacząco zmienione, gdy zarezerwujemy ten bit dla naszych celów. Kiedy staramy się ukryć wiadomość w pliku graficznym, musimy wiedzieć jaką pulą LSB dysponujemy. Bity te zlokalizowane są w pikselach obrazu. Przy założeniu nieprzezroczystości koloru, piksel składać się bezie z kombinacji kolorów czerwonego, zielonego i niebieskiego (RGB), przy czym każdy z tych elementów może mieć wartość od 0 do 255. Jeśli założymy, że obraz ma 200 pikseli szerokości i 300 wysokości, daje nam to 200*300*3 = 180000 LSB. W takiej puli możemy ukryć 180000/8 = 22500 znaków reprezentowanych przy użyciu 8 bitów (np. znaki z rodziny ASCII).
Jeśli chcielibyśmy ukryć wiadomość tekstową w pliku graficznym, musimy wiedzieć, jak taki plik jest zbudowany, zmiana bita nawet w plikach graficznych może prowadzić do ich uszkodzenia, lub utraty bitów informacji ukrytej podczas kompresji, pliki mogą zawierać Metadane lub dodatkowe informacje, które podczas zmiany wypadałoby ominąć, aby skutecznie ukryć wiadomość bez wykrycia. Napiszemy sobie teraz program, który będzie ukrywał nam taką wiadomość w pliku graficznym. Formaty, w którym będziemy mogli bezpiecznie ukryć taką wiadomość to format *.bmp, ponieważ obsługuje tryb RGB i RGBA i podlega prostej kompresji bezstratnej. Więcej o budowie plików z grafiką bitmapową można poczytać na Wikipedi -> link
Proces ukrycia wiadomości:
- Pobieramy plik graficzny do wewnętrznego bufora.
Imports System.Drawing
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
End If
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
End Sub
End Module
- Obliczamy ile mamy dostępnych LSB.
Imports System.Drawing
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
End If
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
Dim lsb As Integer = (bmp.Width * bmp.Height)*3
Console.WriteLine(lsb.ToString + " ilość dozwolonych znaków znaków.")
Console.ReadLine()
End Sub
End Module
- Ograniczamy długość ukrytej wiadomości do Floor(((ilości Pixeli)*3)/8).
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
End If
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
Dim lsb As Integer = (bmp.Width * bmp.Height)*3
Console.WriteLine(Math.Floor(lsb/ 8).ToString + " ilość dozwolonych znaków znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb/ 8)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
Console.WriteLine("Tekst został ukryty!")
Console.ReadLine()
End Sub
End Module
- Skanuj wszystkie pixele obrazka.
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
For y = 0 To ymax
For x = 0 To xmax
Next x
Next y
- Dla każdego R , G i B ustawiamy ostatni bit (LSB) na 0.
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
End If
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
Dim lsb As Integer = (bmp.Width * bmp.Height)*3
Console.WriteLine(Math.Floor(lsb / 8)ToString + " ilość dozwolonych znaków znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb / 8)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For x = 0 To xmax
With bmp.GetPixel(x, y)
R = .R - .R Mod 2
G = .G - .G Mod 2
B = .B - .B Mod 2
End With
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
Next y
bmp.Save("C:\Users\piotr\Desktop\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Tekst został ukryty!")
Console.ReadLine()
End Sub
End Module
- Teraz pobierz numer ASCII aktualnej litery (na podstawie rosnącego indeksu). Następnie schowaj jego 8 bitów w kolejnych kolorach R1,B1,G1,R2,B2,G2,R3,B3 obrazka używając pętli. W każdym LSB każdego R1 R2….B3 ukryj jeden bit znaku. Kiedy 8 bitów znaku zostaną wyczerpane, przejdź do następnego znaku. Powtórz proces dla każdego znaku w teście, który chcemy ukryć. Na koniec dodaj 8 zer, które oddzielą ukrytą wiadomość od reszty pikseli i ułatwi zadanie przy wydobyciu tekstu.
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
'Otwiera dialog z użytkownikiem
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
Dim lsb As Integer = ((bmp.Width * bmp.Height) * 3)
Console.WriteLine(Math.Floor(lsb / 8).ToString + " ilość dozwolonych znaków znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb / 8)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
''''' Ukrywanie wiadomości
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexPixela As Integer = 0
Dim wartoscLitery As Integer = 0
Dim indexLitery As Integer = 0
Dim zera As Integer = 0
Dim wypelniajZerami As Boolean = False
Dim wyjsciezpetli As Boolean = False
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Ustawia LSB na 0
R = pix.R - (pix.R Mod 2)
G = pix.G - (pix.G Mod 2)
B = pix.B - (pix.B Mod 2)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
'Sprawdza czy przeszliśmy już 8 bitów
If (pixelElementIndex Mod 8) = 0 Then
'Sprawdza czy cały proces został już skończony
'upewniamy się nadpisując na końcu 8 zer
If (wypelniajZerami = True AndAlso zera = 8) Then
If (((pixelElementIndex - 1) Mod 3) < 2) Then
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End If
wyjsciezpetli = True
Exit For
End If
If (indexLitery >= TekstDoUkrycia.Length) Then
wypelniajZerami = True
Else
wartoscLitery = Asc(TekstDoUkrycia(indexLitery))
indexLitery += 1
End If
End If
'Ukryj bity numeru ASCII w kolorach
Select Case i
Case 0
'dla czerwonego
If wypelniajZerami = False Then
R += (wartoscLitery Mod 2) 'pobiera resztę z dzielenia wartości dziesiętnej ASCII
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 1
'dla zielonego
If wypelniajZerami = False Then
G += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 2
'dla niebieskiego
If wypelniajZerami = False Then
B += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End Select
pixelElementIndex += 1
If wypelniajZerami = True Then
zera += 1
End If
Next
If wyjsciezpetli = True Then
Exit For
End If
Next x
If wyjsciezpetli = True Then
Exit For
End If
Next y
bmp.Save("C:\Users\piotr\Desktop\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Tekst został ukryty!")
Console.ReadLine()
End If
End Sub
End Module
Przykładowo, mamy plik graficzny, złożony z trzech kolorów , pierwszy (ciemny żółty) to RBG(255;201;14), drugi RBG(112;146;190) i trzeci RBG(181;230;29), zmieści nam się tylko jedna litera, więc umieścimy tam sobie literę „t” której numer ASCII to 116 a reprezentacja bitowa to 0111 0100
przed wyzerowaniem:
255 = 11111111 \\ = 255- (255%2) = 254 11111110 (116%2) = 0 = 11111110 = 254
201= 11001001 \\ = 201- (201%2) = 200 11001000 Floor(116/2)= 58 ( 58%2) = 0 = 11001000 = 200
14= 1110 \\ = 14- (14%2) = 14 1110 Floor(58/2)= 29 (29%2) = 1 =1111 =15
112= 1110000 \\ = 112- (112%2) = 112 1110000 Floor(29/2)= 14 (14%2) = 0 =1110000 = 112
146= 10010010 \\ = 146- (146%2) = 146 10010010 Floor(14/2)= 7 (7%2) = 1 =1110001 = 147
190= 10111110 \\ = 190- (190%2) = 190 10111110 Floor(7/2)= 3 (3%2) = 1 = 10111111 = 191
181= 10110101 \\ = 181- (181%2) = 180 10110100 Floor(3/2)= 1 (1%2) = 1 =10111111 = 181
230= 11100110 \\ = 230- (230%2) = 230 11100110 Floor(1/2)= 0 (0%2) = 0 = 11100110 = 230
29= 11101 \\ = 29- (29%2) = 28 11100 następna litera lub zero kontrolne = 11100 = 28
Oczywiście nic nie szkodzi na przeszkodzie, aby użyć dwóch ostatnich bitów, trzech lub czterech:
dla dwóch ostatnich bitów
przed wyzerowaniem:
255 = 11111111 \\ = 255- (255%4) = 252 11111100 (116%(2^2)) =0 (odwróć bity)=0 = 00 = 11111100 = 252
201= 11001001 \\ = 201- (201%4) = 200 11001000 (29%(2^2)) = 1 (01) (odwróć bity)=2 = 10 = 11001010 = 202
14= 1110 \\ = 14- (14%4) = 12 1100 (7%(2^2)) =3 (odwróć bity)=3 = 11 = 1111 =15
112= 1110000 \\ = 112- (112%4) = 112 1110000 (1%(2^2))= 1 (01) (odwróć bity)=2 = 10 = 1110010 = 114
146= 10010010 \\ = 146- (146%4) = 144 10010000 następna litera lub zero kontrolne = 10010000 = 144
190= 10111110 \\ = 190- (190%4) = 188 10111100 następna litera lub zero kontrolne = 10111100 = 188
181= 10110101 \\ = 181- (181%4) = 180 10110100 następna litera lub zero kontrolne = 10110100 = 180
230= 11100110 \\ = 230- (230%4) = 228 11100100 następna litera lub zero kontrolne = 11100100 = 228
29= 11101 \\ = 29- (29%4) = 28 11100 koniec pętli = 29
Oczywiście im więcej bitów użyjemy, tym nasz obrazek będzie bardziej zniekształcony, na pierwszym etapie dzieliliśmy modulo przez 2, w kolejnym przez 2*2 = 4 w kolejnym byłoby to 2*2*2 = 8 itd. Kiedy wiemy, już jak działa ukrywanie tekstu w pikselach, przejdźmy teraz do interpretatora naszego tekstu. Skorzystajmy z przykładu powyżej:
x= nasz znak =0
254 = 11111110 \\ 0* 2 + (254%2) = 0
200 = 11001000 \\ 0*2 + (200%2) =0
15 =1111 \\ 0*2 + (15%2) =1
112 =1110000 \\ 1*2 + (112%2) =2
147 =1110001 \\ 2*2+ (147%2) =5
191 =10111111 \\ 5*2+ (191%2) =11
181 =10111111 \\ 11*2+ (181%2) =23
230 =11100110 \\ 23*2+ (230%2) =46 = 00101110 -> po odwróceniu -> 0111 0100 = 116 = Chr(116) = t
28 =11100 koniec pętli
przykład dla dwóch bitów:
x= nasz znak =0
252 = 11111100 \\ 0* 4 + (252%4) = 0
202 = 11001001 \\ 0*4 + (202%4) =2
15 =1111 \\ 2*4 + (15%4) =11
114 =1110001 \\ 11*4 + (114%4) =46 = 00101110 -> po odwróceniu -> 0111 0100 = 116 = Chr(116) = t
144 =10010000 =00
188 =10111100 =00
180 =10110100 =00
228 =11100100 =00
29 – koniec pętli
Algorytm który odszyfruje nam wiadomość jest dużo prostszy, wygląda następująco:
Dim wiadomoscUkryta As String = ""
Dim indexkoloru As Integer = 0
Dim wartoscLitery As Integer = 0
Dim wyjscie As Boolean = False
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
Select Case indexkoloru Mod 3
Case 0
wartoscLitery = wartoscLitery * 2 + (pix.R Mod 2)
Case 1
wartoscLitery = wartoscLitery * 2 + (pix.G Mod 2)
Case 2
wartoscLitery = wartoscLitery * 2 + (pix.B Mod 2)
End Select
indexkoloru += 1
'Jeśli 8 bitów zostało dodanych, wtedy utwórz z nich znak
If indexkoloru Mod 8 = 0 Then
wartoscLitery = OdwrocBity(wartoscLitery)
'Jeśli napotkaliśmy na 8 zer wtedy przerywamy zadanie
If wartoscLitery = 0 Then
wyjscie = True
Exit For
Else
'Konwersja numeru znaku na znak
Dim ch As Char = Chr(wartoscLitery)
'dodaje znak do wiadomości
wartoscLitery = 0 'opcjonalne, jeśli użyjemy Chr wtedy wartość
'wykraczająca poza jej zasięg będzie potraktowana z automatu funkcją Mod
wiadomoscUkryta += ch
End If
End If
Next
If wyjscie = True Then
Exit For
End If
Next
If wyjscie = True Then
Exit For
End If
Next
Będzie potrzebna również funkcja która odwróci nam bity:
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
Przykładowy program który ukrywa wiadomość w pliku graficznym, zapisuje go i odczytuje dane:
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
'Otwiera dialog z użytkownikiem
Console.WriteLine("Co chesz zrobić? Wpisz cyfrę zadania." + vbNewLine + "1.Ukryj wiadomość." _
+ vbNewLine + "2.Odczytaj ukrytą wiadomość.")
Dim zad As String
Do
zad = Console.ReadLine()
If zad = "1" Or zad = "2" Then
Exit Do
Else
Console.WriteLine("możesz wybrać tylko cyfrę 1 lub 2.")
End If
Loop
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
If zad = 1 Then
Dim lsb As Integer = ((bmp.Width * bmp.Height) * 3)
Console.WriteLine(Math.Floor(lsb / 8).ToString + " ilość dozwolonych znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb / 8)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
''''' Ukrywanie wiadomości
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexPixela As Integer = 0
Dim wartoscLitery As Integer = 0
Dim indexLitery As Integer = 0
Dim zera As Integer = 0
Dim wypelniajZerami As Boolean = False
Dim wyjsciezpetli As Boolean = False
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Ustawia LSB na 0
R = pix.R - (pix.R Mod 2)
G = pix.G - (pix.G Mod 2)
B = pix.B - (pix.B Mod 2)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
'Sprawdza czy przeszliśmy już 8 bitów
If (pixelElementIndex Mod 8) = 0 Then
'Sprawdza czy cały proces został już skończony
'upewniamy się nadpisując na końcu 8 zer
If (wypelniajZerami = True AndAlso zera = 8) Then
If (((pixelElementIndex - 1) Mod 3) < 2) Then
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End If
wyjsciezpetli = True
Exit For
End If
If (indexLitery >= TekstDoUkrycia.Length) Then
wypelniajZerami = True
Else
wartoscLitery = Asc(TekstDoUkrycia(indexLitery))
indexLitery += 1
End If
End If
'Ukryj bity numeru ASCII w kolorach
Select Case i
Case 0
'dla czerwonego
If wypelniajZerami = False Then
'pobiera resztę z dzielenia wartości dziesiętnej ASCII
R += (wartoscLitery Mod 2)
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 1
'dla zielonego
If wypelniajZerami = False Then
G += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 2
'dla niebieskiego
If wypelniajZerami = False Then
B += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End Select
pixelElementIndex += 1
If wypelniajZerami = True Then
zera += 1
End If
Next
If wyjsciezpetli = True Then
Exit For
End If
Next x
If wyjsciezpetli = True Then
Exit For
End If
Next y
Dim lokalizacjaZapisu As String = "C:\Users\piotr\Desktop\"
Console.Write("Twoja wiadomość jest ukryta, obrazek zostanie zapisany na dysku (" _
+ lokalizacjaZapisu + ") pod nazwą: ")
Dim nazwaPliku As String = Console.ReadLine()
bmp.Save(lokalizacjaZapisu + nazwaPliku + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Tekst został ukryty, plik został zapisany!")
Console.ReadLine()
ElseIf zad = 2 Then
Dim wiadomoscUkryta As String = ""
Dim indexkoloru As Integer = 0
Dim wartoscLitery As Integer = 0
Dim wyjscie As Boolean = False
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
Select Case indexkoloru Mod 3
Case 0
wartoscLitery = wartoscLitery * 2 + (pix.R Mod 2)
Case 1
wartoscLitery = wartoscLitery * 2 + (pix.G Mod 2)
Case 2
wartoscLitery = wartoscLitery * 2 + (pix.B Mod 2)
End Select
indexkoloru += 1
'Jeśli 8 bitów zostało dodanych, wtedy utwórz z nich znak
If indexkoloru Mod 8 = 0 Then
wartoscLitery = OdwrocBity(wartoscLitery)
'Jeśli napotkaliśmy na 8 zer wtedy przerywamy zadanie
If wartoscLitery = 0 Then
wyjscie = True
Exit For
Else
'Konwersja numeru znaku na znak
Dim ch As Char = Chr(wartoscLitery)
'dodaje znak do wiadomości
wartoscLitery = 0 'opcjonalne, jeśli użyjemy Chr wtedy wartość
'wykraczająca poza jej zasięg będzie potraktowana z automatu funkcją Mod
wiadomoscUkryta += ch
End If
End If
Next
If wyjscie = True Then
Exit For
End If
Next
If wyjscie = True Then
Exit For
End If
Next
Console.Write("Odczytano wiadomość ukrytą w pixelach: ")
Console.Write(wiadomoscUkryta)
Console.ReadLine()
End If
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Przykładowy program który ukrywa wiadomość w pliku graficznym z wykorzystaniem dwóch ostatnich bitów, zapisuje go i odczytuje dane:
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
'Otwiera dialog z użytkownikiem
Console.WriteLine("Co chesz zrobić? Wpisz cyfrę zadania." + vbNewLine + "1.Ukryj wiadomość." _
+ vbNewLine + "2.Odczytaj ukrytą wiadomość.")
Dim zad As String
Do
zad = Console.ReadLine()
If zad = "1" Or zad = "2" Then
Exit Do
Else
Console.WriteLine("możesz wybrać tylko cyfrę 1 lub 2.")
End If
Loop
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
If zad = 1 Then
Dim lsb As Integer = ((bmp.Width * bmp.Height) * 6)
Console.WriteLine(Math.Floor(lsb / 4).ToString + " ilość dozwolonych znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb / 4)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
''''' Ukrywanie wiadomości
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexPixela As Integer = 0
Dim wartoscLitery As Integer = 0
Dim indexLitery As Integer = 0
Dim zera As Integer = 0
Dim wypelniajZerami As Boolean = False
Dim wyjsciezpetli As Boolean = False
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Ustawia LSB na 0
R = pix.R - (pix.R Mod 4)
G = pix.G - (pix.G Mod 4)
B = pix.B - (pix.B Mod 4)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
'Sprawdza czy przeszliśmy już 8 bitów
If (pixelElementIndex Mod 4) = 0 Then
'Sprawdza czy cały proces został już skończony
'upewniamy się nadpisując na końcu 8 zer
If (wypelniajZerami = True AndAlso zera = 4) Then
If (((pixelElementIndex - 1) Mod 3) < 2) Then
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End If
wyjsciezpetli = True
Exit For
End If
If (indexLitery >= TekstDoUkrycia.Length) Then
wypelniajZerami = True
Else
wartoscLitery = Asc(TekstDoUkrycia(indexLitery))
indexLitery += 1
End If
End If
'Ukryj bity numeru ASCII w kolorach
Select Case i
Case 0
'dla czerwonego
If wypelniajZerami = False Then
'pobiera resztę z dzielenia wartości dziesiętnej ASCII
R += Odwroc2Bity(wartoscLitery Mod 4)
wartoscLitery = Math.Floor(wartoscLitery / 4)
End If
Case 1
'dla zielonego
If wypelniajZerami = False Then
G += Odwroc2Bity(wartoscLitery Mod 4)
wartoscLitery = Math.Floor(wartoscLitery / 4)
End If
Case 2
'dla niebieskiego
If wypelniajZerami = False Then
B += Odwroc2Bity(wartoscLitery Mod 4)
wartoscLitery = Math.Floor(wartoscLitery / 4)
End If
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
End Select
pixelElementIndex += 1
If wypelniajZerami = True Then
zera += 1
End If
Next
If wyjsciezpetli = True Then
Exit For
End If
Next x
If wyjsciezpetli = True Then
Exit For
End If
Next y
Dim lokalizacjaZapisu As String = "C:\Users\piotr\Desktop\"
Console.Write("Twoja wiadomość jest ukryta, obrazek zostanie zapisany na dysku (" _
+ lokalizacjaZapisu + ") pod nazwą: ")
Dim nazwaPliku As String = Console.ReadLine()
bmp.Save(lokalizacjaZapisu + nazwaPliku + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Tekst został ukryty, plik został zapisany!")
Console.ReadLine()
ElseIf zad = 2 Then
Dim wiadomoscUkryta As String = ""
Dim indexkoloru As Integer = 0
Dim wartoscLitery As Integer = 0
Dim wyjscie As Boolean = False
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Dla każdego Elementu pixela (RGB)
For i As Integer = 0 To 2
Select Case indexkoloru Mod 3
Case 0
wartoscLitery = wartoscLitery * 4 + (pix.R Mod 4)
Case 1
wartoscLitery = wartoscLitery * 4 + (pix.G Mod 4)
Case 2
wartoscLitery = wartoscLitery * 4 + (pix.B Mod 4)
End Select
indexkoloru += 1
'Jeśli 8 bitów zostało dodanych, wtedy utwórz z nich znak
If indexkoloru Mod 4 = 0 Then
wartoscLitery = OdwrocBity(wartoscLitery)
'Jeśli napotkaliśmy na 8 zer wtedy przerywamy zadanie
If wartoscLitery = 0 Then
wyjscie = True
Exit For
Else
'Konwersja numeru znaku na znak
Dim ch As Char = Chr(wartoscLitery)
'dodaje znak do wiadomości
wartoscLitery = 0 'opcjonalne, jeśli użyjemy Chr wtedy wartość
'wykraczająca poza jej zasięg będzie potraktowana z automatu funkcją Mod
wiadomoscUkryta += ch
End If
End If
Next
If wyjscie = True Then
Exit For
End If
Next
If wyjscie = True Then
Exit For
End If
Next
Console.Write("Odczytano wiadomość ukrytą w pixelach: ")
Console.Write(wiadomoscUkryta)
Console.ReadLine()
End If
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
Private Function Odwroc2Bity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 1
For i As Integer = 0 To 1
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Oczywiście im jesteśmy sprytniejsi tym nasza wiadomość będzie lepiej ukryta, można taką wiadomość w prosty sposób zaszyfrować używając szyfru przestawnego lub szyfru z kluczem który dodatkowo utrudni odczytanie wiadomości. Do naszego programu można dodać dodatkowy element piksela którym jest kanał Alfa. Odpowiada on za przezroczystość naszego piksela i oznaczony jest jako A, jego skala jest taka jak dla kolorów od 0-255 z tą różnicą, że 255 odpowiada za pełną widoczność piksela więc automatycznie jego wartość ustawiona jest na 255. Zwiększa to ilość bitów które możemy ukryć, plik taki możemy zapisać w formacie png lub gif na przykład:
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
'Otwiera dialog z użytkownikiem
Console.WriteLine("Co chesz zrobić? Wpisz cyfrę zadania." + vbNewLine + "1.Ukryj wiadomość." _
+ vbNewLine + "2.Odczytaj ukrytą wiadomość.")
Dim zad As String
Do
zad = Console.ReadLine()
If zad = "1" Or zad = "2" Then
Exit Do
Else
Console.WriteLine("możesz wybrać tylko cyfrę 1 lub 2.")
End If
Loop
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp; *.tiff"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
bmp.MakeTransparent()
If zad = 1 Then
Dim lsb As Integer = ((bmp.Width * bmp.Height) * 4)
Console.WriteLine(Math.Floor(lsb / 8).ToString + " ilość dozwolonych znaków.")
Console.Write("Wprowadź tekst do ukrycia: ")
Dim TekstDoUkrycia = New StringBuilder()
Dim maxLength = Math.Floor(lsb / 8)
While True
Dim cki As ConsoleKeyInfo = Console.ReadKey(True)
Select Case cki.Key
Case ConsoleKey.Enter
' Koniec wprowadzania
Console.WriteLine()
Exit While
Case ConsoleKey.Backspace
' Usówanie ostatniego znaku
If TekstDoUkrycia.Length > 0 Then
TekstDoUkrycia.Remove(TekstDoUkrycia.Length - 1, 1)
Console.Write(vbBack & " " & vbBack)
End If
Case Else
' Dodaje znaki do zmiennej string
If TekstDoUkrycia.Length < maxLength AndAlso Not Char.IsControl(cki.KeyChar) Then
TekstDoUkrycia.Append(cki.KeyChar)
Console.Write(cki.KeyChar)
End If
End Select
End While
''''' Ukrywanie wiadomości
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0, A As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexPixela As Integer = 0
Dim wartoscLitery As Integer = 0
Dim indexLitery As Integer = 0
Dim zera As Integer = 0
Dim wypelniajZerami As Boolean = False
Dim wyjsciezpetli As Boolean = False
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Ustawia LSB na 0
R = pix.R - (pix.R Mod 2)
G = pix.G - (pix.G Mod 2)
B = pix.B - (pix.B Mod 2)
A = pix.A - (pix.A Mod 2)
'Dla każdego Elementu pixela (ARGB)
For i As Integer = 0 To 3
'Sprawdza czy przeszliśmy już 8 bitów
If (pixelElementIndex Mod 8) = 0 Then
'Sprawdza czy cały proces został już skończony
'upewniamy się nadpisując na końcu 8 zer
If (wypelniajZerami = True AndAlso zera = 8) Then
If (((pixelElementIndex - 1) Mod 3) < 2) Then
bmp.SetPixel(x, y, Color.FromArgb(A, R, G, B))
End If
wyjsciezpetli = True
Exit For
End If
If (indexLitery >= TekstDoUkrycia.Length) Then
wypelniajZerami = True
Else
wartoscLitery = Asc(TekstDoUkrycia(indexLitery))
indexLitery += 1
End If
End If
'Ukryj bity numeru ASCII w kolorach
' MsgBox(wartoscLitery.ToString)
Select Case i
Case 0
'dla kanału alfa
If wypelniajZerami = False Then
A += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 1
'dla czerwonego
If wypelniajZerami = False Then
'pobiera resztę z dzielenia wartości dziesiętnej ASCII
R += (wartoscLitery Mod 2)
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 2
'dla zielonego
If wypelniajZerami = False Then
G += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
Case 3
'dla niebieskiego
If wypelniajZerami = False Then
B += wartoscLitery Mod 2
wartoscLitery = Math.Floor(wartoscLitery / 2)
End If
bmp.SetPixel(x, y, Color.FromArgb(A, R, G, B))
End Select
pixelElementIndex += 1
If wypelniajZerami = True Then
zera += 1
End If
Next
If wyjsciezpetli = True Then
Exit For
End If
Next x
If wyjsciezpetli = True Then
Exit For
End If
Next y
Dim lokalizacjaZapisu As String = "C:\Users\Aldon\Desktop\"
Console.Write("Twoja wiadomość jest ukryta, obrazek zostanie zapisany na dysku (" _
+ lokalizacjaZapisu + ") pod nazwą: ")
Dim nazwaPliku As String = Console.ReadLine()
bmp.Save(lokalizacjaZapisu + nazwaPliku + ".png", System.Drawing.Imaging.ImageFormat.Png)
Console.WriteLine("Tekst został ukryty, plik został zapisany!")
Console.ReadLine()
ElseIf zad = 2 Then
Dim wiadomoscUkryta As String = ""
Dim indexkoloru As Integer = 0
Dim wartoscLitery As Integer = 0
Dim wyjscie As Boolean = False
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
For y = 0 To ymax
For x = 0 To xmax
'przechowuje aktualnie wybrany pixel
Dim pix As Color = bmp.GetPixel(x, y)
'Dla każdego Elementu pixela (ARGB)
For i As Integer = 0 To 3
Select Case indexkoloru Mod 4
Case 0
wartoscLitery = wartoscLitery * 2 + (pix.A Mod 2)
Case 1
wartoscLitery = wartoscLitery * 2 + (pix.R Mod 2)
Case 2
wartoscLitery = wartoscLitery * 2 + (pix.G Mod 2)
Case 3
wartoscLitery = wartoscLitery * 2 + (pix.B Mod 2)
End Select
indexkoloru += 1
'Jeśli 8 bitów zostało dodanych, wtedy utwórz z nich znak
If indexkoloru Mod 8 = 0 Then
wartoscLitery = OdwrocBity(wartoscLitery)
'Jeśli napotkaliśmy na 8 zer wtedy przerywamy zadanie
If wartoscLitery = 0 Then
wyjscie = True
Exit For
Else
'Konwersja numeru znaku na znak
Dim ch As Char = Chr(wartoscLitery)
'dodaje znak do wiadomości
wartoscLitery = 0 'opcjonalne, jeśli użyjemy Chr wtedy wartość
'wykraczająca poza jej zasięg będzie potraktowana z automatu funkcją Mod
wiadomoscUkryta += ch
End If
End If
Next
If wyjscie = True Then
Exit For
End If
Next
If wyjscie = True Then
Exit For
End If
Next
Console.Write("Odczytano wiadomość ukrytą w pixelach: ")
Console.Write(wiadomoscUkryta)
Console.ReadLine()
End If
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Przykład powyższy ukazuje jak w praktyce ukrywać dane w pikselach, lecz pliki graficzne zawierają różne miejsca w których możemy te dane ukryć, nie są to tylko piksele. Takich miejsc jest więcej, lecz aby je znaleźć musimy poznać strukturę danego formatu. zazwyczaj taki plik składa się z nagłówka i danych, aby podejrzeć i edytować budowę pliku należy wiedzieć czym jest bajt i bit.
- Bit – to najmniejsza ilość informacji potrzebna do określenia, który z dwóch równie prawdopodobnych stanów przyjął układ. Jednostka logiczna. Binarny sposób zapisu informacji związany jest z tym, że komputer jako urządzenie cyfrowe rozpoznać może dwa stany napięciowe 0 lub 1.
- Bajt – najmniejsza adresowalna jednostka informacji pamięci komputerowej, składająca się z bitów. Zwykle przyjmuje się, że jeden bajt to 8 bitów, w praktyce jeden bajt może zawierać dowolną liczbę bitów.
Aby wniknąć w strukturę pliku należy dysponować programem do edycji hex’ów, ja używam programu HxD za pomocą którego prezentował będę wnętrza plików. wyświetla on dane w systemie szesnastkowym. Jeśli jeden bajt to 8 bitów w takim bądź razie przyjmuje on wartości:
00000000-11111111 w systemie dziesiętnym jest to 0-255 w szesnastkowym będzie to 0- FF
Teraz gdy już wiecie czym są LSB i jak zastosować steganografie w praktyce, stwórzcie własne programy bardziej skomplikowane i rozbudowane, powodzenia