Ukrywaliśmy już pliki, wiadomość i obrazki w różnych miejscach. Siłą rzeczy następnym miejscem, w którym ukryjemy nasze elementy, będą pliki dźwiękowe, do najprostszych, jeśli chodzi o strukturę, należy format WAVE. Jest on bardzo podobny do struktury formatu pliku BMP, do zapisu i odczytania danych używana jest konwersja bezstratna, dlatego możemy bez przeszkód, manipulować tymi danymi nie martwiąc się, że po ich zmianie dane ulegną uszkodzeniu lub będą błędnie odczytane. Plik WAVE składa się z trzech głównych elementów:
Nagłówek RIFF:
Podkategoria „fmt” opisuje format danych dźwiękowych:
Podkategoria „danych” zawiera rozmiar danych i rzeczywisty dźwięk:
Czasem występuje również podkategoria JUNK, która nadaje się do ukrycia tekstu w bardzo prosty sposób. W dalszej części pokaże algorytm, który będzie dodawał takie pola. Kategoria ta może występować zarówno po RIFF, jak i po „fmt „:
Można ją wygenerować sztucznie i wpisać w niej cokolwiek tylko chcemy, z zachowaniem pewnych zasad. Przykładowy program pozwalający na ukrycie pliku w bajtach nagłówka JUNK:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
Imports System.Windows.Forms Module Module1 Sub Main() 'Otwiera dialog z użytkownikiem Console.WriteLine("Co chesz zrobić? Wpisz cyfrę zadania." + vbNewLine + "1.Wydobądź ukryty plik." _ + vbNewLine + "2.Ukryj plik.") 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 (*.wav)| *.wav" If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then If zad = 2 Then ''Ukrywanie pliku Dim File_bytes As Byte() = IO.File.ReadAllBytes(open_dialog.FileName) Dim plikDoUkrycia As New OpenFileDialog() plikDoUkrycia.InitialDirectory = "c:\" plikDoUkrycia.Filter = "All Files|*.*" If plikDoUkrycia.ShowDialog() = System.Windows.Forms.DialogResult.OK Then Dim File_DoUkrycia As Byte() = IO.File.ReadAllBytes(plikDoUkrycia.FileName) 'tworzymy nagłówek Junk wraz z wielkością pliku ukrywanego Dim naglowekJunk() As Byte = System.Text.Encoding.ASCII.GetBytes("JUNK") Dim wielkoscJunk() As Byte = System.BitConverter.GetBytes(File_DoUkrycia.Count) 'kopiujemy poczatek bajtów pliku oryginalnego (12 bajtów nagłówka RIFF Dim poczatek(12) As Byte Array.Copy(File_bytes, poczatek, 12) 'Tworzy nową tablicę o wielkośći połączonych ilości bajtów pliku kontenera, ' pliku ukrywanego i 8 bajtów dla slowa JUNK i wielkości dla pliku ukrywanego Dim nowyplik(File_bytes.Count + File_DoUkrycia.Count + 7) As Byte 'kopiuje RIFF Array.Copy(poczatek, nowyplik, poczatek.Count) 'kopiuje JUNK i wielkość pliku ukrywanego Array.Copy(naglowekJunk, 0, nowyplik, 12, naglowekJunk.Count) Array.Copy(wielkoscJunk, 0, nowyplik, 16, wielkoscJunk.Count) 'kopiuje bajty pliku ukrywanego Array.Copy(File_DoUkrycia, 0, nowyplik, 20, File_DoUkrycia.Count) 'kopiuje reszte bajtów kontenera Array.Copy(File_bytes, 12, nowyplik, 20 + File_DoUkrycia.Length, File_bytes.Count - 12) Console.Write("Podaj nazwę pliku pod którym chcesz zapisać plik wav: ") Dim nazwapliku As String = Console.ReadLine() IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku + ".wav", nowyplik) Console.WriteLine("Bajty zostały ukryte. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku + ".wav") End If Console.ReadLine() Else 'przygotowanie pliku Dim listaBajtowPlikuUkrytego As New List(Of Byte) Dim wielkoscPliku As Integer = 0 'otwórz wskazany plik Dim wavefile() As Byte = IO.File.ReadAllBytes(open_dialog.FileName) Dim memstream As New IO.MemoryStream(wavefile) 'czytaj wszystkie bajty pliku Using binreader As New IO.BinaryReader(memstream) Do 'czytaj do czasu aż natrafisz na 'JUNK' lub 'junk' Dim Junk As String = BitConverter.ToString(binreader.ReadBytes(4), 0) If (Junk = "6A-75-6E-6B" Or Junk = "4A-55-4E-4B") Then 'pobierz wielkość pliku ukrytego wielkoscPliku = BitConverter.ToInt32(binreader.ReadBytes(4), 0) Exit Do End If Loop 'zapisz bajty pliku ukrytego do listy For i As Integer = 0 To wielkoscPliku - 1 listaBajtowPlikuUkrytego.Add(binreader.ReadByte) Next End Using 'stwórz nową tablicę Dim nowyplik() As Byte = listaBajtowPlikuUkrytego.ToArray() 'Podaj nazwę pliku Console.WriteLine() Console.Write("Podaj nazwę pliku pod którycm chcesz zapisać plik: ") Dim nazwapliku As String = Console.ReadLine() 'zapisz plik IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku, nowyplik) Console.WriteLine("Bajty zostały odzyskane. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku) Console.ReadLine() End If End If End Sub End Module |
Taki program pobiera bajty pliku kontenera, oddziela dwanaście pierwszych bajtów i dodaje cztery bajty JUNK, wielkość bajtów pliku ukrytego, a następnie bajty ukrytego pliku i resztę bajtów kontenera. Pierwsza opcja pozwala na pobranie i zapis tych bajtów. Jeśli plik ukrywany nie jest duży, może zdezorientować analityka, który potraktuje te sekcje jako śmieci. Kolejną metodą dodania dodatkowych bajtów do kontenera jest przekształcenie nagłówka „fmt” przekształcając Subchunk1size, ExtraParamSize i ExtraParams:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
Imports System.Windows.Forms Module Module1 Sub Main() 'Otwiera dialog z użytkownikiem Console.WriteLine("Co chesz zrobić? Wpisz cyfrę zadania." + vbNewLine + "1.Wydobądź ukryty plik." _ + vbNewLine + "2.Ukryj plik.") 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 (*.wav)| *.wav" If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then If zad = 2 Then ''Ukrywanie pliku Dim File_bytes As Byte() = IO.File.ReadAllBytes(open_dialog.FileName) Dim plikDoUkrycia As New OpenFileDialog() plikDoUkrycia.InitialDirectory = "c:\" plikDoUkrycia.Filter = "All Files|*.*" If plikDoUkrycia.ShowDialog() = System.Windows.Forms.DialogResult.OK Then Dim File_DoUkrycia As Byte() = IO.File.ReadAllBytes(plikDoUkrycia.FileName) Dim poczatek(16) As Byte Array.Copy(File_bytes, poczatek, 16) Dim wielkoscPoczatkowa() As Byte = {File_bytes(16), File_bytes(17), File_bytes(18), File_bytes(19)} 'Jeśli plik nie zawiera dodatkowych danych (Subchunk1Size = 16) If BitConverter.ToString(wielkoscPoczatkowa, 0) = "10-00-00-00" Then 'pobiera początkowe 18 bajtów Dim wielkoscFMT() As Byte = System.BitConverter.GetBytes(File_DoUkrycia.Count + 18) wielkoscPoczatkowa = wielkoscFMT 'Tworzymy nową tablicę o wielkośc pliku i pliku ukrywanego + 2 bajty dla ExtraParamSize Dim nowyplik(File_bytes.Count + File_DoUkrycia.Count + 2) As Byte ' kopiujemy początek do noewgo pliku Array.Copy(poczatek, nowyplik, poczatek.Count) 'nadajemy nową wartość dla Subchunk1Size Array.Copy(wielkoscPoczatkowa, 0, nowyplik, 16, wielkoscPoczatkowa.Count) 'dodajemy 16 bajtów nagłówka fmt Array.Copy(File_bytes, 20, nowyplik, 20, 16) 'tworzymy wielkość dla ExtraParamSize Dim wielkoscPliku As Short Try 'wielkość ta jes dwu bajtowa więc nasz pli nie może mieć więcej niż (FF FF) bajtów czyli 65535 wielkoscPliku = File_DoUkrycia.Length Catch ex As Exception Console.WriteLine("Twój plik jest za duży. Można ukryć tylko pliki nie większe niż: " + Short.MaxValue.ToString + " bajtów" + vbNewLine + " twój plik zawiera: " + File_DoUkrycia.Length.ToString + " bajtów") Exit Sub End Try 'dodajemy ExtraParamSize Array.Copy(System.BitConverter.GetBytes(wielkoscPliku), 0, nowyplik, 36, 2) 'dodajemy bajty pliku do ukrycia Array.Copy(File_DoUkrycia, 0, nowyplik, 38, File_DoUkrycia.Count) 'dodajemy resztę bajtów pliku kontenera Array.Copy(File_bytes, 36, nowyplik, 38 + File_DoUkrycia.Length, File_bytes.Count - 36) 'zapisujemy plik Console.Write("Podaj nazwę pliku pod którym chcesz zapisać plik wav: ") Dim nazwapliku As String = Console.ReadLine() IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku + ".wav", nowyplik) Console.WriteLine("Bajty zostały ukryte. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku + ".wav") Else Console.WriteLine("Twój plik już zawieje dodatkowe parametry.") End If End If Console.ReadLine() Else 'przygotowanie pliku Dim listaBajtowPlikuUkrytego As New List(Of Byte) Dim wavefile() As Byte = IO.File.ReadAllBytes(open_dialog.FileName) Dim wielkoscPoczatkowa() As Byte = {wavefile(16), wavefile(17), wavefile(18), wavefile(19)} 'sprawdzamy czy Subchunk1Size = 16 jesli nie to If Not BitConverter.ToString(wielkoscPoczatkowa, 0) = "10-00-00-00" Then 'pobieramy wielkość ukrytego pliku Dim wielkoscPlikuUkrytego As Short = BitConverter.ToInt16({wavefile(36), wavefile(37)}, 0) 'dodajemy bajty ukrytego pliku do listy For i As Integer = 38 To 38 + wielkoscPlikuUkrytego - 1 listaBajtowPlikuUkrytego.Add(wavefile(i)) Next End If 'tworzymy nową tablicę Dim nowyplik() As Byte = listaBajtowPlikuUkrytego.ToArray() 'Podaj nazwę pliku Console.WriteLine() Console.Write("Podaj nazwę pliku pod którycm chcesz zapisać plik: ") Dim nazwapliku As String = Console.ReadLine() 'zapisz plik IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku, nowyplik) Console.WriteLine("Bajty zostały odzyskane. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku) Console.ReadLine() End If End If End Sub End Module |
Zasada działania jest taka sama jak przy dodawaniu nagłówka JUNK, w tym wypadku pobieramy bajty pliku i modyfikujemy obszary, aby następnie dodać dwa bajty wielkości tekstu/pliku i jego bajty. Wiemy już gdzie ukryć dane w strukturze pliku. Zajmijmy się samym dźwiękiem. Jak już wcześniej wspomniałem pliki w formacie WAVE, zawierają dane 8, 16, 24, lub 32 bitowe. Gdzie 8 bitowych plików WAVE już się nie używa. Zmiana dźwięku poprzez zerowanie LSB będzie najbardziej efektywna, gdy będziemy mieli do czynienia z plikami 32 bitowymi, ponieważ ich modyfikacje nie zmienią znacząco dźwięku, gdyż jego długość jest dosyć duża:
16 bit = 00 00 do FF FF (0 do 65535)
24 bit = 00 00 00 do FF FF FF (0 do 16777215)
32 bity = 00 00 00 00 do FF FF FF FF (0 do 4294967295)
Zapożyczenie, nawet 2 bajtów w przypadku pliku 32 bitowego, nie pogorszy znacząco jego jakości. Przygotowałem aplikację, która prezentuje zmianę dźwięk po wyzerowaniu wskazanej ilości bajtów: program zerujący bity w pliku WAV
Uwaga, program może mieć problem z rysowaniem histogramu dla 32 bitowych plików WAV. Spowodowane jest to dużą skalą, która może w takim pliku występować. Histogram rysowany przez program jest tylko pomocniczy, aby w pełni zaobserwować zmiany dokonane na pliku, zapisz jego zmodyfikowaną wersje przy użyciu przycisku „Zapisz”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
Imports System.Windows.forms Imports System.Drawing Module Module1 Dim pictureboxKontener As New PictureBox Dim buttonKontenera As New Button Dim label1 As New Label Dim trackbar1 As New TrackBar Dim pictureboxGenerowany As New PictureBox Dim buttonGeneratora As New Button Dim buttonZapisz As New Button Dim panel1 As New Panel Dim panel2 As New Panel Dim bajtyNaglowka As New List(Of Byte) Dim DaneOryginalne As New List(Of UInteger) Dim DaneLSB As New List(Of UInteger) Dim najwyzszaWartoscDzwieku As Integer = 0 Dim iloscBitow As Short Private Sub WygenerujForme() Dim form1 As New Form form1.StartPosition = FormStartPosition.CenterScreen form1.Size = New System.Drawing.Size(371, 698) panel1.Size = New System.Drawing.Size(355, 240) panel1.Location = New System.Drawing.Point(0, 0) panel1.AutoScroll = True panel1.Anchor = AnchorStyles.Left Or AnchorStyles.Top Or AnchorStyles.Right form1.Controls.Add(panel1) buttonKontenera.Location = New System.Drawing.Point(5, 246) buttonKontenera.Name = "buttonKontenera" buttonKontenera.Size = New System.Drawing.Size(75, 23) buttonKontenera.TabIndex = 1 buttonKontenera.Text = "Play" AddHandler buttonKontenera.Click, AddressOf GrajOryginal form1.Controls.Add(buttonKontenera) label1.Location = New System.Drawing.Point(12, 287) label1.Name = "label1" label1.AutoSize = False label1.Size = New System.Drawing.Size(331, 23) label1.TabIndex = 2 label1.Text = "Wyzerowano 0 ostatnich bitów , to daje: 0 wojnych Bajtów." form1.Controls.Add(label1) trackbar1.Location = New System.Drawing.Point(0, 316) trackbar1.Name = "trackbar1" trackbar1.RightToLeft = RightToLeft.Yes trackbar1.Size = New System.Drawing.Size(355, 45) If iloscBitow = 16 Then trackbar1.Maximum = 14 ElseIf iloscBitow = 24 Then trackbar1.Maximum = 22 ElseIf iloscBitow = 32 Then trackbar1.Maximum = 30 End If trackbar1.TabIndex = 3 AddHandler trackbar1.ValueChanged, AddressOf TrackBar1_ZmianaWartosci form1.Controls.Add(trackbar1) panel2.Size = New System.Drawing.Size(355, 240) panel2.Location = New System.Drawing.Point(0, 367) panel2.AutoScroll = True panel2.Anchor = AnchorStyles.Left Or AnchorStyles.Top Or AnchorStyles.Right form1.Controls.Add(panel2) buttonGeneratora.Location = New System.Drawing.Point(5, 626) buttonGeneratora.Name = "buttonGeneratora" buttonGeneratora.Size = New System.Drawing.Size(75, 23) buttonGeneratora.TabIndex = 5 buttonGeneratora.Text = "Play" AddHandler buttonGeneratora.Click, AddressOf GrajWygenerownyDzwiek form1.Controls.Add(buttonGeneratora) buttonZapisz.Location = New System.Drawing.Point(85, 626) buttonZapisz.Name = "buttonZapisz" buttonZapisz.Size = New System.Drawing.Size(75, 23) buttonZapisz.TabIndex = 6 buttonZapisz.Text = "Zapisz" AddHandler buttonZapisz.Click, AddressOf ZapiszKlik form1.Controls.Add(buttonZapisz) form1.ShowDialog() End Sub Sub Main() Dim open_dialog As New OpenFileDialog() open_dialog.InitialDirectory = "c:\" open_dialog.Filter = "Image Files (*.wav)|*.wav" open_dialog.Title = "Wskaż plik dźwiękowy" If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then PobierzBajty(open_dialog.FileName) WygenerujForme() End If End Sub Private Sub PobierzBajty(ByVal FileName As String) Dim wavefile() As Byte = IO.File.ReadAllBytes(FileName) Dim memstream As New IO.MemoryStream(wavefile) Using binreader As New IO.BinaryReader(memstream) bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' ChunkID bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' filesize bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' rifftype ' fmtID (czasem w plikach Wave znajduje się dodatkowy nagłówek zwany JUNK, jeśli istnieje, należy jego dane dodać lub usunąć) 'dodawanie Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "66-6D-74-20" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' fmtsize Dim fmtsize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' fmtcode bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' fmtcode bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' samplerate bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' fmtAvgBPS bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' fmtblockalign bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' bitdepth iloscBitow = BitConverter.ToInt16({bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) If BitConverter.ToUInt32(fmtsize, 0) = 18 Then bajtyNaglowka.AddRange(binreader.ReadBytes(2)) End If ' DataID' Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "64-61-74-61" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' DataSize Dim DataSize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3) _ , bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} 'ładuje odpowiednio duże dane do listy If iloscBitow = 16 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 2) - 1 DaneOryginalne.Add(binreader.ReadUInt16()) Next ElseIf iloscBitow = 32 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 4) - 1 DaneOryginalne.Add(binreader.ReadUInt32()) Next ElseIf iloscBitow = 24 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 3) - 1 Dim DataSize2(3) As Byte DataSize2(3) = 0 DataSize2(0) = binreader.ReadByte DataSize2(1) = binreader.ReadByte DataSize2(2) = binreader.ReadByte DaneOryginalne.Add(BitConverter.ToUInt32(DataSize2, 0)) Next End If End Using OryginalHistogram() End Sub Private Sub OryginalHistogram() Dim przeskok As Integer pictureboxKontener.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle pictureboxKontener.Location = New System.Drawing.Point(0, 0) pictureboxKontener.Name = "Kontener" If Not iloscBitow = 32 Then pictureboxKontener.Size = New System.Drawing.Size((DaneOryginalne.Count - 1) / 2, 220) przeskok = 1 Else pictureboxKontener.Size = New System.Drawing.Size((DaneOryginalne.Count - 1) / 2000, 220) przeskok = (DaneOryginalne.Count - 1) / 2000 End If pictureboxKontener.TabIndex = 0 pictureboxKontener.TabStop = False panel1.Controls.Add(pictureboxKontener) Dim obraz As New Bitmap(pictureboxKontener.Width, 240) Dim gfx As Graphics = Graphics.FromImage(obraz) 'Stwórz grafikę kwadratu ktory posłuży za tło gfx.FillRectangle(Brushes.White, 0, 0, obraz.Width, obraz.Height) 'ustawiamy przeskok tak aby zmieściły nam się dane (2*szerokość obrazka) Dim polowa As Integer = (obraz.Height / 2) 'ustalamy połowe wysokości obrazka If Not iloscBitow = 32 Then For i = 1 To obraz.Width - 10 If DaneOryginalne(i * przeskok) > najwyzszaWartoscDzwieku Then 'wymagane do stworzenia histogramu najwyzszaWartoscDzwieku = DaneOryginalne(i * przeskok) End If Next Else For i = 1 To obraz.Width - 10 If DaneOryginalne(i * przeskok) > najwyzszaWartoscDzwieku Then 'wymagane do stworzenia histogramu If DaneOryginalne(i * przeskok) > Integer.MaxValue Then najwyzszaWartoscDzwieku = Integer.MaxValue / 4 Exit For Else najwyzszaWartoscDzwieku = DaneOryginalne(i * przeskok) End If End If Next End If For i As UInteger = 1 To obraz.Width - 10 Step 2 'ponieważ w jednym ruchu rysujemy dwie linie Dim leftdata As UInteger = Math.Abs(DaneOryginalne(i * przeskok)) 'podejmujemy (i* przeskok) element 'procent jaki osiąga dzwięk w stosunku do najdłuższego znalezionego dźwięku Dim leftpercent As Double = leftdata / (najwyzszaWartoscDzwieku * 2) 'otrzymujemy procent połowy wysokości obrazka odpowiadającej długości dźwięku Dim leftpicheight As Integer = leftpercent * obraz.Height gfx.DrawLine(Pens.Green, i, polowa, i, leftpicheight + polowa) 'generuje grafikę słupka w górę 'podejmujemy ((i+1)* przeskok) element (i+1 ponieważ mamy "step 2" w pętli Dim rightdata As UInteger = Math.Abs(DaneOryginalne((i + 1) * przeskok)) Dim rightpercent As Double = -rightdata / (najwyzszaWartoscDzwieku * 2) 'rysuje słupek w dół Dim rightpicheight As Integer = rightpercent * obraz.Height gfx.DrawLine(Pens.Black, i, polowa, i, rightpicheight + polowa) 'generuje grafikę słupka w dół Next pictureboxKontener.Image = obraz End Sub Private Sub GrajOryginal() My.Computer.Audio.Stop() Dim oryginal As Byte() Dim listaPolaczona As New List(Of Byte) listaPolaczona.AddRange(bajtyNaglowka) If iloscBitow = 16 Then For i As Integer = 0 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) Next ElseIf iloscBitow = 32 Then For i As Integer = 0 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) listaPolaczona.Add(test(3)) Next ElseIf iloscBitow = 24 Then For i As Integer = 0 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) Next End If oryginal = listaPolaczona.ToArray() My.Computer.Audio.Play(oryginal, AudioPlayMode.Background) End Sub Dim LISTA As New List(Of String) Private Sub TrackBar1_ZmianaWartosci() DaneLSB.Clear() LISTA.Clear() For i As UInteger = 0 To DaneOryginalne.Count - 1 Dim wartosc As UInteger = DaneOryginalne(i) - (DaneOryginalne(i) Mod (2 ^ trackbar1.Value)) Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) Dim test2 As Byte() = BitConverter.GetBytes(wartosc) Dim string1 As String = BitConverter.ToString(test).ToString Dim testOdwrocony As String() = string1.Split("-") Dim dolisty1 As String = testOdwrocony(3) + "-" + testOdwrocony(2) + "-" + testOdwrocony(1) + "-" + testOdwrocony(0) Dim string2 As String = BitConverter.ToString(test2).ToString Dim testOdwrocony2 As String() = string2.Split("-") Dim dolisty2 As String = testOdwrocony2(3) + "-" + testOdwrocony2(2) + "-" + testOdwrocony2(1) + "-" + testOdwrocony2(0) Dim test3 As UInteger = DaneOryginalne(i) Mod (2 ^ trackbar1.Value) LISTA.Add(DaneOryginalne(i).ToString + " (" + dolisty1 + ") -" + test3.ToString + " =" + (DaneOryginalne(i) - test3).ToString + " (" + dolisty2 + ")" + " (" + Convert.ToString(test2(3), 2).PadLeft(8, "0") + "-" + Convert.ToString(test2(2), 2).PadLeft(8, "0") _ + "-" + Convert.ToString(test2(1), 2).PadLeft(8, "0") + "-" + Convert.ToString(test2(0), 2).PadLeft(8, "0") + ")") DaneLSB.Add(DaneOryginalne(i) - (DaneOryginalne(i) Mod (2 ^ trackbar1.Value))) Next label1.Text = "Wyzerowano " + trackbar1.Value.ToString + " ostatnich bitów, to daje: " + (Math.Floor((DaneOryginalne.Count / 8) * trackbar1.Value).ToString + " wojnych Bajtów.") GenerowanyHistogram() End Sub Private Sub GenerowanyHistogram() panel2.Controls.Clear() Dim przeskok As Integer pictureboxGenerowany.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle pictureboxGenerowany.Location = New System.Drawing.Point(0, 0) pictureboxGenerowany.Name = "pictureboxGenerowany" If Not iloscBitow = 32 Then pictureboxGenerowany.Size = New System.Drawing.Size((DaneOryginalne.Count - 1) / 2, 220) przeskok = 1 Else pictureboxGenerowany.Size = New System.Drawing.Size((DaneOryginalne.Count - 1) / 2000, 220) przeskok = (DaneOryginalne.Count - 1) / 2000 End If pictureboxGenerowany.TabIndex = 4 pictureboxGenerowany.TabStop = False panel2.Controls.Add(pictureboxGenerowany) Dim obraz As New Bitmap(pictureboxGenerowany.Width, 240) Dim gfx As Graphics = Graphics.FromImage(obraz) 'Stwórz grafikę kwadratu ktory posłuży za tło gfx.FillRectangle(Brushes.White, 0, 0, obraz.Width, obraz.Height) Dim polowa As Integer = (obraz.Height / 2) For i As UInteger = 1 To obraz.Width - 10 Step 2 'ponieważ w jednym ruchu rysujemy dwie linie Dim leftdata As UInteger = Math.Abs(DaneLSB(i * przeskok)) Dim leftpercent As Single = leftdata / (najwyzszaWartoscDzwieku * 2) Dim leftpicheight As Integer = leftpercent * obraz.Height gfx.DrawLine(Pens.Green, i, polowa, i, leftpicheight + polowa) Dim rightdata As UInteger = Math.Abs(DaneLSB((i + 1) * przeskok)) Dim rightpercent As Single = -rightdata / (najwyzszaWartoscDzwieku * 2) Dim rightpicheight As Integer = rightpercent * obraz.Height gfx.DrawLine(Pens.Black, i, polowa, i, rightpicheight + polowa) Next pictureboxGenerowany.Image = obraz End Sub Private Sub GrajWygenerownyDzwiek() My.Computer.Audio.Stop() Dim Wygenerowany As Byte() Dim listaPolaczona As New List(Of Byte) listaPolaczona.AddRange(bajtyNaglowka) If iloscBitow = 16 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) Next ElseIf iloscBitow = 32 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) listaPolaczona.Add(test(3)) Next ElseIf iloscBitow = 24 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) Next End If Wygenerowany = listaPolaczona.ToArray() My.Computer.Audio.Play(Wygenerowany, AudioPlayMode.Background) End Sub Private Sub ZapiszKlik() Dim Wygenerowany As Byte() Dim listaPolaczona As List(Of Byte) = bajtyNaglowka If iloscBitow = 16 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) Next ElseIf iloscBitow = 32 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) listaPolaczona.Add(test(3)) Next ElseIf iloscBitow = 24 Then For i As Integer = 0 To DaneLSB.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneLSB(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) Next End If Wygenerowany = listaPolaczona.ToArray() Dim saveFileDialog1 As New SaveFileDialog() saveFileDialog1.Title = "Save an wav File" saveFileDialog1.Filter = "Wav Files | *.wav" saveFileDialog1.DefaultExt = "wav" saveFileDialog1.ShowDialog() If saveFileDialog1.FileName <> "" Then IO.File.WriteAllBytes(saveFileDialog1.FileName, Wygenerowany) Dim sciezka As String = IO.Path.GetDirectoryName(saveFileDialog1.FileName) _ + "\" + IO.Path.GetFileNameWithoutExtension(saveFileDialog1.FileName) IO.File.WriteAllLines(sciezka + "_listabitow.txt", LISTA) End If End Sub End Module |
Aplikacja jest dosyć rozbudowana, ale jej działanie jest bardzo proste. Pobiera bajty pliku i segreguje je, umieszczając w odpowiednich listach, aby po modyfikacji można było połączyć je w całość i odtworzyć (wyświetlić histogram). Pliki, na których ja testowałem algorytmy::
Jeśli wyzerujemy ostatnie 8 bitów w 16 bitowym pliku WAVE, zmiana będzie niezauważalna (w dźwięku). Dopiero wyzerowanie więcej niż 10 bitów zmienia zauważalnie jakość dźwięku:
Procedura zerowania 8 bitów dla 16-bitowego pliku WAVe wygląda następująco:
1 2 |
DaneOryginalne(i) - (DaneOryginalne(i) Mod (2 ^ trackbar1.Value)) 'w naszym wypadku trackbar1.Value =8 |
Odpowiednio, dla pliku 16, 24, 32 bitowego( do pobrania na początku artykułu) zerowanie przebiega następująco:
ZerowanieOstatniegoBajtaW16bitowejPróbce –> plik audio po zerowaniu ostatniego bajta: ZerowanieOstatniegoBajtaW16bitowejPróbce
ZerowanieOstatniegoBajtaW24bitowejPróbce –> plik audio po zerowaniu ostatniego bajta: ZerowanieOstatniegoBajtaW24bitowejPróbce
ZerowanieOstatniegoBajtaW32bitowejPróbce –> plik audio po zerowaniu ostatniego bajta: ZerowanieOstatniegoBajtaW32bitowejPróbce
Zachęcam was do przeprowadzenia takiego eksperymentu, przy użyciu algorytmu i sprawdzenia ich na własnych uszach. Ukrycie pliku w takim nośniku nie powinno sprawić wam problemu. Tak jak w przypadku plików graficznych w formacie bmp, zerujemy bity pliku dźwiękowego (kontener) i ukrywamy tam bity naszego pliku. Aby, nie zerować wszystkich bitów danych pliku WAV użyjemy czterech pierwszych bajtów danych do zapisania w nich wielkości pliku, warto również użyć formatu danych Uinteger(dlaczego, dowiesz się, rozwijając zakładkę projektu). Będzie to wyglądało następująco:
Plik WAVE 16-bitowy z ukrytym tekstem (ukryty plik) :
Jeśli już rozbieraliście na części plik WAV, na pewno zauważyliście, że danych dźwiękowych jest bardzo dużo, więc z plik ukrywany może być znacząco mniejszy niż ilość danych dźwiękowych. Toteż ukrywanie wszystkich bajtów pliku tajnego na początku danych kontenera może spowodować słyszalny szum, dużo lepszą metodą jest podzielenie ilości dostępnych bajtów przez ilość bajtów pliku tajnego i zapisanie jego bajtów, w co piątym, szóstym czy siódmym elemencie:
Znając ilość ukrytych danych (pierwsze cztery bajty danych kontenera) bajty można rozłożyć w kontenerze tak jak na obrazku powyrzej.
plik ukryty w kontenerze: ukryty plik2
do odsłuchania, plik z ukrytymi danymi (16-bit wave z ukrytym plikiem „ukryty plik2”):
Przykładowy algorytm ukrywający bajty pliku w kontenerze WAV:
Już na wstępie chciałbym zaznaczyć, że utworzenie programu do szyfrowania bajtów pliku dźwiękowego będzie trochę trudniejsze niż w przypadku plików graficznych. Problem może sprawić to, że operujemy na więcej niż jednym bajcie. Program pobiera dwa, trzy lub cztery bajty i zamienia je na Uint (zakres integer jest od -2147483648 do 2147483647 (7F-FF-FF-FF) a Uint od 0 do 4294967295 (FF-FF-FF-FF)), jak łatwo się domyślić cztery bajty przyjmują zakres od 00-00-00-00 do FF-FF-FF-FF, a więc wszelkiego rodzaju zamiana czterech czy dwóch bajtów na liczbę naturalną będzie wygodniejsza dla Uint. Jeśli wartość bajtów przekroczy maksymalną wartość integer, wtedy liczna naturalna przyjmuje wartość ujemną, a dodawanie czy odejmowanie ich wartości nie będzie miało sensu. Program poniżej przeprowadza edycje bajtów pliku WAV, wyodrębnia z niego poszczególne elementy i je modyfikuje:
Z uwagi na to, że nie ma w vb.net czegoś takiego jak Uint24, pobieramy trzy bajty i dodajemy zerowy bajt z przodu i zamieniamy cztery bajty na Uint32
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
Imports System.Windows.Forms Module Module1 Sub Main() Dim open_dialog As New OpenFileDialog() Dim open_dialog_ukrywany As New OpenFileDialog() open_dialog.InitialDirectory = "c:\" open_dialog.Filter = "Image Files (*.wav)| *.wav" open_dialog.Title = "Wskarz kontener w formacie WAV w którym ukryjesz plik" If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then open_dialog_ukrywany.InitialDirectory = "c:\" open_dialog_ukrywany.Filter = "All Files|*.*" open_dialog_ukrywany.Title = "Wybierz plik który chcesz ukryć" If open_dialog_ukrywany.ShowDialog() = System.Windows.Forms.DialogResult.OK Then Dim adresKontenera As String = open_dialog.FileName Dim adreskUkrywany As String = open_dialog_ukrywany.FileName Dim DaneOryginalne As New List(Of UInteger) Dim iloscBitow As Integer = 0 Dim bajtyNaglowka As New List(Of Byte) Dim plikUkrywany() As Byte = IO.File.ReadAllBytes(adreskUkrywany) Dim wavefile() As Byte = IO.File.ReadAllBytes(adresKontenera) Dim memstream As New IO.MemoryStream(wavefile) '''''obszar oddziela nagłówek od danych Using binreader As New IO.BinaryReader(memstream) ' DataID' bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' ChunkID Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "66-6D-74-20" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' fmtsize Dim fmtsize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} bajtyNaglowka.AddRange(binreader.ReadBytes(14)) ' fmtcode bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' bitdepth iloscBitow = BitConverter.ToInt16({bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) If BitConverter.ToInt32(fmtsize, 0) = 18 Then bajtyNaglowka.AddRange(binreader.ReadBytes(2)) End If ' DataID' Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "64-61-74-61" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' DataSize Dim DataSize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} 'ładuje odpowiednio duże dane do listy If iloscBitow = 16 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 2) - 1 DaneOryginalne.Add(binreader.ReadUInt16()) Next ElseIf iloscBitow = 32 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 4) - 1 DaneOryginalne.Add(binreader.ReadUInt32()) Next ElseIf iloscBitow = 24 Then For i = 0 To (BitConverter.ToUInt32(DataSize, 0) / 3) - 1 Dim DataSize2(3) As Byte DataSize2(3) = 0 DataSize2(0) = binreader.ReadByte DataSize2(1) = binreader.ReadByte DataSize2(2) = binreader.ReadByte DaneOryginalne.Add(BitConverter.ToUInt32(DataSize2, 0)) Next End If End Using ''''' 'sparwdza czy plik ukrywany ma mniej danych niż kontener If Not plikUkrywany.Length + 2 > DaneOryginalne.Count Then Dim przeskok As UInteger = 1 Dim listaPolaczona As List(Of Byte) = bajtyNaglowka Dim wielkoscpliku As UInteger = plikUkrywany.Length If iloscBitow = 16 Then 'dodaje bajty wielkości pliku listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(0)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(1)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(2)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(3)) DaneOryginalne.RemoveAt(0) 'usuwa pierwszy wiersz (ułatwia to pracę algorytmu) przeskok = Math.Floor(((DaneOryginalne.Count - 1) / plikUkrywany.Length)) 'algorytm ukrywający bajty For i As UInteger = 0 To plikUkrywany.Length - 1 DaneOryginalne((i + 1) * przeskok) = (DaneOryginalne((i + 1) * przeskok) - (DaneOryginalne((i + 1) * przeskok) Mod (2 ^ 8))) + Convert.ToInt32(plikUkrywany(i)) Next Else przeskok = Math.Floor(((DaneOryginalne.Count - 1) / plikUkrywany.Length)) 'algorytm ukrywający bajty For i As UInteger = 0 To plikUkrywany.Length - 1 DaneOryginalne((i + 1) * przeskok) = (DaneOryginalne((i + 1) * przeskok) - (DaneOryginalne((i + 1) * przeskok) Mod (2 ^ 8))) + Convert.ToInt32(plikUkrywany(i)) Next End If Dim Wygenerowany As Byte() 'zespalanie pliku If iloscBitow = 16 Then For i As UInteger = 1 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) Next ElseIf iloscBitow = 32 Then 'dodaje bajty wielkości pliku listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(0)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(1)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(2)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(3)) For i As UInteger = 1 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.AddRange(test.ToList) Next ElseIf iloscBitow = 24 Then 'dodaje bajty wielkości pliku listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(0)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(1)) listaPolaczona.Add(BitConverter.GetBytes(wielkoscpliku)(2)) For i As UInteger = 1 To DaneOryginalne.Count - 1 Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i)) listaPolaczona.Add(test(0)) listaPolaczona.Add(test(1)) listaPolaczona.Add(test(2)) Next End If Wygenerowany = listaPolaczona.ToArray() Console.WriteLine() Console.Write("Podaj nazwę nowego pliku: ") Dim nazwapliku As String = Console.ReadLine() 'zapisz plik IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku + ".wav", Wygenerowany) Console.WriteLine("Bajty zostały ukryte. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku) Console.ReadLine() Else Console.WriteLine("Twój kontener jest zbyt mały.") Console.WriteLine("Bajty kontenera: " + DaneOryginalne.Count.ToString) Console.WriteLine("Bajty pliku ukrywanego: " + plikUkrywany.Length.ToString) Console.WriteLine("Wybierz większy plik kontenera.") Console.ReadLine() End If End If End If End Sub End Module |
1 |
Jeśli wiemy jak dodawać bajty, ich wyciągnięcie nie stanowi problemu, z bajtów danych pobieramy informację na temat wielkości pliku i na tej podstawie wyodrębniamy bajty..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
Imports System.Windows.Forms Module Module1 Sub Main() Dim open_dialog As New OpenFileDialog() open_dialog.InitialDirectory = "c:\" open_dialog.Filter = "Image Files (*.wav)| *.wav" open_dialog.Title = "Wskarz kontener w formacie WAV w którym ukryjesz plik" If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then Dim adresKontenera As String = open_dialog.FileName Dim DaneOryginalne As New List(Of Integer) Dim iloscBitow As Integer = 0 Dim bajtyNaglowka As New List(Of Byte) Dim wavefile() As Byte = IO.File.ReadAllBytes(adresKontenera) Dim memstream As New IO.MemoryStream(wavefile) Dim wielkoscpliku As Integer = 0 'Oddziela nagłówek od danych Using binreader As New IO.BinaryReader(memstream) ' DataID' bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' ChunkID Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "66-6D-74-20" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' fmtsize Dim fmtsize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} bajtyNaglowka.AddRange(binreader.ReadBytes(14)) ' fmtcode bajtyNaglowka.AddRange(binreader.ReadBytes(2)) ' bitdepth iloscBitow = BitConverter.ToInt16({bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) If BitConverter.ToInt32(fmtsize, 0) = 18 Then bajtyNaglowka.AddRange(binreader.ReadBytes(2)) End If ' DataID' Do Until BitConverter.ToString({bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)}, 0) = "64-61-74-61" bajtyNaglowka.Add(binreader.ReadByte) Loop bajtyNaglowka.AddRange(binreader.ReadBytes(4)) ' DataSize Dim DataSize As Byte() = {bajtyNaglowka(bajtyNaglowka.Count - 4), bajtyNaglowka(bajtyNaglowka.Count - 3), bajtyNaglowka(bajtyNaglowka.Count - 2), bajtyNaglowka(bajtyNaglowka.Count - 1)} 'ładuje odpowiednio duże dane do listy If iloscBitow = 16 Then DaneOryginalne.Add(binreader.ReadInt32()) For i = 2 To (BitConverter.ToInt32(DataSize, 0) / 2) - 1 DaneOryginalne.Add(binreader.ReadInt16()) Next ElseIf iloscBitow = 32 Then For i = 1 To (BitConverter.ToInt32(DataSize, 0) / 4) - 1 DaneOryginalne.Add(binreader.ReadInt32()) Next ElseIf iloscBitow = 24 Then For i = 1 To (BitConverter.ToInt32(DataSize, 0) / 3) - 1 Dim DataSize2(3) As Byte DataSize2(3) = 0 DataSize2(0) = binreader.ReadByte DataSize2(1) = binreader.ReadByte DataSize2(2) = binreader.ReadByte DaneOryginalne.Add(BitConverter.ToInt32(DataSize2, 0)) Next End If End Using Dim przeskok As Integer = 1 Dim test2 As Byte() = BitConverter.GetBytes(DaneOryginalne(0)) wielkoscpliku = BitConverter.ToInt32(test2, 0) If iloscBitow = 16 Then przeskok = Math.Floor(((DaneOryginalne.Count - 1) / wielkoscpliku)) Else przeskok = Math.Floor(((DaneOryginalne.Count - 1) / wielkoscpliku)) End If 'tworzymy nowy plik Dim Wygenerowany As Byte() Dim listaPolaczona As New List(Of Byte) If iloscBitow = 16 Then For i As Integer = 1 To wielkoscpliku Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i * przeskok)) listaPolaczona.Add(test(0)) Next ElseIf iloscBitow = 32 Then For i As Integer = 1 To wielkoscpliku Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i * przeskok)) listaPolaczona.Add(test(0)) Next ElseIf iloscBitow = 24 Then For i As Integer = 1 To wielkoscpliku Dim test As Byte() = BitConverter.GetBytes(DaneOryginalne(i * przeskok)) listaPolaczona.Add(test(0)) Next End If Wygenerowany = listaPolaczona.ToArray() Console.WriteLine() Console.Write("Podaj nazwę pliku pod którycm chcesz go zapisać: ") Dim nazwapliku As String = Console.ReadLine() 'zapisz plik IO.File.WriteAllBytes("C:\Users\piotr\Desktop\" + nazwapliku, Wygenerowany) Console.WriteLine("Bajty zostały odzyskane. Zapisano plik: " + "C:\Users\piotr\Desktop\" + nazwapliku) Console.ReadLine() End If End Sub End Module |
Plik wygenerowany, nie będzie miał rozszerzenia. Spowodowane jest to brakiem informacji (którą jeśli chcemy, możemy umieścić w kontenerze) o rodzaju pliku. Warto również połączyć możliwości JUNK i LSB.
Do przetestowania: Program_ukrywający_bajty_w_pliku_wav , Odzyskiwanie_ostatniego_bajta_z_pliku_WAV
(artykuł będzie rozwijany)