Jump to content
  • 1. Introdução

    Dumps de memória, discos ou arquivos binários em geral podem conter outros arquivos dentro. Por exemplo, um arquivo TAR (agrupado, sem compactação) praticamente concatena vários arquivos e um só. O arquivo resultante contém todos os bytes dos arquivos de entrada e mais algumas informações que o formato TAR exige. O mesmo acontece com dumps feitos de memória gerados com software de cópia bit a bit, como o dd, parte integrante do coreutils [1], do projeto GNU. Estes podem, e geralmente é que acontece, conter vários arquivos de diferentes tipos. Neste artigo mostrarei uma forma manual, usando ferramentas básicas, para extrair dados de dentro destes dumps.

    Ao ministrar um dos cursos de segurança na 4Linux, gerei este desafio: O objetivo é extrair dados válidos de um dump de 1 MB fornecido. Dentro dele, há dois arquivos. Não sabemos que tipo de arquivo há dentro do dump, então temos que procurar.

    NOTA: Este texto é didático e você pode seguir utilizando uma máquina com Linux para fazer a análise do dump. Basta baixar o binário dump.bin.

    2. Como procurar tipos de arquivo

    Antes de tudo, é preciso saber o que se vai procurar. Você pode querer procurar milhares de tipos diferentes, mas terá que conhecer o formato de cada um deles para identificar possíveis bytes dentro do dump. Neste artigo vamos buscar por arquivos BMP e GIF mas este texto pode servir de guia para buscar qualquer tipo. Para isso precisamos estudar o a especificação de cada formato. Este tipo de documento quem fez é o desenvolvedor do formato e é particularmente interessante para desenvovledores de software que trabalharão com imagens como editores, visualizadores, browsers etc. A Wikipedia é uma ótima fonte para isso, mas o site dos consórcios que desenvolvem e mantêm os padrões também podem ser utilizados. Basta googlar por “<extensão> file format” e você achará muitos links.;)

    3. Analisando a especificação do bitmap (BMP)

    Para o nosso interesse, que não é desenvolver um aplicativo que trabalhe com o formato, mas sim entender o básico do formato, a parte mais interessante está na estrutura de um arquivo bitmap [2], onde cada grupo de bytes de um arquivo deste tipo ganha um significado. O que queremos é saber os bytes que definem o início de um arquivo bitmap. Em outras palavras, queremos saber que bytes estão no cabeçalho (header) de um arquivo bitmap. Veja que na especificação consta:

    the header field used to identify the BMP & DIB file is 0x42 0x4D in hexadecimal, same as BM in ASCII

    Achamos. Isso nos leva a crer que um bitmap tradicional começa com os bytes 0x42 e 0x4d, o mesmo que BM em ASCII. Sabendo disso podemos checar as strings dentro de nosso dump e fazer um filtro pela string “BM”, certo?

    3.1 Buscando a string que identifica um bitmap

    Para isso, nada mais indicado que o strings, do conjunto binutils [3], também do projeto GNU (thanks FSF!).

    $ strings -t x dump.bin | grep BM
    6ae6d BMP&
    8f699 g,UBM_
    be081 ?O~BMY
    ed589 YBMG
    f63c2 xBM5

    A opção “-t x” do strings faz com que ele imprima os endereços (posições) em hexa das strings que encontrou.

    NOTA: Já se perguntou como funciona o strings? Uma ideia é ler cada byte e testar se este é imprimível, ou seja, tem representação gráfica na tabela ASCII. Caso seja imprimível, só mandar pra tela. Um código de exemplo seria:

    #include <stdio.h>
    #include <string.h>
      
    int main(int argc, char *argv[1])
    {
    	unsigned char byte;
    	FILE *fp = fopen(argv[1], rb”);
    
    	while (fread(&byte, sizeof(unsigned char), 1, fp))
    		putchar(isprint(byte) ? byte : n’);
    
    	fclose(fp);
    }

    Código apenas para fins didáticos. Precisaria de melhorias se fosse usado pra valer.;)

    De acordo com a saída do strings, parece que temos 5 bitmaps dentro deste arquivo. Será? Na verdade não podemos garantir. Os bytes referentes aos caracteres ‘B’ e ‘M’ podem ser parte de outro arquivo, de uma música, de um texto ou simplesmente parte de uma instrução de algum programa. Não pode-se garantir que é um bitmap. Temos que analisar!

    3.2 Analisando a primeira ocorrência da string BM

    Sabemos a posição da primeira ocorrência porque o comando strings nos deu. Agora vamos usar um visualizador hexa/ascii para ver o que há próximo destes bytes. No conjunto coreutils há o od, mas eu estou acostumado ao hexdump (hd), do BSD:

    $ hd -s 0x6ae6d -n 32 dump.bin
    0006ae6d  42 4d 50 26 bf 29 c1 f3  f3 81 f0 99 aa f0 aa fd  |BMP&.)……….|
    0006ae7d  d3 d1 e0 3e 92 54 84 b1  18 74 4e b5 00 c1 ce 06  |…>.T…tN…..|

    A opção “-s 0x6ae6d” faz com que o hd pule (skip) essa quantidade de bytes a partir do início do arquivo, caindo diretamente em nossa string. Já a opção “-n 32” faz com que o hd mostre apenas bytes à frente. Só estamos dando uma olhada, não precisamos ver o arquivo inteiro neste momento.

    A documentação também diz que os próximos 4 bytes de um bitmap representam o tamanho do arquivo em bytes. Então o tamanho da nossa *possível* imagem seriam os 4 bytes “50 26 bf 29”. Organizando-dos em little-endian (de trás para frente) pois estes representam um número inteiro e não texto, temos 0x29bf2650. Quanto dá isso em decimal? O bash responde:

    $ echo $((0x29bf2650))
    700393040

    O bc também responderia (mas as letras precisam estar em maiúsculo):

    $ echo “ibase=16;29BF2650” | bc
    700393040

    Quanto dá isso em megabytes?

    $ echo $((0x29bf2650 / 1024 / 1024))
    667

    Então nosso provável bitmap tem mais de 700 milhões de bytes, aproximadamente 667 MB. Impossível para um dump de 1 MB, certo? Descartado… esses bytes não fazem parte de um bitmap e foi somente coincidência a string “BM”, ainda mais seguida de um “P” (eu fiz isso sem querer hehe).

    Deixarei para o leitor analisar as outras ocorrências da string “BM”. Vá se divertir!

    4. Buscando a string que identifica um GIF

    Agora vamos buscar GIF. Assim como fizemos com o bitmap, vamos olhar a especificação do GIF [4]. De cara, vemos o header da última versão do formato, que deve ser composto pelos bytes 0x47 0x49 0x46 0x38 0x39 0x61, que representam em ASCII a string “GIF89a”. Se quiser confirmar, pode usar o comando ascii do Linux:

    $ sudo apt-get install ascii
    $ ascii

    Então vamos ao grep:

    $ strings -t x dump.bin | grep GIF89a
    80000 GIF89ae

    Opa, casamos com a string inteira. Seria muita coincidência esses 6 caracteres casarem se isso não for o início de um GIF? Não sei. Vamos analisar:

    $ hd -s 0x80000 -n 64 dump.bin
    00080000  47 49 46 38 39 61 65 01  1a 02 f7 00 00 bb 9c 91  |GIF89ae………|
    00080010  6e 5b 54 d9 ad 9c 6b 69  69 ca a4 94 e3 8c 7d 3f  |n[T…kii…..}?|
    00080020  1e 0e 33 0d 09 f6 cb ba  e1 b4 a3 e3 bb aa eb ca  |..3………….|
    00080030  8a c8 44 39 0b 05 04 6b  26 21 44 27 19 fa f2 e4  |..D9…k&!D’….|

    A documentação diz que depois do header de 6 bytes, temos 2 bytes identificando a largura (0x165) e mais 2 para a altura (0x21a), o que definiria um GIF de 357×538 pixels. Aceitável…

    O próximo byte, segundo o exemplo na documentação, quando é 0xf7, diz que o GIF tem 256 cores, dentre outras informações. Este byte bate com o nosso 0xf7. Parece mesmo ser um GIF…

    Vamos testar se é um GIF mesmo? Não precisamos ir até o final checando todos os campos. A maioria dos formatos de arquivo definem um ou mais bytes para marcar o fim do arquivo. No caso do GIF, veja na documentação que os últimos bytes são 0x00 (end) e 0x3b (GIF terminator). Lembre-se que isso é *específico* do formato GIF e não é regra para qualquer tipo de arquivo. Cada um tem seu formato.

    NOTA: Os formatos que não possuem bytes que marcam o fim geralmente possuem um campo que diz o tamanho completo do arquivo, como vimos no bitmap.

    4.1 Estratégia para extrair o possível GIF do dump

    Podemos usar a estratégia de extrair os bytes desde o ‘G’ do “GIF89a” até a primeira sequência 0x00 0x3b. Se for um GIF real, funcionaria, concorda?

    Vamos buscar então:

    $ hd -s 0x80000 dump.bin | grep –color “00 3b”
    00082b60  3d 97 87 80 00 00 3b 55  d9 60 ab 23 eb 24 ac f1  |=…..;U.`.#.$..|
    000ef4e0  2b 61 8d 77 00 3b 7c 58  62 23 4a dc 4c 15 42 da  |+a.w.;|Xb#J.L.B.|

    A primeira sequência que “00 3b” que o grep encontrou após o offset 0x80000 (início do GIF) está em 0x82b65, conseguiu visualizar? É só ir somando 1 em hexa, para cada byte. Neste offset está o 0x00 e em 0x82b66 está o 0x3b. Seguindo nossa teoria, o próximo byte, 0x55 em 0x82b67 não faz parte do GIF.

    Se diminuirmos este valor de 0x80000, teremos o tamanho total do suposto GIF. Vamos ver:

    $ echo $((0x82b67-0x80000))
    11111

    A resposta é em decimal. Só lembrando algumas informações que leventamos:

    Tipo: GIF
    Resolução: 357×538 pixels
    Tamanho: 11111 bytes (11 KB)

    4.2 Extração com o dd

    Se estivermos certo, veremos um GIF quando extrairmos esses bytes. Para isso, podemos usar o dd:

    $ dd if=dump.bin of=possivel.gif bs=1 count=11111 skip=$((0x80000))
    11111+0 registros de entrada
    11111+0 registros de saída
    11111 bytes (11 kB) copiados, 0,036675 s, 303 kB/s

    A opção bs (block size) informa ao dd para assumir blocos de 1 byte. A opção count diz quantos blocos ele vai ler, então vão dar exatamente 11111 bytes. E por fim a opção skip, que só aceita números em decimal (por isso a conversão com o bash) informa quantos bytes o dd vai pular para começar a ler, assim como a opção “-s” do hd. Temos que começar a extração do byte 0x80000, que é onde começa o GIF, lembra?

    Vamos conferir:

    $ file possivel.gif
    possivel.gif: GIF image data, version 89a, 357 x 538
    
    $ wc -c possivel.gif
    11111 possivel.gif

    Agora é só visualizar…:)

    5. Ferramentas para automatizar o trabalho

    Agora a notícia chata depois que você leu tudo isso: existem ferramentas que fazem tudo isso de maneira automatizada. Uma delas é o foremost [5], que conhece vários tipos de arquivo e é capaz de extrair com precisão a partir de dumps como este. No entanto, considero que mais importante que conhecer ferramentas é saber como elas funcionam e/ou como fazer as coisas sozinho. Sem elas. Say no to script kiddies!;)

    6. Referências

    A ilustração utilizada neste artigo foi gentilmente cedida por Anderson Barros [6] e seu uso foi autorizado.

    [1] http://www.gnu.org/software/coreutils/
    [2] http://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
    [3] http://www.gnu.org/software/binutils/
    [4] http://en.wikipedia.org/wiki/Graphics_Interchange_Format#Example_GIF_file
    [5] http://foremost.sourceforge.net/
    [6] http://anderson-barros.blogspot.com


    Revisão: Leandro Fróes
    • Curtir 1

    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.
    Note: Your post will require moderator approval before it will be visible.

    Guest

    • This will not be shown to other users.
    • Add a review...

      ×   Pasted as rich text.   Paste as plain text instead

        Only 75 emoji are allowed.

      ×   Your link has been automatically embedded.   Display as a link instead

      ×   Your previous content has been restored.   Clear editor

      ×   You cannot paste images directly. Upload or insert images from URL.


  • Similar Content

×
×
  • Create New...