Stéganographie dans les GIF

Introduction

Bonjour.
Il n’y a pas si longtemps, alors que j’étudiais à l’université, j’avais un projet sur le sujet « Méthodes logicielles de protection de l’information ». Le sujet consistait à créer un programme intégrant un message dans des fichiers au format GIF. J'ai décidé de le faire en Java.

Dans cet article, je vais décrire certains aspects théoriques ainsi que la façon dont ce petit programme a été créé.

Partie théorique

Format GIF

GIF (abréviation de Graphics Interchange Format — format pour l’échange d’images) est un format de stockage d'images graphiques capable de contenir des données compressées sans perte de qualité, avec une palette de jusqu'à 256 couleurs. Ce format a été développé en 1987 (GIF87a) par CompuServe pour la transmission d'images raster sur des réseaux. En 1989, le format a été modifié (GIF89a) pour ajouter le support de la transparence et de l’animation.

Les fichiers au format GIF ont une structure bloc. Ces blocs ont toujours une longueur fixe (ou celle-ci dépend de certains drapeaux), rendant pratiquement impossible de se tromper sur la localisation des blocs. La structure d'une image GIF non animée la plus simple au format GIF89a :

Stéganographie dans les GIF

Parmi tous les blocs de la structure, nous nous intéresserons ici au bloc de palette globale et aux paramètres qui concernent cette palette :

  • CT — présence d'une palette globale. Si ce drapeau est actif, la palette globale doit commencer immédiatement après le descripteur de l'écran logique.
  • Taille — taille de la palette et nombre de couleurs de l'image. Les valeurs de ce paramètre :

Taille
Nombre de couleurs
Taille de la palette, octets

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Méthodes de cryptage

Les méthodes de cryptage des messages dans les fichiers d'images utiliseront :

  • Méthode LSB (Least Significant Bit, bit le moins significatif)
  • Méthode de complément de la palette

Méthode LSB — méthode de stéganographie répandue. Elle consiste à remplacer les derniers bits significatifs dans le conteneur (dans notre cas, les octets de la palette globale) par les bits du message caché.

Ce programme utilisera les deux derniers bits des octets de la palette globale dans le cadre de cette méthode. Cela signifie que pour une image de 24 bits, où la couleur de la palette est représentée par trois octets pour le rouge, le bleu et le vert, après l'insertion du message, chaque composant de couleur changera au maximum de 3/255 nuances. Ce changement, d'une part, sera imperceptible ou difficile à percevoir pour l'œil humain, et d'autre part, ne sera pas discernable sur des dispositifs de sortie de basse qualité.

La quantité d'informations dépendra directement de la taille de la palette de l'image. Étant donné que la taille maximale de la palette est de 256 couleurs et que, si deux bits du message sont enregistrés dans chaque composant de couleur, la longueur maximale du message (avec la palette maximale dans l'image) est de 192 octets. Après l'insertion du message dans l'image, la taille du fichier ne change pas.

Méthode d'expansion de palette, fonctionnant uniquement pour la structure GIF. Elle sera la plus efficace dans les images avec de petites tailles de palette. Son principe consiste à augmenter la taille de la palette, fournissant ainsi un espace supplémentaire pour enregistrer les octets nécessaires à la place des octets de couleur. Si l'on considère que la taille minimale de la palette est de 2 couleurs (6 octets), la taille maximale du message inséré peut être de 256×3–6=762 octets. L'inconvénient est la faible sécurité cryptographique, le message inséré pouvant être lu avec n'importe quel éditeur de texte, si le message n'a pas été soumis à un chiffrement supplémentaire.

Partie pratique

Conception du programme

Tous les outils nécessaires pour la mise en œuvre des algorithmes de chiffrement et de déchiffrement se trouveront dans le paquet com.tsarik.steganography. Ce paquet comprend l'interface Encryptor avec les méthodes encrypt et decrypt, la classe Binary, permettant de travailler avec des tableaux de bits, ainsi que les classes d'exception UnableToEncryptException et UnableToDecryptException, qui doivent être utilisées dans les méthodes de l'interface Encryptor en cas d'erreurs d'encodage et de décodage respectivement.

Le paquet principal du programme com.tsarik.programs.gifed comprendra la classe exécutable du programme avec la méthode statique main, permettant de lancer le programme ; une classe, contenant les paramètres du programme ; et des paquets avec d'autres classes.

La mise en œuvre des algorithmes eux-mêmes sera présentée dans le package com.tsarik.programs.gifed.gif classes GIFEncryptorByLSBMethod et GIFEncryptorByPaletteExtensionMethod. Ces deux classes mettront en œuvre l'interface Encryptor.

Sur la base de la structure du format GIF, il est possible de composer un algorithme général d'insertion de messages dans la palette de l'image :

Stéganographie dans les GIF

Pour déterminer la présence d'un message dans l'image, il est nécessaire d'ajouter au début du message une certaine séquence de bits que le déchiffreur lit en premier et vérifie pour son intégrité. Si elle ne correspond pas, on considère qu'il n'y a pas de message caché dans l'image. Ensuite, il faut indiquer la longueur du message. Puis le texte du message lui-même.

Diagramme des classes de l'ensemble de l'application :

Stéganographie dans les GIF

Mise en œuvre du programme

La mise en œuvre de l'ensemble du programme peut être divisée en deux composants : la mise en œuvre des méthodes de chiffrement et de déchiffrement de l'interface Encryptor, dans les classes GIFEncryptorByLSBMethod et GIFEncryptorByPaletteExtensionMethod, et la mise en œuvre de l'interface utilisateur.

Examinons la classe GIFEncryptorByLSBMethod.

Stéganographie dans les GIF

Champs firstLSBit et secondLSBit contiennent les numéros des bits de chaque octet de l'image, dans lesquels le message doit être inséré et d'où il doit être lu. Le champ checkSequence contient la séquence de bits de contrôle pour assurer la reconnaissance du message intégré. La méthode statique getEncryptingFileParameters retourne les paramètres du fichier spécifié et les caractéristiques du message potentiel.

L'algorithme de la méthode encrypt classe GIFEncryptorByLSBMethod:

Stéganographie dans les GIF

Et son code :

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Le fichier d'entrée est nul");
	}
	if (out == null) {
		throw new NullPointerException("Le fichier de sortie est nul");
	}
	if (text == null) {
		throw new NullPointerException("Le texte est nul");
	}
	
	// lire les octets du fichier d'entrée
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// vérifier le format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Le fichier d'entrée a un format GIF incorrect");
	}
	
	// lire la propriété de taille de palette à partir des trois premiers bits dans le 10ème octet du fichier
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculer le nombre de couleurs et la longueur possible du message
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // un octet pour le contrôle et un octet pour la longueur du message
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Le texte est trop long");
	}
	
	int n = 13;
	
	// écrire la séquence de contrôle
	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++;
	}
	
	// écrire la longueur du texte
	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++;
	}
	
	// écrire le 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++;
		}
	}
	
	// écrire le fichier de sortie
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

L'algorithme et le code source de la méthode decrypt classe GIFEncryptorByLSBMethod:

Stéganographie dans les GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Le fichier d'entrée est nul");
	}
	
	// lire les octets du fichier d'entrée
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// vérifier le format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Le fichier d'entrée a un format GIF incorrect");
	}
	
	// lire la taille de la palette à partir des trois premiers bits dans le dixième octet du fichier
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculer le nombre de couleurs et la longueur possible du message
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// un octet pour la vérification et un octet pour la longueur du message
	
	int n = 13;
	
	// lire la séquence de vérification
	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("Il n'y a pas de message chiffré dans l'image (La séquence de vérification est incorrecte)");
	}
	
	// lire la longueur du texte
	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("La longueur du texte décodé est inférieure à 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Il n'y a pas de messages (La longueur du message décodé (" + textLength + ") est inférieure à la longueur possible du message (" + possibleTextLength + "))");
	}
	
	// lire les bits de texte et créer des octets de texte
	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);
}

Implémentation de la classe GIFEncryptorByPaletteExtensionMethod sera similaire, mais diffère par la méthode de sauvegarde/de lecture des informations.

Dans la classe MainFrame sont décrites les méthodes « enveloppes » : encryptImage(Encryptor encryptor) et decryptImage(Encryptor encryptor), qui traitent les résultats des méthodes de l'interface Encryptor et interagissent avec l'utilisateur, c'est-à-dire ouvrent une boîte de dialogue pour sélectionner des fichiers, affichent des messages d'erreur, etc.; ainsi que d'autres méthodes : openImage(), permettant à l'utilisateur de sélectionner une image, exit(), assurant la sortie de l'application. Ces méthodes sont appelées depuis Actiondes éléments de menu correspondants. Dans cette classe, des méthodes auxiliaires supplémentaires sont également mises en œuvre : createComponents() — création des composants du formulaire, loadImageFile(File f) — chargement d'une image dans un composant spécial à partir d'un fichier. L'implémentation de la classe GIFEncryptorByPaletteExtensionMethod est similaire à l'implémentation de la classe GIFEncryptorByLSBMethod, la différence principale réside dans la manière d'écrire et de lire les octets du message depuis la palette.

Fonctionnement du programme

La méthode LBS

Supposons qu'il existe une telle image :

Stéganographie dans les GIF

Dans cette image, la palette se compose de 256 couleurs (comme Paint les sauvegarde). Les quatre premières couleurs : blanc, noir, rouge, vert. Les autres couleurs sont noires. La séquence des bits de la palette globale sera la suivante :

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Stéganographie dans les GIF

Après l'insertion du message, les bits soulignés seront remplacés par des bits provenant du message. L'image obtenue sera presque indistinguable de l'originale.

Original
Image avec message inséré

Stéganographie dans les GIF
Stéganographie dans les GIF

Méthode d'expansion de palette

En ouvrant l'image dans laquelle le message a été inséré par cette méthode, on peut découvrir un tel tableau :

Stéganographie dans les GIF

Il est évident que pour une activité d'espionnage complète, cette méthode ne conviendrait pas et nécessiterait peut-être un cryptage supplémentaire du message.

Le cryptage/décryptage dans les images animées fonctionne comme dans les images statiques normales, tout en préservant l'animation.

Sources utilisées :

Télécharger :

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster