Steganografi i GIF

Inledning

Hälsningar.
För inte så länge sedan, när jag studerade på universitetet, fanns det en kurs i disciplinen "Software metoder för informationssäkerhet." Uppdraget krävde att vi skapade ett program som bäddar in ett meddelande i GIF-filer. Jag bestämde mig för att göra det i Java.

I den här artikeln kommer jag att beskriva några teoretiska punkter, samt hur detta lilla program skapades.

Teoretisk del

GIF-format

GIF (Graphics Interchange Format - ett format för att utbyta bilder) är ett format för att lagra grafiska bilder, som kan lagra komprimerad data utan kvalitetsförlust i ett format på upp till 256 färger. Detta format utvecklades 1987 (GIF87a) av CompuServe för att överföra rasterbilder över nätverk. 1989 modifierades formatet (GIF89a), stöd för transparens och animering tillkom.

GIF-filer har en blockstruktur. Dessa block har alltid en fast längd (eller så beror det på vissa flaggor), så det är nästan omöjligt att göra ett misstag om var varje block ligger. Strukturen för den enklaste icke-animerade GIF-bilden i GIF89a-format:

Steganografi i GIF

Av alla block i strukturen kommer vi i det här fallet att vara intresserade av det globala palettblocket och parametrarna som ansvarar för paletten:

  • CT — närvaron av en global palett. Om denna flagga är inställd måste den globala paletten börja omedelbart efter det logiska skärmhandtaget.
  • Size — palettstorlek och antal färger i bilden. Värden för denna parameter:

Storlek
Antal färger
Palettstorlek, 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öljande metoder kommer att användas för att kryptera meddelanden i bildfiler:

  • LSB-metoden (Least Significant Bit).
  • Paletttilläggsmetod

LSB-metod - en vanlig metod för steganografi. Den består av att ersätta de sista signifikanta bitarna i behållaren (i vårt fall de globala palettbytena) med bitarna i det dolda meddelandet.

Programmet kommer att använda de två sista bitarna i den globala palettbyten som en del av denna metod. Detta innebär att för en 24-bitars bild, där färgpaletten är tre byte för rött, blått och grönt, efter att ett meddelande bäddats in i den, kommer varje färgkomponent att ändras med maximalt 3/255 graderingar. En sådan förändring kommer för det första att vara osynlig eller svår att märka för det mänskliga ögat, och för det andra kommer den inte att vara synlig på enheter av låg kvalitet.

Mängden information kommer direkt att bero på storleken på bildpaletten. Eftersom palettens maximala storlek är 256 färger, och om två meddelandebitar skrivs in i komponenten av varje färg, är den maximala meddelandelängden (med den maximala paletten i bilden) 192 byte. När meddelandet är inbäddat i bilden ändras inte filstorleken.

Palettexpansionsmetod, som bara fungerar för GIF-strukturen. Det blir mest effektivt på bilder med en liten palett. Dess kärna är att den ökar palettens storlek, vilket ger ytterligare utrymme för att skriva de nödvändiga byten i stället för färgbytena. Om vi ​​anser att palettens minsta storlek är 2 färger (6 byte), kan den maximala storleken på det inbäddade meddelandet vara 256 × 3–6 = 762 byte. Nackdelen är låg kryptografisk säkerhet, det inbäddade meddelandet kan läsas med vilken textredigerare som helst om meddelandet inte har utsatts för ytterligare kryptering.

Praktisk del

Programdesign

Alla nödvändiga verktyg för att implementera krypterings- och dekrypteringsalgoritmer kommer att inkluderas i paketet com.tsarik.steganography. Detta paket inkluderar gränssnittet Encryptor med metoder encrypt и decrypt, Klass Binary, som ger möjlighet att arbeta med bitarrayer, såväl som undantagsklasser UnableToEncryptException и UnableToDecryptException, som bör användas i gränssnittsmetoder Encryptor vid kodnings- respektive avkodningsfel.

Huvudprogrampaket com.tsarik.programs.gifed kommer att inkludera en körbar programklass med en statisk metod main, så att du kan köra programmet; en klass som lagrar programparametrar; och paket med andra klasser.

Implementeringen av själva algoritmerna kommer att presenteras i paketet com.tsarik.programs.gifed.gif klasser GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Båda dessa klasser kommer att implementera gränssnittet Encryptor.

Baserat på strukturen för GIF-formatet kan du skapa en allmän algoritm för att introducera ett meddelande i bildpaletten:

Steganografi i GIF

För att bestämma närvaron av ett meddelande i en bild är det nödvändigt att lägga till en viss sekvens av bitar i början av meddelandet, som avkodaren läser först och kontrollerar om det är korrekt. Om det inte stämmer överens, så anses det att det inte finns något dolt meddelande i bilden. Därefter måste du ange längden på meddelandet. Sedan texten i själva meddelandet.

Klassdiagram över hela applikationen:

Steganografi i GIF

Genomförande av programmet

Implementeringen av hela programmet kan delas upp i två komponenter: implementering av gränssnittskryptering och dekrypteringsmetoder Encryptor, i klasser GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, och implementeringen av användargränssnittet.

Tänk på klassen GIFEncryptorByLSBMethod.

Steganografi i GIF

fält firstLSBit и secondLSBit innehålla antalet bitar av varje byte i bilden i vilken meddelandet ska matas in och varifrån meddelandet ska läsas. Fält checkSequence lagrar en kontrollbitsekvens för att säkerställa igenkänning av det inbäddade meddelandet. Statisk metod getEncryptingFileParameters returnerar parametrarna för den angivna filen och egenskaperna för det potentiella meddelandet.

Metodalgoritm encrypt klass GIFEncryptorByLSBMethod:

Steganografi i GIF

Och hans kod:

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

Metodens algoritm och källkod decrypt klass 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);
}

Genomförande av klassen GIFEncryptorByPaletteExtensionMethod kommer att vara liknande, bara metoden för att spara/läsa information är annorlunda.

På lektionen MainFrame omslagsmetoder beskrivs: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), bearbetning av resultaten av gränssnittsmetoder Encryptor och interagera med användaren, dvs. öppna en filvalsdialog, visa felmeddelanden, etc.; samt andra metoder: openImage(), låter användaren välja en bild, exit(), som avslutar applikationen. Dessa metoder kallas från Actionmotsvarande menyalternativ. Denna klass implementerar dessutom hjälpmetoder: createComponents() - skapande av formulärkomponenter, loadImageFile(File f) — ladda en bild till en speciell komponent från en fil. Genomförande av klassen GIFEncryptorByPaletteExtensionMethod liknande klassimplementeringen GIFEncryptorByLSBMethod, är den största skillnaden i hur meddelandebytes skrivs och läses från paletten.

Programdrift

LBS-metod

Låt oss säga att det finns en sådan här bild:

Steganografi i GIF

I den här bilden består paletten av 256 färger (som Paint sparar den). De fyra första färgerna är: vit, svart, röd, grön. Andra färger är svarta. Den globala palettbitsekvensen blir som följer:

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

Steganografi i GIF

När meddelandet är inbäddat kommer de understrukna bitarna att ersättas med bitarna från meddelandet. Den resulterande bilden skiljer sig nästan inte från originalet.

Original
Bild med inbäddat meddelande

Steganografi i GIF
Steganografi i GIF

Palettexpansionsmetod

När du öppnar en bild som innehåller ett meddelande med den här metoden, kommer du att se följande bild:

Steganografi i GIF

Det är tydligt att den här metoden inte kommer att fungera för fullfjädrad spionageverksamhet och kan kräva ytterligare kryptering av meddelandet.

Kryptering/dekryptering i animerade bilder fungerar precis som i vanliga statiska bilder, men animeringen är inte trasig.

Använda källor:

nedladdning:

Källa: will.com

Lägg en kommentar