Esteganografia em GIF

Introdução

Bem-vindo.
Não faz muito tempo, quando eu estava na universidade, havia um curso na disciplina “Métodos de software para segurança da informação”. A tarefa exigia que criássemos um programa que incorporasse uma mensagem em arquivos GIF. Decidi fazer isso em Java.

Neste artigo irei descrever alguns pontos teóricos, bem como como este pequeno programa foi criado.

Parte teórica

Formato GIF

GIF (Graphics Interchange Format - formato de troca de imagens) é um formato de armazenamento de imagens gráficas, capaz de armazenar dados compactados sem perda de qualidade em um formato de até 256 cores. Este formato foi desenvolvido em 1987 (GIF87a) pela CompuServe para transmissão de imagens raster em redes. Em 1989, o formato foi modificado (GIF89a), foi adicionado suporte para transparência e animação.

Os arquivos GIF têm uma estrutura de blocos. Esses blocos sempre têm comprimento fixo (ou depende de algumas flags), então é quase impossível errar sobre a localização de cada bloco. A estrutura da imagem GIF não animada mais simples no formato GIF89a:

Esteganografia em GIF

De todos os blocos da estrutura, neste caso estaremos interessados ​​no bloco da paleta global e nos parâmetros responsáveis ​​pela paleta:

  • CT — presença de uma paleta global. Se esse sinalizador estiver definido, a paleta global deverá começar imediatamente após o identificador de tela lógica.
  • Size — tamanho da paleta e número de cores na imagem. Valores para este parâmetro:

Tamanho
Número de cores
Tamanho da 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 criptografia

Os seguintes métodos serão usados ​​para criptografar mensagens em arquivos de imagem:

  • Método LSB (bit menos significativo)
  • Método de adição de paleta

Método LSB - um método comum de esteganografia. Consiste em substituir os últimos bits significativos do contêiner (no nosso caso, os bytes da paleta global) pelos bits da mensagem oculta.

O programa usará os dois últimos bits dos bytes da paleta global como parte deste método. Isso significa que para uma imagem de 24 bits, onde a paleta de cores tem três bytes para vermelho, azul e verde, após incorporar uma mensagem nela, cada componente de cor mudará no máximo 3/255 gradações. Tal mudança, em primeiro lugar, será invisível ou difícil de perceber ao olho humano e, em segundo lugar, não será visível em dispositivos de saída de informação de baixa qualidade.

A quantidade de informações dependerá diretamente do tamanho da paleta de imagens. Como o tamanho máximo da paleta é de 256 cores, e se dois bits de mensagem forem gravados no componente de cada cor, o comprimento máximo da mensagem (com a paleta máxima na imagem) é de 192 bytes. Depois que a mensagem é incorporada à imagem, o tamanho do arquivo não muda.

Método de expansão de paleta, que funciona apenas para a estrutura GIF. Será mais eficaz em imagens com uma paleta pequena. Sua essência é aumentar o tamanho da paleta, proporcionando assim espaço adicional para escrever os bytes necessários no lugar dos bytes de cores. Se considerarmos que o tamanho mínimo da paleta é de 2 cores (6 bytes), então o tamanho máximo da mensagem incorporada pode ser 256 × 3–6 = 762 bytes. A desvantagem é a baixa segurança criptográfica; a mensagem incorporada pode ser lida usando qualquer editor de texto se a mensagem não tiver sido submetida a criptografia adicional.

Parte prática

Desenho do programa

Todas as ferramentas necessárias para implementar algoritmos de criptografia e descriptografia serão incluídas no pacote com.tsarik.steganography. Este pacote inclui a interface Encryptor com métodos encrypt и decrypt, Aula Binary, que fornece a capacidade de trabalhar com matrizes de bits, bem como classes de exceção UnableToEncryptException и UnableToDecryptException, que deve ser usado em métodos de interface Encryptor em caso de erros de codificação e decodificação, respectivamente.

Pacote principal do programa com.tsarik.programs.gifed incluirá uma classe de programa executável com um método estático main, permitindo que você execute o programa; uma classe que armazena parâmetros de programa; e pacotes com outras classes.

A implementação dos próprios algoritmos será apresentada no pacote com.tsarik.programs.gifed.gif Aulas GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Ambas as classes implementarão a interface Encryptor.

Com base na estrutura do formato GIF, você pode criar um algoritmo geral para inserir uma mensagem na paleta de imagens:

Esteganografia em GIF

Para determinar a presença de uma mensagem em uma imagem, é necessário adicionar uma determinada sequência de bits ao início da mensagem, que o decodificador lê primeiro e verifica a correção. Se não corresponder, considera-se que não há mensagem oculta na imagem. Em seguida, você precisa especificar o comprimento da mensagem. Depois, o próprio texto da mensagem.

Diagrama de classes de todo o aplicativo:

Esteganografia em GIF

Implementação do programa

A implementação de todo o programa pode ser dividida em dois componentes: implementação de métodos de criptografia e descriptografia de interface Encryptor, nas aulas GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethode a implementação da interface do usuário.

Considere a classe GIFEncryptorByLSBMethod.

Esteganografia em GIF

campos firstLSBit и secondLSBit contêm os números de bits de cada byte da imagem na qual a mensagem deve ser inserida e de onde a mensagem deve ser lida. Campo checkSequence armazena uma sequência de bits de verificação para garantir o reconhecimento da mensagem incorporada. Método estático getEncryptingFileParameters retorna os parâmetros do arquivo especificado e as características da mensagem potencial.

Algoritmo de método encrypt classe GIFEncryptorByLSBMethod:

Esteganografia em GIF

E seu 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 e código fonte do método decrypt classe GIFEncryptorByLSBMethod:

Esteganografia em 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);
}

Implementação da classe GIFEncryptorByPaletteExtensionMethod será semelhante, apenas o método de salvar/ler informações é diferente.

Na aula MainFrame métodos de wrapper são descritos: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), processando os resultados dos métodos de interface Encryptor e interagir com o usuário, ou seja, abrir uma caixa de diálogo de seleção de arquivos, mostrar mensagens de erro, etc.; bem como outros métodos: openImage(), permitindo ao usuário selecionar uma imagem, exit(), que sai do aplicativo. Esses métodos são chamados de Actionitens de menu correspondentes. Esta classe implementa adicionalmente métodos auxiliares: createComponents() - criação de componentes de formulário, loadImageFile(File f) — carregar uma imagem em um componente especial de um arquivo. Implementação da classe GIFEncryptorByPaletteExtensionMethod semelhante à implementação da classe GIFEncryptorByLSBMethod, a principal diferença está na maneira como os bytes da mensagem são gravados e lidos na paleta.

operação do programa

Método LBS

Digamos que exista uma imagem como esta:

Esteganografia em GIF

Nesta imagem, a paleta consiste em 256 cores (conforme o Paint salva). As primeiras quatro cores são: branco, preto, vermelho, verde. Outras cores são pretas. A sequência de bits da paleta global será a seguinte:

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

Esteganografia em GIF

Depois que a mensagem for incorporada, os bits sublinhados serão substituídos pelos bits da mensagem. A imagem resultante quase não difere da original.

Original
Imagem com mensagem incorporada

Esteganografia em GIF
Esteganografia em GIF

Método de expansão de paleta

Ao abrir uma imagem contendo uma mensagem usando este método, você verá a seguinte imagem:

Esteganografia em GIF

É claro que este método não funcionará para atividades de espionagem completas e poderá exigir criptografia adicional da mensagem.

A criptografia/descriptografia em imagens animadas funciona exatamente como em imagens estáticas normais, mas a animação não é interrompida.

Fontes usadas:

Baixar:

Fonte: habr.com

Adicionar um comentário