Steganografi i GIF

Indledning

Vær hilset.
For ikke så længe siden, da jeg studerede på universitetet, var der et kursus i disciplinen "Softwaremetoder til informationssikkerhed." Opgaven krævede, at vi lavede et program, der indlejrede en besked i GIF-filer. Jeg besluttede at gøre det i Java.

I denne artikel vil jeg beskrive nogle teoretiske pointer, samt hvordan dette lille program blev til.

Teoretisk del

GIF-format

GIF (Graphics Interchange Format - et format til udveksling af billeder) er et format til lagring af grafiske billeder, der er i stand til at gemme komprimerede data uden tab af kvalitet i et format på op til 256 farver. Dette format blev udviklet i 1987 (GIF87a) af CompuServe til transmission af rasterbilleder over netværk. I 1989 blev formatet ændret (GIF89a), understøttelse af gennemsigtighed og animation blev tilføjet.

GIF-filer har en blokstruktur. Disse blokke har altid en fast længde (eller det afhænger af nogle flag), så det er næsten umuligt at lave en fejl om, hvor hver blok er placeret. Strukturen af ​​det enkleste ikke-animerede GIF-billede i GIF89a-format:

Steganografi i GIF

Af alle strukturens blokke vil vi i dette tilfælde være interesserede i den globale paletblok og de parametre, der er ansvarlige for paletten:

  • CT — tilstedeværelsen af ​​en global palet. Hvis dette flag er indstillet, skal den globale palet begynde umiddelbart efter det logiske skærmhåndtag.
  • Size — paletstørrelse og antal farver på billedet. Værdier for denne parameter:

Størrelse
Antal farver
Paletstørrelse, 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

Krypteringsmetoder

Følgende metoder vil blive brugt til at kryptere meddelelser i billedfiler:

  • LSB-metoden (Least Significant Bit).
  • Palettetilsætningsmetode

LSB metode - en almindelig metode til steganografi. Det består i at erstatte de sidste signifikante bits i containeren (i vores tilfælde de globale paletbytes) med bits af den skjulte meddelelse.

Programmet vil bruge de sidste to bits i de globale paletbytes som en del af denne metode. Det betyder, at for et 24-bit billede, hvor farvepaletten er tre bytes for rød, blå og grøn, efter indlejring af en besked i det, vil hver farvekomponent ændre sig med maksimalt 3/255 gradueringer. En sådan ændring vil for det første være usynlig eller svær at bemærke for det menneskelige øje, og for det andet vil den ikke være synlig på informationsoutputenheder af lav kvalitet.

Mængden af ​​information vil direkte afhænge af størrelsen af ​​billedpaletten. Da palettens maksimale størrelse er 256 farver, og hvis to beskedbits er skrevet ind i komponenten af ​​hver farve, så er den maksimale beskedlængde (med den maksimale palet i billedet) 192 bytes. Når meddelelsen er indlejret i billedet, ændres filstørrelsen ikke.

Paletteudvidelsesmetode, som kun virker for GIF-strukturen. Det vil være mest effektivt på billeder med en lille palet. Dens essens er, at den øger palettens størrelse og giver derved ekstra plads til at skrive de nødvendige bytes i stedet for farvebytes. Hvis vi overvejer, at palettens minimumsstørrelse er 2 farver (6 bytes), så kan den maksimale størrelse af den indlejrede meddelelse være 256 × 3-6 = 762 bytes. Ulempen er lav kryptografisk sikkerhed; den indlejrede besked kan læses ved hjælp af en hvilken som helst teksteditor, hvis beskeden ikke er blevet udsat for yderligere kryptering.

Praktisk del

Program design

Alle nødvendige værktøjer til implementering af krypterings- og dekrypteringsalgoritmer vil være inkluderet i pakken com.tsarik.steganography. Denne pakke inkluderer grænsefladen Encryptor med metoder encrypt и decrypt, Klasse Binary, som giver mulighed for at arbejde med bit-arrays, såvel som undtagelsesklasser UnableToEncryptException и UnableToDecryptException, som skal bruges i grænseflademetoder Encryptor i tilfælde af henholdsvis indkodnings- og afkodningsfejl.

Hovedprogrampakke com.tsarik.programs.gifed vil inkludere en kørebar programklasse med en statisk metode main, så du kan køre programmet; en klasse, der gemmer programparametre; og pakker med andre klasser.

Implementeringen af ​​selve algoritmerne vil blive præsenteret i pakken com.tsarik.programs.gifed.gif klasser GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Begge disse klasser vil implementere grænsefladen Encryptor.

Baseret på strukturen af ​​GIF-formatet kan du oprette en generel algoritme til at introducere en besked i billedpaletten:

Steganografi i GIF

For at bestemme tilstedeværelsen af ​​en meddelelse i et billede er det nødvendigt at tilføje en bestemt sekvens af bit til begyndelsen af ​​meddelelsen, som dekoderen læser først og kontrollerer for korrekthed. Hvis det ikke matcher, så anses det for, at der ikke er nogen skjult besked i billedet. Dernæst skal du angive længden af ​​meddelelsen. Derefter selve beskedens tekst.

Klassediagram over hele applikationen:

Steganografi i GIF

Implementering af programmet

Implementeringen af ​​hele programmet kan opdeles i to komponenter: implementering af grænsefladekryptering og dekrypteringsmetoder Encryptor, i klasserne GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, og implementeringen af ​​brugergrænsefladen.

Overvej klassen GIFEncryptorByLSBMethod.

Steganografi i GIF

felter firstLSBit и secondLSBit indeholde antallet af bits af hver byte af billedet, som meddelelsen skal indtastes i, og hvorfra meddelelsen skal læses. Mark checkSequence gemmer en tjekbitsekvens for at sikre genkendelse af den indlejrede meddelelse. Statisk metode getEncryptingFileParameters returnerer parametrene for den angivne fil og egenskaberne for den potentielle besked.

Metode algoritme encrypt klasse GIFEncryptorByLSBMethod:

Steganografi i GIF

Og hans kode:

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

Gennemførelse af klassen GIFEncryptorByPaletteExtensionMethod vil være ens, kun metoden til at gemme/læse information er anderledes.

I klassen MainFrame indpakningsmetoder er beskrevet: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), behandling af resultaterne af grænseflademetoder Encryptor og interagere med brugeren, dvs. åbne en filvalgsdialog, vise fejlmeddelelser, etc.; samt andre metoder: openImage(), der giver brugeren mulighed for at vælge et billede, exit(), som afslutter applikationen. Disse metoder kaldes fra Actions tilsvarende menupunkter. Denne klasse implementerer desuden hjælpemetoder: createComponents() - oprettelse af formularkomponenter, loadImageFile(File f) — indlæsning af et billede i en speciel komponent fra en fil. Gennemførelse af klassen GIFEncryptorByPaletteExtensionMethod svarende til klasseimplementeringen GIFEncryptorByLSBMethod, er den største forskel i den måde, meddelelsesbytes skrives og læses fra paletten.

Program drift

LBS metode

Lad os sige, at der er et billede som dette:

Steganografi i GIF

På dette billede består paletten af ​​256 farver (som Paint gemmer). De første fire farver er: hvid, sort, rød, grøn. Andre farver er sorte. Den globale palette bitsekvens vil være som følger:

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

Steganografi i GIF

Når meddelelsen er indlejret, vil de understregede bits blive erstattet med bits fra meddelelsen. Det resulterende billede er næsten ikke anderledes end originalen.

Original
Billede med indlejret besked

Steganografi i GIF
Steganografi i GIF

Paletteudvidelsesmetode

Når du åbner et billede, der indeholder en besked ved hjælp af denne metode, vil du se følgende billede:

Steganografi i GIF

Det er klart, at denne metode ikke vil fungere for fuldgyldige spionageaktiviteter og kan kræve yderligere kryptering af beskeden.

Kryptering/dekryptering i animerede billeder fungerer ligesom i almindelige statiske billeder, men animationen er ikke brudt.

Anvendte kilder:

Download:

Kilde: www.habr.com

Tilføj en kommentar