Esteganografía en GIF

introducción

Bienvenido.
No hace mucho, cuando estudiaba en la universidad, había un curso en la disciplina "Métodos de software de seguridad de la información". La tarea requería que creáramos un programa que incrustara un mensaje en archivos GIF. Decidí hacerlo en Java.

En este artículo describiré algunos puntos teóricos, así como cómo se creó este pequeño programa.

La parte teórica

formato gif

GIF (Formato de intercambio de gráficos: un formato para intercambiar imágenes) es un formato para almacenar imágenes gráficas, capaz de almacenar datos comprimidos sin pérdida de calidad en un formato de hasta 256 colores. Este formato fue desarrollado en 1987 (GIF87a) por CompuServe para transmitir imágenes rasterizadas a través de redes. En 1989, se modificó el formato (GIF89a), se agregó soporte para transparencia y animación.

Los archivos GIF tienen una estructura de bloques. Estos bloques siempre tienen una longitud fija (o depende de algunas banderas), por lo que es casi imposible equivocarse sobre dónde está ubicado cada bloque. La estructura de la imagen GIF no animada más simple en formato GIF89a:

Esteganografía en GIF

De todos los bloques de la estructura, en este caso nos interesará el bloque de paleta global y los parámetros responsables de la paleta:

  • CT — presencia de una paleta global. Si se establece este indicador, la paleta global debe comenzar inmediatamente después del identificador de pantalla lógica.
  • Size — tamaño de la paleta y número de colores en la imagen. Valores para este parámetro:

Tamaño
Número de colores
Tamaño de paleta, 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

Métodos de cifrado

Se utilizarán los siguientes métodos para cifrar mensajes en archivos de imagen:

  • Método LSB (bit menos significativo)
  • Método de adición de paleta

método LSB - un método común de esteganografía. Consiste en sustituir los últimos bits significativos del contenedor (en nuestro caso, los bytes de la paleta global) por los bits del mensaje oculto.

El programa utilizará los dos últimos bits de los bytes de la paleta global como parte de este método. Esto significa que para una imagen de 24 bits, donde la paleta de colores es de tres bytes para rojo, azul y verde, después de incrustar un mensaje en ella, cada componente de color cambiará en un máximo de 3/255 gradaciones. Tal cambio, en primer lugar, será invisible o difícil de notar para el ojo humano y, en segundo lugar, no será visible en dispositivos de salida de información de baja calidad.

La cantidad de información dependerá directamente del tamaño de la paleta de imágenes. Dado que el tamaño máximo de la paleta es de 256 colores, y si se escriben dos bits de mensaje en el componente de cada color, entonces la longitud máxima del mensaje (con la paleta máxima en la imagen) es de 192 bytes. Una vez que el mensaje está incrustado en la imagen, el tamaño del archivo no cambia.

Método de expansión de paleta, que sólo funciona para la estructura GIF. Será más efectivo en imágenes con una paleta pequeña. Su esencia es que aumenta el tamaño de la paleta, proporcionando así espacio adicional para escribir los bytes necesarios en lugar de los bytes de color. Si consideramos que el tamaño mínimo de la paleta es de 2 colores (6 bytes), entonces el tamaño máximo del mensaje incrustado puede ser 256 × 3–6 = 762 bytes. La desventaja es la baja seguridad criptográfica; el mensaje incrustado se puede leer usando cualquier editor de texto si el mensaje no ha sido sometido a cifrado adicional.

Parte práctica

Diseño de programa

Todas las herramientas necesarias para implementar algoritmos de cifrado y descifrado se incluirán en el paquete. com.tsarik.steganography. Este paquete incluye la interfaz. Encryptor con metodos encrypt и decrypt, clase Binary, que brinda la capacidad de trabajar con matrices de bits, así como con clases de excepción UnableToEncryptException и UnableToDecryptException, que debe usarse en métodos de interfaz Encryptor en caso de errores de codificación y decodificación respectivamente.

Paquete de programa principal com.tsarik.programs.gifed incluirá una clase de programa ejecutable con un método estático main, permitiéndole ejecutar el programa; una clase que almacena parámetros del programa; y paquetes con otras clases.

La implementación de los propios algoritmos se presentará en el paquete. com.tsarik.programs.gifed.gif clases GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Ambas clases implementarán la interfaz. Encryptor.

Según la estructura del formato GIF, puede crear un algoritmo general para introducir un mensaje en la paleta de imágenes:

Esteganografía en GIF

Para determinar la presencia de un mensaje en una imagen, es necesario agregar una determinada secuencia de bits al comienzo del mensaje, que el decodificador lee primero y verifica su corrección. Si no coincide, se considera que no hay ningún mensaje oculto en la imagen. A continuación debe especificar la longitud del mensaje. Luego el texto del mensaje en sí.

Diagrama de clases de toda la aplicación:

Esteganografía en GIF

Implementación del programa.

La implementación de todo el programa se puede dividir en dos componentes: implementación de métodos de cifrado y descifrado de interfaz. Encryptor, en clases GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethody la implementación de la interfaz de usuario.

considere la clase GIFEncryptorByLSBMethod.

Esteganografía en GIF

campos firstLSBit и secondLSBit contienen los números de bits de cada byte de la imagen en la que se debe ingresar el mensaje y desde donde se debe leer el mensaje. Campo checkSequence almacena una secuencia de bits de verificación para garantizar el reconocimiento del mensaje incrustado. método estático getEncryptingFileParameters devuelve los parámetros del archivo especificado y las características del mensaje potencial.

Algoritmo del método encrypt clase GIFEncryptorByLSBMethod:

Esteganografía en GIF

Y su código:

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

Algoritmo y código fuente del método. decrypt clase GIFEncryptorByLSBMethod:

Esteganografía en 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);
}

Implementación de la clase. GIFEncryptorByPaletteExtensionMethod será similar, solo el método de guardar/leer información es diferente.

En la clase MainFrame Los métodos de contenedor se describen: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), procesando los resultados de los métodos de interfaz. Encryptor e interactuar con el usuario, es decir, abrir un cuadro de diálogo de selección de archivos, mostrar mensajes de error, etc.; así como otros métodos: openImage(), permitiendo al usuario seleccionar una imagen, exit(), que sale de la aplicación. Estos métodos se llaman desde Actionlos elementos de menú correspondientes. Esta clase implementa adicionalmente métodos auxiliares: createComponents() - creación de componentes de formulario, loadImageFile(File f) — cargar una imagen en un componente especial desde un archivo. Implementación de la clase. GIFEncryptorByPaletteExtensionMethod similar a la implementación de la clase GIFEncryptorByLSBMethod, la principal diferencia está en la forma en que se escriben y leen los bytes de mensaje desde la paleta.

Operación del programa

método LBS

Digamos que hay una imagen como esta:

Esteganografía en GIF

En esta imagen, la paleta consta de 256 colores (como guarda Paint). Los primeros cuatro colores son: blanco, negro, rojo, verde. Otros colores son el negro. La secuencia de bits de la paleta global será la siguiente:

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

Esteganografía en GIF

Una vez incrustado el mensaje, los bits subrayados se reemplazarán con los bits del mensaje. La imagen resultante casi no se diferencia del original.

Original
Imagen con mensaje incrustado

Esteganografía en GIF
Esteganografía en GIF

Método de expansión de paleta

Cuando abres una imagen que contiene un mensaje usando este método, verás la siguiente imagen:

Esteganografía en GIF

Está claro que este método no funcionará para actividades de espionaje en toda regla y puede requerir un cifrado adicional del mensaje.

El cifrado/descifrado de imágenes animadas funciona igual que en imágenes estáticas normales, pero la animación no se interrumpe.

Fuentes utilizadas:

Descargar:

Fuente: habr.com

Añadir un comentario