Steganogrāfija GIF

Ievads

Sveiki.
Nesen, mācoties universitātē, man bija kursa darbs par tēmu "Programmatiskās metodes informācijas aizsardzībai". Uzdevumā bija jāizveido programma, kas ievieto ziņu GIF formāta failos. Izvēlējos to veikt Java valodā.

Šajā rakstā es aprakstīšu dažus teorētiskos aspektus, kā arī to, kā tika izveidota šī nelielā programma.

Teorētiskā daļa

GIF formāts

GIF (angl. Graphics Interchange Format — attēlu apmaiņas formāts) — ir grafisko attēlu uzglabāšanas formāts, kas spēj uzglabāt saspiestas datu bez kvalitātes zuduma līdz 256 krāsām. Šo formātu 1987. gadā (GIF87a) izstrādāja uzņēmums CompuServe, lai pārraidītu rastra attēlus pa tīkliem. 1989. gadā formāts tika modificēts (GIF89a), pievienojot atbalstu caurspīdīguma un animācijas funkcijām.

GIF formāta faili ir strukturēti blokos. Šiem blokiem vienmēr ir fiksēta garuma (vai arī tā var būt atkarīga no dažiem karogiem), tādēļ kļūdīties, kur atrodas katrs bloks, ir praktiski neiespējami. Visvienkāršākā GIF89a formāta neanimēto GIF attēlu struktūra:

Steganogrāfija GIF

No visiem struktūras blokiem šajā gadījumā mūs interesē globālās paletes bloks un parametri, kas atbild par paleti:

  • CT — globālās paletes esamība. Ja šis karogs ir ieslēgts, tad tūlīt pēc loģiskā ekrāna apraksta jāsākas globālajai paletei.
  • Izmērs — paletes lielums un attēla krāsu skaits. Šī parametra vērtības:

Izmērs
Krāsu skaits
Paletes izmērs, baiti

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Šifrēšanas metodes

Kā ziņu šifrēšanas metodes attēlu failos tiks izmantotas:

  • LSB metode (Least Significant Bit, mazāk nozīmīgs bits)
  • Paletes papildināšanas metode

LSB metode — izplatīta steganogrāfijas metode. Tā sastāv no pēdējo nozīmīgo bitu maiņas konteinerā (mūsu gadījumā globālās paletes baitos) uz slēpto ziņu bitiem.

Programmā tiks izmantoti divi pēdējie biti globālās paletes baitos šīs metodes ietvaros. Tas nozīmē, ka 24 bitu attēlā, kur krāsu palete sastāv no trim baitiem sarkanai, zilai un zaļai krāsai, pēc ziņojuma ievadīšanas katra krāsu komponente tiks mainīta maksimums par 3/255 gradācijām. Šāda izmaiņa vispirms būs pamanāma vai grūti pamanāma cilvēka acij, un, otrkārt, nebūs atšķirama uz zemas kvalitātes informācijas izvadīšanas ierīcēm.

Informācijas daudzums būs tieši atkarīgs no attēla paletes izmēra. Tā kā maksimālais paletes izmērs ir 256 krāsas un, ja katras krāsas sastāvdaļā tiek ierakstīti divi ziņojuma biti, tad maksimālais ziņojuma garums (pie maksimālas paletes attēlā) ir 192 baudi. Pēc ziņojuma ievadīšanas attēlā faila izmērs nemainās.

Paletes paplašināšanas metode, kas darbojas tikai GIF struktūrā. Tā būs visefektīvākā attēlos ar maziem paletes izmēriem. Tās būtība ir tāda, ka tā palielina paletes izmēru, tādējādi nodrošinot papildu vietu nepieciešamo baitu ierakstīšanai krāsu baitos. Ņemot vērā, ka minimālais paletes izmērs ir 2 krāsas (6 baiti), maksimālais ievadītā ziņojuma izmērs var būt 256×3–6=762 baiti. Trūkums — zema šifrēšanas aizsardzība, ievadīto ziņojumu var izlasīt jebkurā teksta redaktora programmā, ja ziņojums nav pakļauts papildu šifrēšanai.

Praktiskā daļa

Programmas projektēšana

