Steganografie in GIF

Introductie

Приветствую.
Nog niet zo lang geleden, toen ik aan de universiteit studeerde, was er een cursus in de discipline 'Softwaremethoden voor informatiebeveiliging'. Voor de opdracht moesten we een programma maken dat een bericht in GIF-bestanden insluit. Ik besloot het op Java te doen.

In dit artikel zal ik enkele theoretische punten beschrijven, evenals hoe dit kleine programma tot stand is gekomen.

Theoretisch gedeelte

GIF-formaat

GIF (Graphics Interchange Format - een formaat voor het uitwisselen van afbeeldingen) is een formaat voor het opslaan van grafische afbeeldingen, dat gecomprimeerde gegevens kan opslaan zonder kwaliteitsverlies in een formaat van maximaal 256 kleuren. Dit formaat is in 1987 ontwikkeld (GIF87a) door CompuServe voor het verzenden van rasterafbeeldingen via netwerken. In 1989 werd het formaat gewijzigd (GIF89a), ondersteuning voor transparantie en animatie werd toegevoegd.

GIF-bestanden hebben een blokstructuur. Deze blokken hebben altijd een vaste lengte (of het hangt af van sommige vlaggen), dus het is bijna onmogelijk om een ​​fout te maken over waar elk blok zich bevindt. De structuur van de eenvoudigste niet-geanimeerde GIF-afbeelding in GIF89a-formaat:

Steganografie in GIF

Van alle blokken van de structuur zullen we in dit geval geïnteresseerd zijn in het globale paletblok en de parameters die verantwoordelijk zijn voor het palet:

  • CT — aanwezigheid van een mondiaal palet. Als deze vlag is ingesteld, moet het globale palet onmiddellijk na de logische schermgreep beginnen.
  • Size — paletgrootte en aantal kleuren in de afbeelding. Waarden voor deze parameter:

Maat
Aantal kleuren
Paletgrootte, 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

Versleutelingsmethoden

De volgende methoden worden gebruikt om berichten in afbeeldingsbestanden te coderen:

  • LSB-methode (Least Significant Bit).
  • Methode voor het toevoegen van palet

LSB-methode - een gebruikelijke methode van steganografie. Het bestaat uit het vervangen van de laatste significante bits in de container (in ons geval de globale paletbytes) door de bits van het verborgen bericht.

Het programma gebruikt de laatste twee bits in de globale paletbytes als onderdeel van deze methode. Dit betekent dat voor een 24-bits afbeelding, waarbij het kleurenpalet drie bytes bedraagt ​​voor rood, blauw en groen, elke kleurcomponent na het insluiten van een bericht daarin met maximaal 3/255 gradaties zal veranderen. Een dergelijke verandering zal ten eerste onzichtbaar of moeilijk waarneembaar zijn voor het menselijk oog, en ten tweede zal deze niet zichtbaar zijn op informatie-uitvoerapparatuur van lage kwaliteit.

De hoeveelheid informatie hangt rechtstreeks af van de grootte van het afbeeldingspalet. Aangezien de maximale grootte van het palet 256 kleuren is, en als er twee berichtbits in de component van elke kleur worden geschreven, is de maximale berichtlengte (met het maximale palet in de afbeelding) 192 bytes. Zodra het bericht in de afbeelding is ingesloten, verandert de bestandsgrootte niet.

Paletuitbreidingsmethode, wat alleen werkt voor de GIF-structuur. Het is het meest effectief op afbeeldingen met een klein palet. De essentie ervan is dat het de grootte van het palet vergroot, waardoor extra ruimte ontstaat voor het schrijven van de benodigde bytes in plaats van de kleurbytes. Als we bedenken dat de minimale grootte van het palet 2 kleuren (6 bytes) is, kan de maximale grootte van het ingesloten bericht 256 × 3–6 = 762 bytes zijn. Het nadeel is de lage cryptografische beveiliging; het ingebedde bericht kan met elke teksteditor worden gelezen als het bericht niet is onderworpen aan aanvullende codering.

Praktisch deel

Programma ontwerp

Alle benodigde tools voor het implementeren van encryptie- en decryptie-algoritmen zullen in het pakket worden opgenomen com.tsarik.steganography. Dit pakket bevat de interface Encryptor met methoden encrypt и decrypt, Klas Binary, wat de mogelijkheid biedt om met bitarrays te werken, evenals met uitzonderingsklassen UnableToEncryptException и UnableToDecryptException, die moet worden gebruikt in interfacemethoden Encryptor in geval van respectievelijk coderings- en decoderingsfouten.

