DEV Community

Cover image for Busca em arquivos binários
Hilton Fernandes
Hilton Fernandes

Posted on

Busca em arquivos binários

Algumas vezes é necessário fazer buscas em arquivos binários. Há relativamente poucos tutoriais disponíveis na Web sobre o tema, apesar das ferramentas disponíveis no Linux oferecerem recursos para isso.

Este tutorial é escrito para apresentar esses recursos.

Visualização do arquivo com hexdump

O programa hexdump não está disponível nas instalações padrão do Linux.

Assim, é preciso instalalá-lo. Em distribuições derivadas do Debian (como Ubuntu, Linux Mint etc.), isto pode ser feito com o familiar comando

apt install hexdump
Enter fullscreen mode Exit fullscreen mode

A partir daí, uma apresentação muito conveniente é dada pela opção -C, que mostrará o conteúdo do arquivo em hexadecimal e ao lado, uma tentativade mostrá-lo em ASCII.

Vamos usar, nos exemplos a seguir, o conto Quem boa cama faz* de Machado de Assis, que é um pequeno arquivo PDF disponível para ser baixado legal e gratuitamente em

https://dominiopublico.mec.gov.br/download/texto/ua000225.pdf

É um texto breve, de 19 páginas, com tamanho de apenas 64 kb.

Segue a apresentação das primeiras 10 linhas, obtidas com os comandos

hexdump -C ua000225.pdf | head

Neste caso o programa head mostra as 10 primeiras linhas da saída da execução de hexdump -C

A barra em pé | chamada de pipe ou "canalização", conecta a saída de hexdump -C com a entrada de head.

00000000  25 50 44 46 2d 31 2e 32  20 0d 0a 25 e2 e3 cf d3  |%PDF-1.2 ..%....|
00000010  0d 0a 20 0d 0a 38 20 30  20 6f 62 6a 0d 0a 3c 3c  |.. ..8 0 obj..<<|
00000020  0d 0a 2f 4c 65 6e 67 74  68 20 39 20 30 20 52 0d  |../Length 9 0 R.|
00000030  0a 2f 46 69 6c 74 65 72  20 2f 46 6c 61 74 65 44  |./Filter /FlateD|
00000040  65 63 6f 64 65 20 0d 0a  3e 3e 0d 0a 73 74 72 65  |ecode ..>>..stre|
00000050  61 6d 0d 0a 48 89 85 96  dd 6e 62 39 0c c7 9f 80  |am..H....nb9....|
00000060  77 c8 65 47 da 93 c6 76  3e 7b 35 14 a8 54 69 a7  |w.eG...v>{5..Ti.|
00000070  d3 d1 d2 3b 6e ce 00 dd  65 05 54 0b 6a 2b f5 e9  |...;n...e.T.j+..|
00000080  c7 49 ce a1 b4 98 1d 21  50 48 9c ff cf 71 e2 38  |.I.....!PH...q.8|
00000090  d7 d3 81 c5 a4 42 8c 1a  9d 52 d3 f1 c0 28 a3 9d  |.....B...R...(..|
Enter fullscreen mode Exit fullscreen mode

Os arquivos PDF possuem muitas informações codificadas em binário, mas também têm conteúdo em texto ASCII. Por exemplo, neste caso, o arquivo contém o texto /FlateDecode.

Outras informações podem ser obtidas paginando a execução de hexdump -C por mais linhas, usando, por exemplo, hexdump -C | less.

Busca com grep

Para buscar um padrão específico em um arquivo, o comando grep é uma espécie canivete suíço -- com múltiplas utilidades.

Uma busca simples em um arquivo de textos comum -- por exemplo, o código fonte de um programa --, a sintaxe de grep é trivial:

grep 'padrão'nome_do_arquivo

Para buscas em arquivos binários, outras opções são necessárias. Segue uma lista delas:

  • -a ou --text faz com que grep ignore marcações especiais no arquivo binário;

  • -b ou --byte-offset faz com que grep mostre o deslocamento (ou offset) em bytes da cadeia de caracteres encontrada.

    Neste caso, offset significa que o primeiro byte é referidocomo0, o segundo como 1 etc.

  • -o ou --only-matching faz com que grep mostre apenas a cadeia de caracteres que atende os critérios de busca.

Para localizar a cadeia de caracteres Filter no arquivo ua000225.pdf aqui usado como exemplo, pode-se fazer

grep -a -b -o 'Filter' ua000225.pdf
Enter fullscreen mode Exit fullscreen mode

Como sabemos que há muitas ocorrências da palavra Filter neste pequeno arquivo, vamos restringir sua busca por apenas 1 ocorrência, usando novamente o utilitário head:

grep -a -b -o 'Filter' ua000225.pdf | head -1
Enter fullscreen mode Exit fullscreen mode

Isto gerará a saída:

50:Filter
Enter fullscreen mode Exit fullscreen mode

Isto significa que a palavra Filter começa a partir da posição 51 do
arquivo ua000225.pdf O que, segundo alguns, é uma boa ideia.

Expressões regulares

Para aumentar ainda mais o poder do grep há o recurso das
expressões regulares

Esse recurso é muito poderoso e pode até mesmo se tornar complexo. Aqui, usaremos apenas o mínimo delas.

Para usar expressões regulares com o grep pode-se usar a opção -E ou --extended-regexp, que permite certas simplificações.

Por exemplo, para encontrar todas palavras iniciadas por uma letra maiúscula e seguidas por pelo menos uma letra minúscula, usa-se como expressão de busca o texto [A-Z][a-z]\+

As expressões [A-Z] e [a-z] indicam classes de caracteres. No caso, respectivamente todas as letras maiúsculas e todas as letras minúsculas. O sinal + indica que o caractere anterior deve aparecer pelo menos uma vez, sem limite de repetições.

O caracter + é prefixado por uma contrabarra, ou \ para distinguir essa repetição da busca do caracter +.

Então, neste caso, o comando se torna:

grep -a -b -o '[A-Z][a-z]\+' ua000225.pdf
Enter fullscreen mode Exit fullscreen mode

Então, surgem 1088 linhas. Para restringir a busca a apenas 10 linhas, usamos novamente head:

grep -a -b -o '[A-Z][a-z]\+' ua000225.pdf | head
Enter fullscreen mode Exit fullscreen mode

O que gera:

35:Length
50:Filter
58:Flate
63:Decode
109:Ti
160:Ae
279:Tc
283:Kpj
292:Hu
340:Rɢ
Enter fullscreen mode Exit fullscreen mode

As cadeias de caracteres muito curtas (como Ti ou Ae) possivelmente são combinações aleatórias de valores binários. É possível exigir comprimentos mínimos maiores de resultados de busca. Uma das formas é usar as chaves.

Uma curiosidade é que quando se usa + em vez de +, o resultado é
diferente. Ou seja: os comandos

grep -a -b -o '[A-Z][a-z]+' ua000225.pdf
Enter fullscreen mode Exit fullscreen mode

geram

12090:Yk+
24288:Ld+
Enter fullscreen mode Exit fullscreen mode

A breve visualização do arquivo mostrou que as palavras maiores são
precedidas por uma barra /. Assim, pode-se alterar a expressão de busca para

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf
Enter fullscreen mode Exit fullscreen mode

Isto gerará 428 ocorrências no arquivo em uso. Para restringi-las, usa-se
novamente o head:

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | head 
Enter fullscreen mode Exit fullscreen mode

E finalmente,

34:/Length
49:/Filter
57:/Flate
339:/Rɢ
992:/Type
1008:/Subtype
1017:/Image
1025:/Name
1037:/Filter
1058:/Width
Enter fullscreen mode Exit fullscreen mode

Separação de campos

Para usar os resultados das buscas, pode-se aplicar o utilitário padrão awk, que permite separar o offset da cadeia de caracteres.

Por exemplo, para extrair apenas a cadeia de caracteres:

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }'
Enter fullscreen mode Exit fullscreen mode

Para apresentar apenas as primeiras linhas, usa-se de novo head:

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }' | head
Enter fullscreen mode Exit fullscreen mode

O que gera:

/Length
/Filter
/Flate
/Rɢ
/Type
/Subtype
/Image
/Name
/Filter
/Width
Enter fullscreen mode Exit fullscreen mode

Ou para extrair apenas o offset:

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $1 }' | head
Enter fullscreen mode Exit fullscreen mode

Com o resultado:

34
49
57
992
1008
1017
1025
1037
1058
1070
Enter fullscreen mode Exit fullscreen mode

Pelos exemplos, não é tão difícil ver que o comando awk está permitindo separar os elementos da saída de grep. A opção -F ':'' define o caracter de separação entre os dois campos, que é :'.

A forma $1 define o primeiro campo (o offset) e a forma $2 define o
segundo campo, que é a cadeia de caracteres encontrada.

Contagem de ocorrências

Para contar quantas vezes uma determinada string aparece, podem-se suar dois comandos, sort e uniq.

O comando sort (literalmente "classificação") ordena a saída de grep. Assim, a linha

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }' | sort | head
Enter fullscreen mode Exit fullscreen mode

vai mostrar

/Amer
/Amer
/Arial
/Arial
/Arial
/Arial
/Ascent
/Ascent
/Ascent
/Ascent
Enter fullscreen mode Exit fullscreen mode

O que permite contar que /Amer occore 2 vezes e /Arial ocorre 4 vezes.

Para contar todas as ocorrências, usa-se uniq -c. Assim, a linha

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }' | sort | uniq -c | head
Enter fullscreen mode Exit fullscreen mode

mostra

2 /Amer
4 /Arial
6 /Ascent
6 /Avg
6 /Base
2 /Batang
1 /Bi
1 /Bits
2 /Cal
6 /Cap
Enter fullscreen mode Exit fullscreen mode

Caso não se queira a contagem, deixa-se de lado o -c do uniq.

Assim, a linha

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }' | sort | uniq | head
Enter fullscreen mode Exit fullscreen mode

agora mostra

/Amer
/Arial
/Ascent
/Avg
/Base
/Batang
/Bi
/Bits
/Cal
/Cap
Enter fullscreen mode Exit fullscreen mode

Por último, para se saber quais são as cadeias de caracteres mais comuns,faz-se

grep -a -b -o '/[A-Z][a-z]\+' ua000225.pdf | awk -F ':' '{ print $2 }' | sort | uniq -c | sort -r -n | head
Enter fullscreen mode Exit fullscreen mode

O que gera

49 /Font
38 /Type
23 /Parent
21 /Length
21 /Filter
20 /Flate
19 /Resources
19 /Proc
19 /Page
19 /Contents
Enter fullscreen mode Exit fullscreen mode

A opção -r de sort faz com que a ordenação seja decrescente: do mais frequente para o menos frequente. A opção -n faz com que o primeiro campo da saída seja interpretado como um número.

Top comments (0)