Visi nepieciešamie rīki šifrēšanas un atšifrēšanas algoritmu īstenošanai atradīsies paketē com.tsarik.steganography. Šajā paketē ietilpst saskarne Encryptor ar metodēm encrypt un decrypt, klase Binary, kas nodrošina iespēju strādāt ar bitu masīviem, kā arī izņēmumu klases UnableToEncryptException un UnableToDecryptException, kas būtu jāizmanto saskarnes metodēs Encryptor kļūdu gadījumā kodēšanā un dekodēšanā attiecīgi.

Programmas pamatpakete com.tsarik.programs.gifed ietver programmas izpildāmo klasi ar statisko metodi main, kas ļauj palaist programmu; klase, kurā tiek glabāti programmas parametri; un paketes ar citām klasēm.

Realizācija tieši pašiem algoritmiem tiks sniegta paketē com.tsarik.programs.gifed.gif klasēm GIFEncryptorByLSBMethod un GIFEncryptorByPaletteExtensionMethod. Abi šie klases īstenos interfeisu Encryptor.

Balstoties uz GIF formāta struktūru, var sastādīt vispārēju algoritmu ziņojuma ievietošanai attēla paletē:

Steganogrāfija GIF

Lai noteiktu ziņojuma klātbūtni attēlā, ir jāievada kāda bitu secība ziņojuma sākumā, kuru dekodētājs pirmkārt nolasīs un pārbaudīs uz pareizību. Ja tā nesakrīt, tad uzskata, ka attēlā nav slēpta ziņojuma. Pēc tam jānorāda ziņojuma garums. Tad pats ziņojuma teksts.

Visas aplikācijas klasifikācijas diagramma:

Steganogrāfija GIF

Programmas realizācija

Visas programmas realizāciju var sadalīt divās sastāvdaļās: šifrēšanas un atšifrēšanas metožu realizācija interfeisā Encryptor, klasēs GIFEncryptorByLSBMethod un GIFEncryptorByPaletteExtensionMethod, un lietotāja interfeisa realizācija.

Apskatīsim klasi GIFEncryptorByLSBMethod.

Steganogrāfija GIF

Lauki firstLSBit un secondLSBit satur katra attēla bita numurus, kuros jāievieto un no kuriem jālasī ziņojums. Lauks checkSequence uzglabā kontrolbitu secību, lai nodrošinātu iebūvētā ziņojuma atpazīšanu. Statiskā metode getEncryptingFileParameters atgriež norādītā faila parametrus un potenciālā ziņojuma īpašības.

Algoritms metodei encrypt klase GIFEncryptorByLSBMethod:

Steganogrāfija GIF