Hoofdprogrammapakket com.tsarik.programs.gifed zal een uitvoerbare programmaklasse met een statische methode bevatten main, waardoor u het programma kunt uitvoeren; een klasse die programmaparameters opslaat; en pakketten met andere klassen.

De implementatie van de algoritmen zelf zal in het pakket worden gepresenteerd com.tsarik.programs.gifed.gif klassen GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Beide klassen zullen de interface implementeren Encryptor.

Op basis van de structuur van het GIF-formaat kunt u een algemeen algoritme maken voor het introduceren van een bericht in het afbeeldingenpalet:

Steganografie in GIF

Om de aanwezigheid van een bericht in een afbeelding te bepalen, is het nodig om aan het begin van het bericht een bepaalde reeks bits toe te voegen, die de decoder eerst leest en controleert op juistheid. Als dit niet overeenkomt, wordt ervan uitgegaan dat er geen verborgen boodschap in de afbeelding zit. Vervolgens moet u de lengte van het bericht opgeven. Dan de tekst van het bericht zelf.

Klassendiagram van de gehele applicatie:

Steganografie in GIF

Implementatie van het programma

De implementatie van het gehele programma kan in twee componenten worden verdeeld: implementatie van interface-encryptie en decryptiemethoden Encryptor, in klassen GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethoden de implementatie van de gebruikersinterface.

Denk aan de klas GIFEncryptorByLSBMethod.

Steganografie in GIF

velden firstLSBit и secondLSBit bevatten de aantallen bits van elke byte van de afbeelding waarin het bericht moet worden ingevoerd en van waaruit het bericht moet worden gelezen. Veld checkSequence slaat een controlebitreeks op om herkenning van het ingebedde bericht te garanderen. Statische methode getEncryptingFileParameters retourneert de parameters van het opgegeven bestand en de kenmerken van het potentiële bericht.

Methode algoritme encrypt klasse GIFEncryptorByLSBMethod:

Steganografie in GIF

En zijn code:

@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 broncode van de methode decrypt klasse GIFEncryptorByLSBMethod:

Steganografie in 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);
}

Implementatie van de klasse GIFEncryptorByPaletteExtensionMethod zal vergelijkbaar zijn, alleen de methode voor het opslaan/lezen van informatie is anders.

In de klas MainFrame wrapper-methoden worden beschreven: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), het verwerken van de resultaten van interfacemethoden Encryptor en interactie met de gebruiker, d.w.z. het openen van een dialoogvenster voor bestandsselectie, het tonen van foutmeldingen, enz.; evenals andere methoden: openImage(), waardoor de gebruiker een afbeelding kan selecteren, exit(), waarmee de toepassing wordt afgesloten. Deze methoden worden aangeroepen vanuit Action's overeenkomstige menu-items. Deze klasse implementeert bovendien aanvullende methoden: createComponents() - creatie van formuliercomponenten, loadImageFile(File f) — een afbeelding vanuit een bestand in een speciaal onderdeel laden. Implementatie van de klasse GIFEncryptorByPaletteExtensionMethod vergelijkbaar met de klassenimplementatie GIFEncryptorByLSBMethod, het belangrijkste verschil zit in de manier waarop berichtbytes worden geschreven en gelezen vanuit het palet.

Programma bediening

LBS-methode

Laten we zeggen dat er een afbeelding als deze is:

Steganografie in GIF

In deze afbeelding bestaat het palet uit 256 kleuren (zoals Paint opslaat). De eerste vier kleuren zijn: wit, zwart, rood, groen. Andere kleuren zijn zwart. De globale paletbitreeks is als volgt:

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

Steganografie in GIF

Zodra het bericht is ingesloten, worden de onderstreepte bits vervangen door de bits uit het bericht. Het resulterende beeld verschilt bijna niet van het origineel.

Origineel
Afbeelding met ingesloten bericht

Steganografie in GIF
Steganografie in GIF

Paletuitbreidingsmethode

Wanneer u met deze methode een afbeelding met een bericht opent, ziet u de volgende afbeelding:

Steganografie in GIF

Het is duidelijk dat deze methode niet zal werken voor volwaardige spionageactiviteiten en mogelijk aanvullende versleuteling van het bericht vereist.

Codering/decodering in geanimeerde afbeeldingen werkt net als bij gewone statische afbeeldingen, maar de animatie wordt niet verbroken.

Gebruikte bronnen:

Download:

Bron: www.habr.com

Voeg een reactie