Στεγανογραφία σε GIF

Εισαγωγή

Χαιρετισμούς.
Όχι πολύ καιρό πριν, όταν σπούδαζα στο πανεπιστήμιο, υπήρχε ένα μάθημα στον κλάδο «Μέθοδοι λογισμικού για την ασφάλεια των πληροφοριών». Η ανάθεση απαιτούσε να δημιουργήσουμε ένα πρόγραμμα που ενσωματώνει ένα μήνυμα σε αρχεία GIF. Αποφάσισα να το κάνω σε Java.

Σε αυτό το άρθρο θα περιγράψω μερικά θεωρητικά σημεία, καθώς και πώς δημιουργήθηκε αυτό το μικρό πρόγραμμα.

Θεωρητικό μέρος

Μορφή GIF

Το GIF (Graphics Interchange Format - μια μορφή για την ανταλλαγή εικόνων) είναι μια μορφή για την αποθήκευση εικόνων γραφικών, ικανή να αποθηκεύει συμπιεσμένα δεδομένα χωρίς απώλεια ποιότητας σε μορφή έως και 256 χρωμάτων. Αυτή η μορφή αναπτύχθηκε το 1987 (GIF87a) από την CompuServe για τη μετάδοση εικόνων ράστερ μέσω δικτύων. Το 1989, η μορφή τροποποιήθηκε (GIF89a), προστέθηκε υποστήριξη για διαφάνεια και κινούμενα σχέδια.

Τα αρχεία GIF έχουν δομή μπλοκ. Αυτά τα μπλοκ έχουν πάντα ένα σταθερό μήκος (ή εξαρτάται από ορισμένες σημαίες), επομένως είναι σχεδόν αδύνατο να κάνουμε λάθος σχετικά με το πού βρίσκεται κάθε μπλοκ. Η δομή της απλούστερης μη κινούμενης εικόνας GIF σε μορφή GIF89a:

Στεγανογραφία σε GIF

Από όλα τα μπλοκ της δομής, σε αυτήν την περίπτωση θα μας ενδιαφέρει το μπλοκ καθολικής παλέτας και οι παράμετροι που είναι υπεύθυνες για την παλέτα:

  • CT — παρουσία μιας παγκόσμιας παλέτας. Εάν έχει οριστεί αυτή η σημαία, η καθολική παλέτα πρέπει να ξεκινήσει αμέσως μετά τη λογική λαβή οθόνης.
  • Size — μέγεθος παλέτας και αριθμός χρωμάτων στην εικόνα. Τιμές για αυτήν την παράμετρο:

Μέγεθος
Αριθμός χρωμάτων
Μέγεθος παλέτας, 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

Μέθοδοι κρυπτογράφησης

Οι ακόλουθες μέθοδοι θα χρησιμοποιηθούν για την κρυπτογράφηση μηνυμάτων σε αρχεία εικόνας:

  • Μέθοδος LSB (Last Significant Bit).
  • Μέθοδος προσθήκης παλέτας

Μέθοδος LSB - μια κοινή μέθοδος στεγανογραφίας. Συνίσταται στην αντικατάσταση των τελευταίων σημαντικών bits στο κοντέινερ (στην περίπτωσή μας, τα byte της καθολικής παλέτας) με τα bit του κρυφού μηνύματος.

Το πρόγραμμα θα χρησιμοποιήσει τα δύο τελευταία bit στα byte της καθολικής παλέτας ως μέρος αυτής της μεθόδου. Αυτό σημαίνει ότι για μια εικόνα 24-bit, όπου η χρωματική παλέτα είναι τρία byte για κόκκινο, μπλε και πράσινο, μετά την ενσωμάτωση ενός μηνύματος σε αυτήν, κάθε στοιχείο χρώματος θα αλλάξει κατά 3/255 διαβαθμίσεις το πολύ. Μια τέτοια αλλαγή, πρώτον, θα είναι αόρατη ή δύσκολο να παρατηρηθεί στο ανθρώπινο μάτι και, δεύτερον, δεν θα είναι ορατή σε συσκευές εξόδου πληροφοριών χαμηλής ποιότητας.

Ο όγκος των πληροφοριών θα εξαρτηθεί άμεσα από το μέγεθος της παλέτας εικόνων. Δεδομένου ότι το μέγιστο μέγεθος της παλέτας είναι 256 χρώματα και εάν δύο bit μηνύματος είναι γραμμένα στο στοιχείο κάθε χρώματος, τότε το μέγιστο μήκος μηνύματος (με τη μέγιστη παλέτα στην εικόνα) είναι 192 byte. Μόλις το μήνυμα ενσωματωθεί στην εικόνα, το μέγεθος του αρχείου δεν αλλάζει.

Μέθοδος επέκτασης παλέτας, το οποίο λειτουργεί μόνο για τη δομή GIF. Θα είναι πιο αποτελεσματικό σε εικόνες με μικρή παλέτα. Η ουσία του είναι ότι αυξάνει το μέγεθος της παλέτας, παρέχοντας έτσι επιπλέον χώρο για την εγγραφή των απαραίτητων byte στη θέση των χρωματικών byte. Αν θεωρήσουμε ότι το ελάχιστο μέγεθος της παλέτας είναι 2 χρώματα (6 byte), τότε το μέγιστο μέγεθος του ενσωματωμένου μηνύματος μπορεί να είναι 256 × 3–6 = 762 byte. Το μειονέκτημα είναι η χαμηλή κρυπτογραφική ασφάλεια· το ενσωματωμένο μήνυμα μπορεί να διαβαστεί χρησιμοποιώντας οποιοδήποτε πρόγραμμα επεξεργασίας κειμένου, εάν το μήνυμα δεν έχει υποβληθεί σε πρόσθετη κρυπτογράφηση.

Πρακτικό μέρος

Σχεδιασμός προγράμματος

Όλα τα απαραίτητα εργαλεία για την εφαρμογή αλγορίθμων κρυπτογράφησης και αποκρυπτογράφησης θα περιλαμβάνονται στη συσκευασία com.tsarik.steganography. Αυτό το πακέτο περιλαμβάνει τη διεπαφή Encryptor με μεθόδους encrypt и decrypt, Τάξη Binary, το οποίο παρέχει τη δυνατότητα εργασίας με πίνακες bit, καθώς και με κλάσεις εξαίρεσης UnableToEncryptException и UnableToDecryptException, το οποίο θα πρέπει να χρησιμοποιείται σε μεθόδους διεπαφής Encryptor σε περίπτωση σφαλμάτων κωδικοποίησης και αποκωδικοποίησης αντίστοιχα.

Κύριο πακέτο προγράμματος com.tsarik.programs.gifed θα περιλαμβάνει μια κλάση προγράμματος με δυνατότητα εκτέλεσης με στατική μέθοδο main, που σας επιτρέπει να εκτελέσετε το πρόγραμμα. μια κλάση που αποθηκεύει τις παραμέτρους του προγράμματος. και πακέτα με άλλες τάξεις.

Η υλοποίηση των ίδιων των αλγορίθμων θα παρουσιαστεί στο πακέτο com.tsarik.programs.gifed.gif τάξεις GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Και οι δύο αυτές κλάσεις θα υλοποιήσουν τη διεπαφή Encryptor.

Με βάση τη δομή της μορφής GIF, μπορείτε να δημιουργήσετε έναν γενικό αλγόριθμο για την εισαγωγή ενός μηνύματος στην παλέτα εικόνων:

Στεγανογραφία σε GIF

Για να προσδιορίσετε την παρουσία ενός μηνύματος σε μια εικόνα, είναι απαραίτητο να προσθέσετε μια ορισμένη ακολουθία bit στην αρχή του μηνύματος, την οποία ο αποκωδικοποιητής διαβάζει πρώτα και ελέγχει την ορθότητα. Αν δεν ταιριάζει, τότε θεωρείται ότι δεν υπάρχει κρυφό μήνυμα στην εικόνα. Στη συνέχεια, πρέπει να καθορίσετε τη διάρκεια του μηνύματος. Στη συνέχεια, το ίδιο το κείμενο του μηνύματος.

Διάγραμμα τάξης ολόκληρης της εφαρμογής:

Στεγανογραφία σε GIF

Υλοποίηση του προγράμματος

Η υλοποίηση ολόκληρου του προγράμματος μπορεί να χωριστεί σε δύο μέρη: εφαρμογή κρυπτογράφησης διεπαφής και μεθόδους αποκρυπτογράφησης Encryptor, στις τάξεις GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, και την υλοποίηση της διεπαφής χρήστη.

Σκεφτείτε την τάξη GIFEncryptorByLSBMethod.

Στεγανογραφία σε GIF

Πεδίο firstLSBit и secondLSBit περιέχει τους αριθμούς των bit κάθε byte της εικόνας στην οποία πρέπει να εισαχθεί το μήνυμα και από όπου πρέπει να διαβαστεί το μήνυμα. Πεδίο checkSequence αποθηκεύει μια ακολουθία bit ελέγχου για να διασφαλίσει την αναγνώριση του ενσωματωμένου μηνύματος. Στατική μέθοδος getEncryptingFileParameters επιστρέφει τις παραμέτρους του καθορισμένου αρχείου και τα χαρακτηριστικά του πιθανού μηνύματος.

Αλγόριθμος μεθόδου encrypt κατηγορία GIFEncryptorByLSBMethod:

Στεγανογραφία σε GIF

Και ο κωδικός του:

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

Αλγόριθμος και πηγαίος κώδικας της μεθόδου decrypt κατηγορία GIFEncryptorByLSBMethod:

Στεγανογραφία σε 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);
}

Υλοποίηση της τάξης GIFEncryptorByPaletteExtensionMethod θα είναι παρόμοια, μόνο η μέθοδος αποθήκευσης/ανάγνωσης πληροφοριών είναι διαφορετική.

Στην τάξη MainFrame Οι μέθοδοι περιτυλίγματος περιγράφονται: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), επεξεργασία των αποτελεσμάτων των μεθόδων διεπαφής Encryptor και αλληλεπίδραση με τον χρήστη, δηλαδή άνοιγμα διαλόγου επιλογής αρχείου, εμφάνιση μηνυμάτων σφάλματος κ.λπ. καθώς και άλλες μεθόδους: openImage(), επιτρέποντας στον χρήστη να επιλέξει μια εικόνα, exit(), το οποίο βγαίνει από την εφαρμογή. Αυτές οι μέθοδοι καλούνται από Actionτου αντίστοιχου μενού. Αυτή η κλάση εφαρμόζει επιπλέον βοηθητικές μεθόδους: createComponents() - δημιουργία στοιχείων φόρμας, loadImageFile(File f) — φόρτωση μιας εικόνας σε ένα ειδικό στοιχείο από ένα αρχείο. Υλοποίηση της τάξης GIFEncryptorByPaletteExtensionMethod παρόμοια με την υλοποίηση της τάξης GIFEncryptorByLSBMethod, η κύρια διαφορά έγκειται στον τρόπο με τον οποίο γράφονται και διαβάζονται τα byte μηνυμάτων από την παλέτα.

Λειτουργία προγράμματος

Μέθοδος LBS

Ας πούμε ότι υπάρχει μια εικόνα σαν αυτή:

Στεγανογραφία σε GIF

Σε αυτήν την εικόνα, η παλέτα αποτελείται από 256 χρώματα (όπως αποθηκεύει το Paint). Τα πρώτα τέσσερα χρώματα είναι: λευκό, μαύρο, κόκκινο, πράσινο. Άλλα χρώματα είναι μαύρο. Η παγκόσμια ακολουθία bit παλέτας θα είναι η εξής:

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

Στεγανογραφία σε GIF

Μόλις ενσωματωθεί το μήνυμα, τα υπογραμμισμένα bit θα αντικατασταθούν με τα bit από το μήνυμα. Η εικόνα που προκύπτει δεν διαφέρει σχεδόν από την αρχική.

Πρωτότυπο
Εικόνα με ενσωματωμένο μήνυμα

Στεγανογραφία σε GIF
Στεγανογραφία σε GIF

Μέθοδος επέκτασης παλέτας

Όταν ανοίγετε μια εικόνα που περιέχει ένα μήνυμα χρησιμοποιώντας αυτήν τη μέθοδο, θα δείτε την ακόλουθη εικόνα:

Στεγανογραφία σε GIF

Είναι σαφές ότι αυτή η μέθοδος δεν θα λειτουργήσει για πλήρεις δραστηριότητες κατασκοπείας και μπορεί να απαιτεί πρόσθετη κρυπτογράφηση του μηνύματος.

Η κρυπτογράφηση/αποκρυπτογράφηση σε κινούμενες εικόνες λειτουργεί ακριβώς όπως στις κανονικές στατικές εικόνες, αλλά το animation δεν έχει σπάσει.

Πηγές που χρησιμοποιήθηκαν:

Download:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο