Geral

Arquivo binário dentro de outro arquivo binário

esconder um arquivo

Já escrevi anteriormente como esconder um arquivo zip dentro de uma imagem com extensão .jpg, como pode ser visto nesse artigo. Porém no outro artigo mostrei como fazê-lo diretamente pelo shell do Linux, mas também não expliquei muitos detalhes, por isso, para dar continuidade na série de manipulação de arquivo binário, vamos compreender melhor como isso funciona.

Header e footer do arquivo binário .jpg

Dentro de um arquivo binário .jpg temos duas informações que determinam o início e o fim do arquivo. Não é o único arquivo que contém essas marcas, mas estou falando dele porque é o que utilizaremos nesse exemplo.

Data carving

Data carving é utilizado para buscar por arquivos na mídia de armazenamento quando não resta outra opção. Isso porque um arquivo não alocado pode estar distribuído de forma entrópica na mídia de armazenamento, de forma que seja impossível recuperá-lo, mas também é possível que ele seja armazenado de forma sequencial pelos inodes. Nesse último caso, a recuperação independe de um sistema de arquivos.

Quando alocamos um arquivo em uma mídia de armazenamento que contém um sistema de arquivos (seja um HD, SDD ou uma memória flash), as informações do exif são guardadas no sistema de arquivos. Quando “apagamos” um arquivo, ele não foi realmente apagado, pois isso demandaria mais I/O do disco. O que acontece é que simplesmente o sistema de arquivos abre a “porta” para uma nova entrada em todos os inodes cujo arquivo estava alocado. Isso significa que um próximo arquivo salvo em disco pode ser alocado parcialmente nesses inodes. Essa técnica é utilizada em perícia forense apenas quando não há outro meio.

Bem, e o que isso tudo tem a ver com esconder um arquivo zip dentro de um arquivo de imagem?

Assinatura

Como citei anteriormente, os arquivos possuem uma assinatura que identificam seu tipo. Essa assinatura marca o início e o fim da região binária que contém os dados relevantes. Para nosso exemplo, utilizaremos a imagem a seguir:

stegano - do bit ao byte - arquivo binário

Apesar da imagem parecer um arquivo .png devido ao fundo transparente, é um .jpg, pois trata-se de um screenshot. Bem, vamos agora analisar as informações que essa imagem possui alocada no sistema de arquivos. Para isso, utilize um sistema Linux, ainda que rodando em uma máquina virtual com o VirtualBox, por exemplo.

O arquivo se chama bB-logo-3d.jpg. Para coletar as informações, utilize o comando exiftool, passando o nome de arquivo como parâmetro:

As informações retornadas devem ser similares a essas:

esconder arquivos - exif - arquivo binário

Daí você pode pensar: “Essa é fácil, basta ler a extensão”. Mas na verdade o exif não está no nome do arquivo e sim nele próprio. Para testar, remova a extensão do arquivo e repita a operação, caso deseje comprovar.

Mas onde está essa informação? Bem, é o que veremos agora, utilizando outra ferramenta; o hexdump:

Se você não é íntimo da linha de comandos do Linux, explico. O programa hexdump retorna o código binário de um arquivo. O parâmetro “-C” retorna o valor em hexa+ASCII, de forma que possamos cruzar referências. O pipe (a barra em pé ‘|’) pega a saída do primeiro comando e envia para o segundo comando – no caso, o programa de paginação de arquivos, chamado more.

header

A assinatura do header de um arquivo com extensão .jpg é FF D8 FF. Chequemos, utilizando o comando supracitado:

esconder arquivos - header jpg - arquivo binário

Isso não garante que o arquivo seja realmente um .jpg, afinal é possível forjar um arquivo binário, como vimos no artigo anterior. Mas esse é o indício do tipo de tratamento que o arquivo deve receber. Por isso que os tipos mime do sistema operacional relacionam um arquivo a um respectivo programa.

footer

O “ponto final” do arquivo é identificado pelo hexa FF D9. Para procurar na saída que aparece no terminal onde foi executado o comando, use a barra (‘/’) seguido do valor hexa, como mostrado ao início do parágrafo.

esconder arquivos - footer jpg - arquivo binário

A saída do programa hexdump mostra 16 bytes por linha (representados em hexa, que vai de 00 a FF, cujo valor varia entre 0 e 255 para cada byte). À esquerda está o número de bytes exibidos; 10 para 16, 20 para 32 e assim por diante. O footer desse arquivo .jpg está em 2EB0. Isso significa que para o programa que exibirá o arquivo, apenas os dados entre o header e o footer serão considerados.

Esconder um arquivo dentro de uma imagem .jpg

O que faremos então é concatenar o segundo arquivo dentro do primeiro. Quando um programa atuar sobre a imagem com extensão .jpg, tudo irá transcorrer de forma natural, como se nada estivesse fora do padrão, mas ao final do arquivo teremos anexado um arquivo .zip.

Gravar o arquivo zip dentro da imagem jpg

Prepare um arquivo .zip simples. crie um arquivo .txt contendo o texto “hello world”, por exemplo. Feito isso, comprima-o.

Agora vamos guardar o arquivo stegano.txt.gz dentro do arquivo bB-logo.3d.jpg. Escreva um arquivo.py ou execute direto dentro do interpretador Python, chamando o comando python sem utilizar parâmetros:

O que fizemos foi ler os arquivos .jpg.gz de forma binária, então reescrevemos para um novo arquivo, adicionando o arquivo zipado ao final da imagem jpg.

O novo arquivo foi salvo com o nome new_f.jpg, contendo a imagem e o arquivo zip. Será que está corrompido?

esconder um arquivo - arquivo binário

Abriu sem problemas. Podemos verificar onde está o arquivo utilizando novamente o programa hexdump:

esconder um arquivo - stegano - arquivo binário

Comparando os dois arquivos, fica fácil ver a concatenação:

esconder um arquivo - compare - arquivo binário

Recuperar um arquivo escondido

E para extrair esse arquivo de dentro da imagem .jpg  agora?

Para salvar os dados, primeiro deve-se ter em mente que o arquivo binário original tem N bytes. O novo dado está em N+1. Mas considerando que a leitura posicional começa em 0, se lermos o número de bytes do arquivo original já teremos automaticamente N+1, porque o primeiro byte é contado como unidade 1, não como unidade 0. Desse modo, basta pegar o número total de bytes do arquivo original para usar como ponto de partida para a leitura dos dados inseridos em formato zip:

arquivo binário

O novo arquivo tem 11.999 bytes, enquanto o arquivo original tem 11.995. Posicionalmente, o arquivo original vai de 0 até 11.994. O valor 11.995 é o primeiro byte do arquivo concatenado. Ficou claro agora?

arquivo binário

O arquivo foi salvo como data.txt.gz. Descomprima-o e veja o conteúdo do arquivo resultante:

arquivo binário

Oras, não é que funcionou?

Próximo artigo relacionado

Já temos informação o suficiente para manipular  um arquivo binário, agora precisamos brincar com isso em uma MCU. No próximo artigo já poderemos começar a brincadeira, espero que acompanhe e até a próxima!