Steganograafia GIF-is

Sissejuhatus

Tere.
Hiljuti, kui Ă”ppisin ĂŒlikoolis, oli mul kursustöö "Tehnilised meetodid teabe kaitsmiseks". Ülesanne oli luua programm, mis sisestaks sĂ”numi GIF-formaadiga failidesse. Otsustasin teha seda Java keeles.

Selles artiklis kirjeldan mÔned teoreetilised punktid ning samuti, kuidas ma selle vÀikese programmi lÔin.

Teoreetiline osa

GIF-formaat

GIF (ingl. k. Graphics Interchange Format — kujutiste vahetamise formaat) on graafiliste kujutiste salvestamise formaat, mis suudab salvestada tihendatud andmeid kvaliteedi kaotamata 256 vĂ€rvi formatis. See formaat töötati vĂ€lja 1987. aastal (GIF87a) ettevĂ”tte CompuServe poolt rasterpiltide edastamiseks vĂ”rkude kaudu. Aastal 1989 muudeti formaati (GIF89a), lisati toetus lĂ€bipaistvusele ja animatsioonile.

GIF-formaadiga failidel on plokkstruktuur. Need andmeplokid on alati fikseeritud pikkusega (vÔi need sÔltuvad teatud lippudest), mistÔttu on peaaegu vÔimatu eksida selles, kus mis plokk asub. KÔige lihtsama animatsioonita GIF-pildi struktuur GIF89a formaadis:

Steganograafia GIF-is

KÔigist struktuuri plokkidest huvitavad meid sel juhul globaalne palett ja parameetrid, mis vastutavad palette eest:

  • CT — globaalne palett. Kui see lipp on seatud, peab globaalne palett algama kohe pĂ€rast loogilise ekraani descriptorit.
  • Suurus — paleti suurus ja pildi vĂ€rvid. Selle parameetri vÀÀrtused:

Suurus
VĂ€rvide arv
Paleti suurus, baitides

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

KrĂŒpteerimismeetodid

Kuna meetodeid sĂ”numite krĂŒpteerimiseks pildifailides kasutatakse:

  • LSB meetod (Least Significant Bit, vĂ€him oluline bit)
  • Pikenduse meetod paletis

LSB meetod — levinud steganograafia meetod. See seisneb konteineris (meie puhul globaalsete palettide baitides) viimaste olulisemate bittide asendamises peidetud sĂ”numi bittidega.

Programmis kasutatakse selle meetodi raames kahte viimast bitti globaalsete vÀrvipalettide baitides. See tÀhendab, et 24-bitise pildi puhul, kus vÀrvipalett koosneb kolmest baitist punase, sinise ja rohelise vÀrvi jaoks, iga vÀrviosade vÀÀrtus muutub pÀrast sÔnumi sisestamist maksimaalselt 3/255 kraadi. Selline muudatus on esiteks inimsilma jaoks nÀhtamatu vÔi raskesti mÀrgatav ja teiseks ei ole see madala kvaliteediga vÀljundseadmetes eristatav.

Infokogus sÔltub otseselt pildi vÀrvipaleti suurusest. Kuna maksimaalne vÀrvipalett on 256 vÀrvi ja kui sisestada sÔnumi jaoks iga vÀrvi koostisosasse kaks bitti, siis maksimaalne sÔnumi pikkus (maksimaalse paletiga pildis) on 192 baiti. PÀrast sÔnumi sisestamist pildi sisse ei muutu faili suurus.

VĂ€rvipaleti laiendamise meetod, mis töötab ainult GIF-struktuuri korral. See on kĂ”ige tĂ”husam vĂ€ikeste vĂ€rvipalettidega piltides. Tema olemus seisneb paleti suuruse suurendamises, pakkudes seelĂ€bi tĂ€iendavat ruumi vajalike baitide salvestamiseks vĂ€rvibaitide kohtades. KĂ”ige vĂ€iksema paleti suurusega, mis on 2 vĂ€rvi (6 baiti), vĂ”ib maksimaalne sisestatud sĂ”numi suurus olla 256×3–6=762 baiti. Puudus — madal krĂŒptograafia usaldusvÀÀrsus, kuna sisestatud sĂ”numit saab lugeda mis tahes tekstiredaktoris, kui sĂ”numit ei ole tĂ€iendavalt krĂŒpteeritud.

Praktiline osa

Programmi projekteerimine

KĂ”ik vajalikud tööriistad krĂŒpteerimis- ja dekrĂŒpteerimisalgoritmide rakendamiseks asuvad pakis com.tsarik.steganography. Selle paketi koosseisu kuulub liides Encryptor meetoditega encrypt ja decrypt, klass Binary, mis vĂ”imaldab töötada bite massiividega ning erandite klassid UnableToEncryptException ja UnableToDecryptException, mida tuleb kasutada liidese meetodites Encryptor kodeerimise ja dekodeerimise vigade korral vastavalt.

Programmi peamine pakett com.tsarik.programs.gifed sisaldab programmi kÀivitatavat klassi staatilise meetodiga main, mis vÔimaldab programmi kÀivitada; klass, mis salvestab programmi parameetrid; ja teiste klasside pakette.

Alglite sÔnumite realiseerimise algoritmid esitatakse paketina com.tsarik.programs.gifed.gif klassides GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod. MÔlemad klassid realiseerivad liidest Encryptor.

GIF formaadi struktuuri alusel vĂ”ib koostada ĂŒldise algoritmi sĂ”numi sisestamiseks pildialusesse paletti:

Steganograafia GIF-is

SÔnumi kohaloleku mÀÀramiseks pildis tuleb sÔnumi algusesse lisada teatud bitijada, mille dekooder loeb esimesena ja kontrollib selle Ôigsust. Kui see ei kattu, siis loetakse, et pildis ei ole varjatud sÔnumit. Edasi tuleb mÀrkida sÔnumi pikkus. SeejÀrel ise sÔnumi tekst.

Kogu rakenduse klasside diagramm:

Steganograafia GIF-is

Programmi realiseerimine

Kogu programmi realiseerimine vĂ”ib jagada kaheks osaks: krĂŒpteerimise ja dekrĂŒpteerimise meetodite realiseerimine liideses Encryptor, klassides GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod, ja kasutajaliidese realiseerimine.

Vaadakem klassi GIFEncryptorByLSBMethod.

Steganograafia GIF-is

VÀljad firstLSBit ja secondLSBit sisaldavad iga pildi byte'i bitide numbreid, kuhu sÔnum peab kirjutama ja kust see lugema peab. VÀli checkSequence salvestab kontrollimise bitijada, et tagada sisestatud sÔnumi tuvastamine. Staatiline meetod getEncryptingFileParameters tagastab mÀÀratud faili parameetrid ja potentsiaalse sÔnumi omadused.

Meetodi algoritm encrypt klassist GIFEncryptorByLSBMethod:

Steganograafia GIF-is

Ja selle kood:

@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();
}

Algoritm ja meetodi lÀhtekood decrypt klassist GIFEncryptorByLSBMethod:

Steganograafia GIF-is

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Sisendfail on tĂŒhi");
	}
	
	// loe baitid sisendfailist
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// kontrolli formaati
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Sisendfailil on vale GIF formaat");
	}
	
	// loe paleti suuruse omadus esimesest kolmest bitist 10. baitis failist
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// arvuta vÀrvide arv ja vÔimaliku sÔnumi pikkus
	int bOrigColorCount = (int)Math.pow(2, bsize + 1);
	int possibleMessageLength = bOrigColorCount * 3 / 4;
	int possibleTextLength = possibleMessageLength - 2; // ĂŒks bait kontrolliks ja ĂŒks bait sĂ”numi pikkuseks
	
	int n = 13;
	
	// loe kontrolljÀrjestus
	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("Pildil ei ole krĂŒptitud sĂ”numit (kontrolljĂ€rjestus on vale)");
	}
	
	// loe teksti pikkus
	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("Dekodeeritud teksti pikkus on vÀhem kui 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Ei ole sÔnumeid (Dekodeeritud sÔnumi pikkus (" + textLength + ") on vÀiksem kui VÔimalik sÔnumi pikkus (" + possibleTextLength + "))");
	}
	
	// loe teksti baitid ja tee teksti baitideks
	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);
}

Klassi rakendus GIFEncryptorByPaletteExtensionMethod on sarnane, ainult salvestamise/loetava teabe meetod erineb.

Klassis MainFrame on kirjeldatud "ĂŒmbrismooduseid": encryptImage(Encryptor encryptor) ja decryptImage(Encryptor encryptor), mis töötlevad liidese meetodite tulemusi Encryptor ja vĂ”imaldavad kasutajaga suhtlemist, st avavad failide valiku dialooge, kuvavad veateateid jne; samuti teised meetodid: openImage(), mis vĂ”imaldab kasutajal koos pilti valida, exit(), mis vĂ”imaldab rakendusest vĂ€ljumist. Need meetodid kutsutakse esile vastava menĂŒĂŒpunkti Toiming‘de kaudu. Selles klassis on samuti rakendatud abimeetodeid: createComponents() — vormi komponentide loomine, loadImageFile(File f) — pildi laadimine spetsiaalsesse komponenti failist. Klasside rakendus GIFEncryptorByPaletteExtensionMethod on sarnane klassi rakendusele GIFEncryptorByLSBMethod, peamine erinevus seisneb sĂ”numi baitide kirjutamise ja lugemise meetodis paletist.

Programmi töö

LBS meetod

Oletame, et on olemas selline pilt:

Steganograafia GIF-is

Antud pildil on vĂ€rvipalett, mis koosneb 256 vĂ€rvist (sellisena salvestab Paint). Esimesed neli vĂ€rvi: valge, must, punane, roheline. ÜlejÀÀnud vĂ€rvid on mustad. Globaalse paletti bitijĂ€rjestus on jĂ€rgmine:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000


Steganograafia GIF-is

PĂ€rast sĂ”numi peitmist asendatakse allajoonitud bitid sĂ”numi bitidega. Saadud pilt ei erine originaalist peaaegu ĂŒldse.

Originaal
Pilt peidetud sÔnumiga

Steganograafia GIF-is
Steganograafia GIF-is

VĂ€rvipaleti laiendamise meetod

Avades pildi, kuhu see meetodiga sÔnum on peidetud, vÔib ette tulla jÀrgmine pilt:

Steganograafia GIF-is

Muidugi ei sobi selline meetod tÀielikuks luuretegevuseks ja vÔib-olla vajab see lisakodeerimist.

KrĂŒpteerimine/dekrĂŒpteerimine animeeritud piltides töötab samamoodi nagu tavalistes staatilistes piltides, samas kui animatsioon ei riku.

Kasutatavad allikaid:

Laadi alla:

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster