Steganografi i GIF

Innledning

Hilsener.
For ikke så lenge siden, da jeg studerte ved universitetet, var det et kurs i disiplinen "Programvaremetoder for informasjonssikkerhet." Oppgaven krevde at vi laget et program som legger inn en melding i GIF-filer. Jeg bestemte meg for å gjøre det i Java.

I denne artikkelen vil jeg beskrive noen teoretiske punkter, samt hvordan dette lille programmet ble til.

Teoretisk del

GIF-format

GIF (Graphics Interchange Format - et format for utveksling av bilder) er et format for lagring av grafiske bilder, i stand til å lagre komprimerte data uten tap av kvalitet i et format på opptil 256 farger. Dette formatet ble utviklet i 1987 (GIF87a) av CompuServe for overføring av rasterbilder over nettverk. I 1989 ble formatet endret (GIF89a), støtte for åpenhet og animasjon ble lagt til.

GIF-filer har en blokkstruktur. Disse blokkene har alltid en fast lengde (eller det avhenger av noen flagg), så det er nesten umulig å ta feil om hvor hver blokk befinner seg. Strukturen til det enkleste ikke-animerte GIF-bildet i GIF89a-format:

Steganografi i GIF

Av alle blokkene i strukturen vil vi i dette tilfellet være interessert i den globale palettblokken og parameterne som er ansvarlige for paletten:

  • CT — tilstedeværelsen av en global palett. Hvis dette flagget er satt, må den globale paletten begynne umiddelbart etter det logiske skjermhåndtaket.
  • Size — palettstørrelse og antall farger i bildet. Verdier for denne parameteren:

Størrelse
Antall farger
Palettstørrelse, byte

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Krypteringsmetoder

Følgende metoder vil bli brukt for å kryptere meldinger i bildefiler:

  • LSB-metoden (Least Significant Bit).
  • Paletttilsetningsmetode

LSB metode - en vanlig metode for steganografi. Den består av å erstatte de siste signifikante bitene i beholderen (i vårt tilfelle de globale palettbytene) med bitene i den skjulte meldingen.

Programmet vil bruke de to siste bitene i de globale palettbytene som en del av denne metoden. Dette betyr at for et 24-bits bilde, der fargepaletten er tre byte for rødt, blått og grønt, etter å ha innebygd en melding i det, vil hver fargekomponent endres med maksimalt 3/255 graderinger. En slik endring vil for det første være usynlig eller vanskelig å legge merke til for det menneskelige øyet, og for det andre vil den ikke være synlig på informasjonsutdataenheter av lav kvalitet.

Mengden informasjon vil direkte avhenge av størrelsen på bildepaletten. Siden den maksimale størrelsen på paletten er 256 farger, og hvis to meldingsbiter er skrevet inn i komponenten av hver farge, er den maksimale meldingslengden (med den maksimale paletten i bildet) 192 byte. Når meldingen er innebygd i bildet, endres ikke filstørrelsen.

Palettutvidelsesmetode, som bare fungerer for GIF-strukturen. Det vil være mest effektivt på bilder med en liten palett. Essensen er at den øker størrelsen på paletten, og gir dermed ekstra plass til å skrive de nødvendige bytene i stedet for fargebytene. Hvis vi vurderer at minimumsstørrelsen på paletten er 2 farger (6 byte), kan den maksimale størrelsen på den innebygde meldingen være 256 × 3–6 = 762 byte. Ulempen er lav kryptografisk sikkerhet; den innebygde meldingen kan leses ved hjelp av et hvilket som helst tekstredigeringsprogram hvis meldingen ikke har blitt utsatt for ytterligere kryptering.

Praktisk del

Programdesign

Alle nødvendige verktøy for å implementere krypterings- og dekrypteringsalgoritmer vil være inkludert i pakken com.tsarik.steganography. Denne pakken inkluderer grensesnittet Encryptor med metoder encrypt и decrypt, klasse Binary, som gir muligheten til å jobbe med bitmatriser, samt unntaksklasser UnableToEncryptException и UnableToDecryptException, som skal brukes i grensesnittmetoder Encryptor ved henholdsvis kodings- og dekodingsfeil.

Hovedprogrampakke com.tsarik.programs.gifed vil inkludere en kjørbar programklasse med en statisk metode main, slik at du kan kjøre programmet; en klasse som lagrer programparametere; og pakker med andre klasser.

Implementeringen av selve algoritmene vil bli presentert i pakken com.tsarik.programs.gifed.gif klasser GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Begge disse klassene vil implementere grensesnittet Encryptor.

Basert på strukturen til GIF-formatet, kan du lage en generell algoritme for å introdusere en melding i bildepaletten:

Steganografi i GIF

For å bestemme tilstedeværelsen av en melding i et bilde, er det nødvendig å legge til en viss sekvens av biter i begynnelsen av meldingen, som dekoderen leser først og kontrollerer for korrekthet. Hvis det ikke stemmer, anses det som at det ikke er noen skjult melding i bildet. Deretter må du spesifisere lengden på meldingen. Deretter teksten til selve meldingen.

Klassediagram over hele applikasjonen:

Steganografi i GIF

Implementering av programmet

Implementeringen av hele programmet kan deles inn i to komponenter: implementering av grensesnittkryptering og dekrypteringsmetoder Encryptor, i klassene GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, og implementeringen av brukergrensesnittet.

Tenk på klassen GIFEncryptorByLSBMethod.

Steganografi i GIF

felt firstLSBit и secondLSBit inneholder antall biter av hver byte i bildet som meldingen skal legges inn i og hvor meldingen skal leses. Felt checkSequence lagrer en sjekkbitsekvens for å sikre gjenkjennelse av den innebygde meldingen. Statisk metode getEncryptingFileParameters returnerer parameterne til den angitte filen og egenskapene til den potensielle meldingen.

Metode algoritme encrypt klasse GIFEncryptorByLSBMethod:

Steganografi i GIF

Og koden hans:

@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 og kildekode for metoden decrypt klasse GIFEncryptorByLSBMethod:

Steganografi i 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);
}

Gjennomføring av klassen GIFEncryptorByPaletteExtensionMethod vil være lik, bare metoden for å lagre/lese informasjon er forskjellig.

I klassen MainFrame innpakningsmetoder er beskrevet: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), behandle resultatene av grensesnittmetoder Encryptor og samhandle med brukeren, dvs. åpne en filvalgsdialog, vise feilmeldinger, etc.; samt andre metoder: openImage(), som lar brukeren velge et bilde, exit(), som avslutter applikasjonen. Disse metodene kalles fra Actiontilhørende menyelementer. Denne klassen implementerer i tillegg hjelpemetoder: createComponents() - opprettelse av skjemakomponenter, loadImageFile(File f) — laste et bilde inn i en spesiell komponent fra en fil. Gjennomføring av klassen GIFEncryptorByPaletteExtensionMethod lik klasseimplementeringen GIFEncryptorByLSBMethod, er hovedforskjellen i måten meldingsbytes skrives og leses fra paletten.

Programdrift

LBS-metoden

La oss si at det er et bilde som dette:

Steganografi i GIF

På dette bildet består paletten av 256 farger (som Paint lagrer). De fire første fargene er: hvit, svart, rød, grønn. Andre farger er svart. Den globale palettbitsekvensen vil være som følger:

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

Steganografi i GIF

Når meldingen er innebygd, vil de understrekede bitene erstattes med bitene fra meldingen. Det resulterende bildet er nesten ikke forskjellig fra originalen.

Original
Bilde med innebygd melding

Steganografi i GIF
Steganografi i GIF

Palettutvidelsesmetode

Når du åpner et bilde som inneholder en melding ved hjelp av denne metoden, vil du se følgende bilde:

Steganografi i GIF

Det er klart at denne metoden ikke vil fungere for fullverdige spionasjeaktiviteter, og kan kreve ytterligere kryptering av meldingen.

Kryptering/dekryptering i animerte bilder fungerer akkurat som i vanlige statiske bilder, men animasjonen er ikke ødelagt.

Kilder som brukes:

laste ned:

Kilde: www.habr.com

Legg til en kommentar