Steganografy yn GIF

Ynlieding

Groetnis.
Net sa lang lyn, doe't ik oan 'e universiteit studearre, wie d'r in kursus yn 'e dissipline "Softwaremetoaden foar ynformaasjefeiligens." De opdracht frege ús om in programma te meitsjen dat in berjocht ynbêde yn GIF-bestannen. Ik besleat it te dwaan yn Java.

Yn dit artikel sil ik guon teoretyske punten beskriuwe, lykas hoe't dit lytse programma makke is.

Teoretysk diel

GIF-formaat

GIF (Graphics Interchange Format - in formaat foar it útwikseljen fan ôfbyldings) is in formaat foar it opslaan fan grafyske ôfbyldings, by steat om komprimearre gegevens op te slaan sûnder kwaliteitsferlies yn in opmaak fan maksimaal 256 kleuren. Dit formaat is ûntwikkele yn 1987 (GIF87a) troch CompuServe foar it ferstjoeren fan rasterôfbyldings oer netwurken. Yn 1989 waard it formaat oanpast (GIF89a), stipe foar transparânsje en animaasje waard tafoege.

GIF-bestannen hawwe in blokstruktuer. Dizze blokken hawwe altyd in fêste lingte (of it hinget fan guon flaggen ôf), dus it is hast ûnmooglik om in flater te meitsjen oer wêr't elk blok leit. De struktuer fan 'e ienfâldichste net-animearre GIF-ôfbylding yn GIF89a-formaat:

Steganografy yn GIF

Fan alle blokken fan 'e struktuer, yn dit gefal sille wy ynteressearre wêze yn it globale paletblok en de parameters ferantwurdlik foar it palet:

  • CT - oanwêzigens fan in wrâldwide palet. As dizze flagge is ynsteld, moat de globale palet fuortendaliks begjinne nei de logyske skermhandgreep.
  • Size - paletgrutte en oantal kleuren yn 'e ôfbylding. Wearden foar dizze parameter:

Grutte
Oantal kleuren
Paletgrutte, bytes

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Fersifering metoaden

De folgjende metoaden sille brûkt wurde om berjochten te fersiferjen yn ôfbyldingsbestannen:

  • LSB (Least Significant Bit) metoade
  • Palette tafoeging metoade

LSB metoade - in mienskiplike metoade fan steganography. It bestiet út it ferfangen fan de lêste wichtige bits yn 'e kontener (yn ús gefal, de globale paletbytes) mei de bits fan it ferburgen berjocht.

It programma sil de lêste twa bits yn 'e globale paletbytes brûke as ûnderdiel fan dizze metoade. Dit betsjut dat foar in 24-bit ôfbylding, wêrby't it kleurpalet trije bytes is foar read, blau en grien, nei it ynbêden fan in berjocht deryn, sil elke kleurkomponint feroarje mei in maksimum fan 3/255 gradaasjes. Sa'n feroaring, yn it foarste plak, sil ûnsichtber of lestich te merken oan it minsklik each, en twad, it sil net sichtber op lege kwaliteit ynformaasje útfier apparaten.

De hoemannichte ynformaasje sil direkt ôfhinklik wêze fan 'e grutte fan it byldpalet. Sûnt de maksimale grutte fan it palet is 256 kleuren, en as twa berjocht bits wurde skreaun yn de komponint fan elke kleur, dan is de maksimale berjocht lingte (mei it maksimum palet yn 'e ôfbylding) 192 bytes. Sadree't it berjocht is ynbêde yn 'e ôfbylding, feroaret de triemgrutte net.

Palette útwreiding metoade, dy't allinich wurket foar de GIF-struktuer. It sil it meast effektyf wêze op ôfbyldings mei in lyts palet. De essinsje dêrfan is dat it de grutte fan it palet fergruttet, en dêrmei ekstra romte biedt foar it skriuwen fan de nedige bytes yn plak fan de kleurbytes. As wy beskôgje dat de minimale grutte fan it palet 2 kleuren is (6 bytes), dan kin de maksimale grutte fan it ynbêde berjocht 256 × 3-6 = 762 bytes wêze. It neidiel is lege kryptografyske feiligens; it ynbêde berjocht kin lêzen wurde mei elke tekstbewurker as it berjocht net ûnderwurpen is oan ekstra fersifering.

Praktysk part

Programma ûntwerp

Alle nedige ark foar it ymplementearjen fan fersiferings- en ûntsiferingsalgoritmen sille wurde opnaam yn it pakket com.tsarik.steganography. Dit pakket befettet de ynterface Encryptor mei metoaden encrypt и decrypt, Klas Binary, dy't de mooglikheid biedt om te wurkjen mei bitarrays, lykas útsûnderingsklassen UnableToEncryptException и UnableToDecryptException, dy't brûkt wurde moatte yn ynterfacemetoaden Encryptor yn gefal fan kodearring en dekodearjen flaters respektivelik.

Haadprogrammapakket com.tsarik.programs.gifed sil ûnder oaren in runnable programma klasse mei in statyske metoade main, wêrtroch jo it programma kinne útfiere; in klasse dy't programma parameters bewarret; en pakketten mei oare klassen.

De ymplemintaasje fan 'e algoritmen sels sil wurde presintearre yn it pakket com.tsarik.programs.gifed.gif klassen GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Beide fan dizze klassen sille de ynterface ymplementearje Encryptor.

Op grûn fan de struktuer fan it GIF-formaat kinne jo in algemien algoritme meitsje foar it yntrodusearjen fan in berjocht yn it byldpalet:

Steganografy yn GIF

Om de oanwêzigens fan in berjocht yn in ôfbylding te bepalen, is it nedich om in bepaalde folchoarder fan bits ta te foegjen oan it begjin fan it berjocht, dat de dekoder earst lêst en kontrolearret op korrektheid. As it net oerienkomt, dan wurdt beskôge dat d'r gjin ferburgen berjocht yn 'e ôfbylding is. Dêrnei moatte jo de lingte fan it berjocht opjaan. Dan de tekst fan it berjocht sels.

Klassediagram fan 'e heule applikaasje:

Steganografy yn GIF

Utfiering fan it programma

De útfiering fan it hiele programma kin wurde ferdield yn twa komponinten: ymplemintaasje fan ynterface fersifering en ûntsiferjen metoaden Encryptor, yn klassen GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, en de ymplemintaasje fan de brûkersynterface.

Tink oan de klasse GIFEncryptorByLSBMethod.

Steganografy yn GIF

fjilden firstLSBit и secondLSBit befetsje de oantallen bits fan elke byte fan de ôfbylding wêryn it berjocht ynfierd wurde moat en wêrfan it berjocht lêzen wurde moat. Fjild checkSequence bewarret in sjekbitsekwinsje om erkenning fan it ynbêde berjocht te garandearjen. Statyske metoade getEncryptingFileParameters jout de parameters fan it opjûne bestân werom en de skaaimerken fan it potinsjele berjocht.

Metoade algoritme encrypt klasse GIFEncryptorByLSBMethod:

Steganografy yn GIF

En syn koade:

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

Algoritme en boarnekoade fan 'e metoade decrypt klasse GIFEncryptorByLSBMethod:

Steganografy yn GIF

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

Utfiering fan 'e klasse GIFEncryptorByPaletteExtensionMethod sil gelyk wêze, allinich de metoade foar it bewarjen / lêzen fan ynformaasje is oars.

Yn 'e klasse MainFrame wrapper metoaden wurde beskreaun: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), ferwurkjen fan de resultaten fan ynterface metoaden Encryptor en ynteraksje mei de brûker, dat wol sizze it iepenjen fan in triem seleksje dialooch, showing flater berjochten, ensfh; lykas oare metoaden: openImage(), wêrtroch de brûker in ôfbylding kin selektearje, exit(), dy't de applikaasje útgiet. Dizze metoaden wurde neamd út Action's oerienkommende menu-items. Dizze klasse ymplementearret ekstra helpmetoaden: createComponents() - oanmeitsjen fan formulierkomponinten, loadImageFile(File f) - it laden fan in ôfbylding yn in spesjale komponint fan in bestân. Utfiering fan 'e klasse GIFEncryptorByPaletteExtensionMethod fergelykber mei de klasse ymplemintaasje GIFEncryptorByLSBMethod, It wichtichste ferskil is yn 'e manier wêrop berjochtbytes wurde skreaun en lêzen fan it palet.

Programma operaasje

LBS metoade

Litte wy sizze dat d'r in ôfbylding is lykas dit:

Steganografy yn GIF

Yn dizze ôfbylding bestiet it palet út 256 kleuren (as Paint besparret). De earste fjouwer kleuren binne: wyt, swart, read, grien. Oare kleuren binne swart. De globale palettebitsekwinsje sil as folgjend wêze:

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

Steganografy yn GIF

Sadree't it berjocht is ynbêde, wurde de ûnderstreke bits ferfongen troch de bits út it berjocht. De resultearjende ôfbylding is hast net oars as it orizjineel.

Oarspronklik
Ofbylding mei ynbêde berjocht

Steganografy yn GIF
Steganografy yn GIF

Palette útwreiding metoade

As jo ​​​​in ôfbylding iepenje mei in berjocht mei dizze metoade, sille jo de folgjende ôfbylding sjen:

Steganografy yn GIF

It is dúdlik dat dizze metoade net wurket foar folweardige spionaazjeaktiviteiten, en kin ekstra fersifering fan it berjocht fereaskje.

Fersifering / ûntsiferje yn animearre ôfbyldings wurket krekt as yn reguliere statyske ôfbyldings, mar de animaasje is net brutsen.

Boarnen brûkt:

Download:

Boarne: www.habr.com

Add a comment