Steganografija GIF formatu

įvedimas

Sveikinimai.
Ne taip seniai, kai studijavau universitete, buvo kursinis darbas iš disciplinos „Informacijos saugumo programinės įrangos metodai“. Užduočiai reikėjo sukurti programą, įterpiančią pranešimą į GIF failus. Aš nusprendžiau tai padaryti Java.

Šiame straipsnyje aprašysiu keletą teorinių punktų, taip pat kaip buvo sukurta ši nedidelė programa.

Teorinė dalis

GIF formatu

GIF (Graphics Interchange Format - keitimosi vaizdais formatas) yra grafinių vaizdų saugojimo formatas, galintis išsaugoti suglaudintus duomenis neprarandant kokybės iki 256 spalvų formatu. Šį formatą 1987 m. (GIF87a) sukūrė CompuServe, skirtą rastriniams vaizdams perduoti tinklais. 1989 m. buvo pakeistas formatas (GIF89a), pridėtas skaidrumo ir animacijos palaikymas.

GIF failai turi blokinę struktūrą. Šie blokai visada turi fiksuotą ilgį (arba tai priklauso nuo kai kurių vėliavėlių), todėl beveik neįmanoma suklysti, kur yra kiekvienas blokas. Paprasčiausio neanimuoto GIF vaizdo GIF89a formatu struktūra:

Steganografija GIF formatu

Iš visų struktūros blokų šiuo atveju mus sudomins pasaulinis paletės blokas ir parametrai, atsakingi už paletę:

  • CT - pasaulinės paletės buvimas. Jei ši vėliavėlė nustatyta, pasaulinė paletė turi prasidėti iškart po loginės ekrano rankenos.
  • Size - paletės dydis ir spalvų skaičius paveikslėlyje. Šio parametro reikšmės:

Dydis
Spalvų skaičius
Paletės dydis, baitai

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Šifravimo metodai

Norėdami užšifruoti vaizdo failų pranešimus, bus naudojami šie metodai:

  • LSB (Least Significant Bit) metodas
  • Paletės papildymo būdas

LSB metodas – paplitęs steganografijos metodas. Jį sudaro paskutinių reikšmingų bitų konteineryje (mūsų atveju pasaulinės paletės baitų) pakeitimas paslėpto pranešimo bitais.

Kaip šio metodo dalį programa naudos du paskutinius pasaulinės paletės baitų bitus. Tai reiškia, kad 24 bitų vaizde, kuriame spalvų paletė yra trys baitai raudonai, mėlynai ir žaliai, įterpus pranešimą, kiekvienas spalvos komponentas pasikeis daugiausiai 3/255 gradacijomis. Toks pokytis, pirma, bus nematomas arba sunkiai pastebimas žmogaus akiai, antra, jis nebus matomas nekokybiškuose informacijos išvedimo įrenginiuose.

Informacijos kiekis tiesiogiai priklausys nuo vaizdų paletės dydžio. Kadangi maksimalus paletės dydis yra 256 spalvos, o jei į kiekvienos spalvos komponentą įrašyti du pranešimo bitai, tai maksimalus pranešimo ilgis (su maksimalia palete paveikslėlyje) yra 192 baitai. Kai pranešimas įterpiamas į vaizdą, failo dydis nesikeičia.

Paletės išplėtimo būdas, kuris veikia tik GIF struktūrai. Tai bus efektyviausia vaizduose su maža palete. Jo esmė ta, kad jis padidina paletės dydį ir taip suteikia papildomos vietos reikalingiems baitams įrašyti vietoj spalvų baitų. Jei laikysime, kad minimalus paletės dydis yra 2 spalvos (6 baitai), tai didžiausias įterpto pranešimo dydis gali būti 256 × 3–6 = 762 baitai. Trūkumas yra mažas kriptografinis saugumas; įterptąjį pranešimą galima perskaityti naudojant bet kurį teksto rengyklę, jei pranešimas nebuvo papildomai šifruotas.

Praktinė dalis

Programos dizainas

Į paketą bus įtraukti visi reikalingi įrankiai šifravimo ir iššifravimo algoritmams įgyvendinti com.tsarik.steganography. Šiame pakete yra sąsaja Encryptor su metodais encrypt и decrypt, Klasė Binary, kuri suteikia galimybę dirbti su bitų masyvais, taip pat išimčių klasėmis UnableToEncryptException и UnableToDecryptException, kuris turėtų būti naudojamas sąsajos metoduose Encryptor esant atitinkamai kodavimo ir dekodavimo klaidoms.

Pagrindinis programos paketas com.tsarik.programs.gifed apims paleidžiamų programų klasę su statiniu metodu main, leidžianti paleisti programą; klasė, kurioje saugomi programos parametrai; ir paketus su kitomis klasėmis.

Pačių algoritmų įgyvendinimas bus pristatytas pakete com.tsarik.programs.gifed.gif klases GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Abi šios klasės įdiegs sąsają Encryptor.

Remdamiesi GIF formato struktūra, galite sukurti bendrą algoritmą, kaip įvesti pranešimą į vaizdo paletę:

Steganografija GIF formatu

Norint nustatyti pranešimo buvimą vaizde, pranešimo pradžioje reikia pridėti tam tikrą bitų seką, kurią dekoderis nuskaito pirmiausia ir patikrina teisingumą. Jei nesutampa, laikoma, kad paveikslėlyje nėra paslėptos žinutės. Toliau reikia nurodyti pranešimo ilgį. Tada pats pranešimo tekstas.

Visos programos klasės diagrama:

Steganografija GIF formatu

Programos įgyvendinimas

Visos programos įgyvendinimą galima suskirstyti į du komponentus: sąsajos šifravimo ir iššifravimo metodų įgyvendinimą Encryptor, pamokose GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, ir vartotojo sąsajos diegimas.

Apsvarstykite klasę GIFEncryptorByLSBMethod.

Steganografija GIF formatu

laukai firstLSBit и secondLSBit yra kiekvieno vaizdo baito, į kurį turi būti įvedamas pranešimas ir iš kur žinutė turi būti skaitoma, bitų skaičius. Laukas checkSequence saugo tikrinimo bitų seką, kad būtų užtikrintas įterpto pranešimo atpažinimas. Statinis metodas getEncryptingFileParameters grąžina nurodyto failo parametrus ir galimo pranešimo charakteristikas.

Metodo algoritmas encrypt klasė GIFEncryptorByLSBMethod:

Steganografija GIF formatu

Ir jo kodas:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	if (out == null) {
		throw new NullPointerException("Output file is null");
	}
	if (text == null) {
		throw new NullPointerException("Text is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;// one byte for check and one byte for message length
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Text is too big");
	}
	
	int n = 13;
	
	// write check sequence
	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++;
	}
	
	// write text length
	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++;
	}
	
	// write message
	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++;
		}
	}
	
	// write output file
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Metodo algoritmas ir šaltinio kodas decrypt klasė GIFEncryptorByLSBMethod:

Steganografija GIF formatu

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// one byte for check and one byte for message length
	
	int n = 13;
	
	// read check sequence
	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("There is no encrypted message in the image (Check sequence is incorrect)");
	}
	
	// read text length
	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("Decoded text length is less than 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("There is no messages (Decoded message length (" + textLength + ") is less than Possible message length (" + possibleTextLength + "))");
	}
	
	// read text bits and make text bytes
	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);
}

Klasės įgyvendinimas GIFEncryptorByPaletteExtensionMethod bus panašus, skiriasi tik informacijos išsaugojimo/skaitymo būdas.

Klasėje MainFrame Aprašyti vyniojimo būdai: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), apdorojant sąsajos metodų rezultatus Encryptor ir bendravimas su vartotoju, ty failo pasirinkimo dialogo lango atidarymas, klaidų pranešimų rodymas ir pan.; taip pat kiti metodai: openImage(), leidžianti vartotojui pasirinkti vaizdą, exit(), kuris išeina iš programos. Šie metodai vadinami iš Actionatitinkamus meniu elementus. Ši klasė papildomai įgyvendina pagalbinius metodus: createComponents() - formos komponentų kūrimas, loadImageFile(File f) - vaizdo įkėlimas į specialų komponentą iš failo. Klasės įgyvendinimas GIFEncryptorByPaletteExtensionMethod panašus į klasės įgyvendinimą GIFEncryptorByLSBMethod, pagrindinis skirtumas yra pranešimų baitų rašymo ir skaitymo iš paletės būdas.

Programos veikimas

LBS metodas

Tarkime, kad yra toks vaizdas:

Steganografija GIF formatu

Šiame paveikslėlyje paletę sudaro 256 spalvos (kaip išsaugo Paint). Pirmosios keturios spalvos: balta, juoda, raudona, žalia. Kitos spalvos juodos. Pasaulinė paletės bitų seka bus tokia:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000...

Steganografija GIF formatu

Kai pranešimas bus įdėtas, pabraukti bitai bus pakeisti pranešimo bitais. Gautas vaizdas beveik nesiskiria nuo originalo.

Originalus
Vaizdas su įterptu pranešimu

Steganografija GIF formatu
Steganografija GIF formatu

Paletės išplėtimo būdas

Kai atidarysite vaizdą su pranešimu šiuo metodu, pamatysite šį paveikslėlį:

Steganografija GIF formatu

Akivaizdu, kad šis metodas netiks visavertei šnipinėjimo veiklai, todėl gali prireikti papildomo pranešimo šifravimo.

Šifravimas / iššifravimas animuotuose vaizduose veikia taip pat, kaip ir įprastuose statiniuose vaizduose, tačiau animacija nenutrūksta.

Naudoti šaltiniai:

Parsisiuntimas:

Šaltinis: www.habr.com

Добавить комментарий