Un tās kods:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Ievades fails ir null");
	}
	if (out == null) {
		throw new NullPointerException("Izvades fails ir null");
	}
	if (text == null) {
		throw new NullPointerException("Teksts ir null");
	}
	
	// nolasīt bajtus no ievades faila
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// pārbaudīt formātu
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Ievades fails ir nepareizs GIF formāts");
	}
	
	// nolasīt paletes lieluma īpašību no pirmajām trim bitiem 10. baitā no faila
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// aprēķināt krāsu skaitu un iespējamo ziņojuma garumu
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // viens bajts kontrolei un viens bajts ziņojuma garumam
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Teksts ir pārāk liels");
	}
	
	int n = 13;
	
	// ierakstīt kontroles sekvenci
	for (int i = 0; i < checkSequence.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = checkSequence[2*i];
		ba[secondLSBit] = checkSequence[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// ierakstīt teksta garumu
	byte[] cl = Binary.toBitArray((byte)text.length());
	for (int i = 0; i < cl.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = cl[2*i];
		ba[secondLSBit] = cl[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// ierakstīt ziņojumu
	byte[] textBytes = text.getBytes();
	for (int i = 0; i < textBytes.length; i++) {
		byte[] c = Binary.toBitArray(textBytes[i]);
		for (int ci = 0; ci < c.length/2; ci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			ba[firstLSBit] = c[2*ci];
			ba[secondLSBit] = c[2*ci+1];
			bytes[n] = Binary.toByte(ba);
			n++;
		}
	}
	
	// ierakstīt izvades failu
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algoritms un tā metodika decrypt klase GIFEncryptorByLSBMethod:

Steganogrāfija GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Ievades fails ir null");
	}
	
	// nolasīt baitus no ievades faila
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// pārbaudīt formātu
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Ievades fails ir nepareizā GIF formātā");
	}
	
	// nolasīt paletes izmēra īpašību no pirmajiem trim bitiem 10. baitā
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// aprēķināt krāsu skaitu un iespējamo ziņojuma garumu
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// viens baits pārbaudei un viens baits ziņojuma garumam
	
	int n = 13;
	
	// nolasīt pārbaudes secību
	byte[] csBits = new byte[checkSequence.length];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		csBits[2*i] = ba[firstLSBit];
		csBits[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte cs = Binary.toByte(csBits);
	
	if (cs != Binary.toByte(checkSequence)) {
		throw new UnableToDecodeException("Attēlā nav šifrēta ziņa (pārbaudes secība ir nepareiza)");
	}
	
	// nolasīt teksta garumu
	byte[] cl = new byte[8];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		cl[2*i] = ba[firstLSBit];
		cl[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte textLength = Binary.toByte(cl);
	
	if (textLength < 0) {
		throw new UnableToDecodeException("Dekodētā teksta garums ir mazāks par 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Nav ziņojumu (Dekodētā ziņojuma garums (" + textLength + ") ir mazāks par iespējamo ziņojuma garumu (" + possibleTextLength + "))");
	}
	
	// nolasīt teksta bitus un veidot teksta baitus
	byte[] bt = new byte[textLength];
	for (int i = 0; i < bt.length; i++) {
		byte[] bc = new byte[8];
		for (int bci = 0; bci < bc.length/2; bci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			bc[2*bci] = ba[firstLSBit];
			bc[2*bci+1] = ba[secondLSBit];
			n++;
		}
		bt[i] = Binary.toByte(bc);
	}
	
	return new String(bt);
}

Klases īstenojums GIFEncryptorByPaletteExtensionMethod būs līdzīgs, vienīgi atšķiras informācijas saglabāšanas/nolasīšanas metode.

Klases MainFrame ir aprakstītas metodes-"apvalki": encryptImage(Encryptor encryptor) un decryptImage(Encryptor encryptor), kas apstrādā interfeisa metožu rezultātus Encryptor un veic mijiedarbību ar lietotāju, t.i. atver failu izvēles dialogu, parāda kļūdu ziņojumus u.c.; kā arī citas metodes: openImage(), dodot lietotājam iespēju izvēlēties attēlu, exit(), veicot izeju no lietojumprogrammas. Šīs metodes tiek izsauktas no Actionattiecīgajiem izvēlnes punktiem. Šajā klasē papildus ir īstenoti palīgmetodes: createComponents() — veidojot formas komponentus, loadImageFile(File f) — ielādējot attēlu īpašā komponentē no faila. Klases īstenojums GIFEncryptorByPaletteExtensionMethod ir līdzīgs klases GIFEncryptorByLSBMethod, galvenā atšķirība ir ziņojuma baitu rakstīšanas un lasīšanas metode no paletes.

Programmas darbība

Metode LBS

Pieņemsim, ka ir tāds attēls:

Steganogrāfija GIF

Šajā attēlā palete sastāv no 256 krāsām (tā saglabā Paint). Pirmās četras krāsas: balta, melna, sarkana, zaļa. Pārējās krāsas ir melnas. Globālās paletes bitu secība būs šāda:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Steganogrāfija GIF

Pēc ziņojuma iekļaušanas, uzsvērti biti tiks aizvietoti ar biti no ziņojuma. Iegūtais attēls gandrīz neatšķiras no oriģināla.

Oriģināls
Attēls ar iekļauto ziņojumu

Steganogrāfija GIF
Steganogrāfija GIF

Paletes paplašināšanas metode

Atverot attēlu, kurā ievietots ziņojums, izmantojot šo metodi, var redzēt šādu ainu:

Steganogrāfija GIF

Saprotams, ka šādai spiegošanai šī metode neder, un varbūt nepieciešama papildu ziņojuma šifrēšana.

Šifrēšana/atšifrēšana animētos attēlos darbojas tāpat kā parastos statiskajos attēlos, un animācija netiek traucēta.

Izmantotie avoti:

Lejupielādēt:

Avots: habr.com

Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster