O Comando od (Octal Dump)

Introdução

Imagine que você queira observar o conteúdo de arquivos binários e sua primeira iniciativa é abri-los em um editor de textos. Porém você só enxerga caracteres especiais e de controle, e as informações textuais do arquivo binário estão embaralhadas em meio a esses caracteres. Os comandos que fazem dump em arquivos binários, como o od, são utilizados para visualizar essas informações textuais facilmente. Comandos como o xxd [1] também são muito usados para essa tarefa.

Hoje veremos como utilizar o comando od e alguns de seus exemplos práticos.

O comando od

Esse comando é um dos mais antigos do Unix, e está disponível em qualquer versão desse sistema operacional, inclusive no Linux. O que ele faz, basicamente, é ler os bytes de um arquivo de entrada e imprimi-los em uma série de formatos, como octal, hexadecimal e decimal.

O melhor modo de aprender sobre esse comando é testando-o. Para isso, execute-o no terminal:

$ od /etc/passwd | head
0000000 067562 072157 074072 030072 030072 071072 067557 035164
0000020 071057 067557 035164 061057 067151 061057 071541 005150
0000040 064542 035156 035170 035061 035061 064542 035156 061057
0000060 067151 027472 061163 067151 067057 066157 063557 067151
0000100 062012 062541 067555 035156 035170 035062 035062 060544
0000120 066545 067157 027472 061163 067151 027472 061163 067151
0000140 067057 066157 063557 067151 060412 066544 074072 031472
0000160 032072 060472 066544 027472 060566 027562 062141 035155
0000200 071457 064542 027556 067556 067554 064547 005156 070154
0000220 074072 032072 033472 066072 035160 073057 071141 071457
\_   _/ \_________________________ ___________________________/
   v                              v
 offset                        dados em octal

A primeira coluna da saída representa o offset do primeiro byte da coluna da frente. Por padrão, esse offset vem na base octal. As demais colunas representam os dados do arquivo.

Executar o od sem opções é o mesmo que executar:

$ od -A o -t oS -w16

Isso é: offset em octal (-A o), os dados em octal com dois bytes por coluna (-t oS) e 16 bytes por linha (-w16). Mais adiante veremos o que cada uma dessas opções faz.

Alterando o offset

A primeira coluna da saída do od é o offset. Esse offset é a posição do primeiro byte da coluna da frente. Podemos alterar a base desse offset através da opção -A. Os parâmetros dessa opção podem ser: d (decimal), x (hexadecimal), o (octal – padrão), n (ocultar offset).

$ od -Ax /etc/passwd | tail
0008d0 064547 005156 064544 072163 060543 064143 035145 035170
0008e0 032071 034472 035064 064504 072163 060543 064143 035145
0008f0 035057 071457 064542 027556 067556 067554 064547 005156
000900 061564 062160 066565 035160 035170 031067 033472 035062
000910 027472 027472 061163 067151 067057 066157 063557 067151
000920 062012 062541 067555 064556 035157 035170 030061 030060
000930 030472 030060 035060 060515 061562 071557 050040 072541
000940 067554 027472 067550 062555 062057 062541 067555 064556
000950 035157 061057 067151 061057 071541 005150
\_  _/
  v
offset em hexa

Quando só os dados são interessantes, a coluna offset pode ser ocultada com a opção -An:

$ od -An /etc/passwd | head
 067562 072157 074072 030072 030072 071072 067557 035164
 071057 067557 035164 061057 067151 061057 071541 005150
 064542 035156 035170 035061 035061 064542 035156 061057
 067151 027472 061163 067151 067057 066157 063557 067151
 062012 062541 067555 035156 035170 035062 035062 060544
 066545 067157 027472 061163 067151 027472 061163 067151
 067057 066157 063557 067151 060412 066544 074072 031472
 032072 060472 066544 027472 060566 027562 062141 035155
 071457 064542 027556 067556 067554 064547 005156 070154
 074072 032072 033472 066072 035160 073057 071141 071457

Bytes por linha

A opção -w seta quantos bytes serão mostrados por linha. Por padrão esse valor é 16, mas pode ser facilmente alterado:

$ od -w32 /etc/passwd | head
0000000 067562 072157 074072 030072 030072 071072 067557 035164 ...
0000040 064542 035156 035170 035061 035061 064542 035156 061057 ...
0000100 062012 062541 067555 035156 035170 035062 035062 060544 ...
0000140 067057 066157 063557 067151 060412 066544 074072 031472 ...
0000200 071457 064542 027556 067556 067554 064547 005156 070154 ...
0000240 067560 066157 066057 062160 027472 061163 067151 067057 ...
0000300 035060 074563 061556 027472 061163 067151 027472 064542 ...
0000340 035170 035066 035060 064163 072165 067544 067167 027472 ...
0000400 073557 005156 060550 072154 074072 033472 030072 064072 ...
0000440 060550 072154 066412 064541 035154 035170 035070 031061 ...
OBS: saída cortada

Como cada coluna tem dois bytes e para se obter os 32 bytes por linha, será preciso 16 colunas. Para diminuir a quantidade de colunas mantendo a quantidade de bytes por linha, devemos alterar a representação dos dados.

Mudando a representação dos dados

Como vimos, os bytes de entrada são transformados para a base octal. Isso é devido a opção -t oS que é utilizada pelo od por padrão. Podemos mudar a representação dos dados de duas formas: uma utilizando opções flags e a outra utilizando a opção -t.

Mudando a representação dos dados com as opções flags

As opções flags, além de setar uma representação de saída, inclui também a quantidade de bits que serão lidos da entrada. Existem várias dessas opções como visto em [2], porém as mais úteis são:

Opções OD

Opções OD

É bom lembrar que quando mais de um byte é lido, sempre que permitido, a saída sairá em little endian (bytes invertidos).

Alguns exemplos de uso das opções flags:

Teste OD /etc/passwd

Teste OD /etc/passwd

O comando leu cada byte do arquivo e o imprimiu como caractere ASCII. Alguns caracteres de controles são substituídos por suas representações em barra (ex: 0x10 = \n).

$ od -D /etc/passwd | head
0000000 1953460082  809138234 1916416058  980709231
0000020 1869574703 1647262324 1647275625  174617441
0000040  980314466  976304760 1768045105 1647262318
0000060  792358505 1852400243 1819242031 1852401519
0000100 1700881418  980316013  976370296 1633958450
0000120 1852796261 1651715898  792358505 1852400243
0000140 1819242031 1852401519 1835294986  859469882
0000160 1631204410  792358244  796025206  980247649
0000200 1768059695 1869492078 1768386412 1886128750
0000220  876247098 1815754554 1982806640 1932489313

A opção -D faz o od ler 4 bytes do arquivo de entrada e transformá-los em um número decimal sem sinal. Para entender o que foi feito, considere os primeiros bytes de /etc/passwd: ‘r’, ‘o’, ‘o’ e ‘t’. Em hexa, esses bytes são escritos como ‘r’=0x72, ‘o’=0x6F, ‘o’=0x6F e ‘t’=0x74, ou melhor: 0x746F6F72 (little endian). Esse número nada mais é que 1953460082 em decimal sem sinal, que é o mesmo valor observado na segunda coluna da primeira linha.

Valores em hexa são muito comuns. A opção -x lê 2 bytes da entrada, e a opção -X lê 4 bytes:

$ od -X /etc/passwd | head
0000000 746f6f72 303a783a 723a303a 3a746f6f
0000020 6f6f722f 622f3a74 622f6e69 0a687361
0000040 3a6e6962 3a313a78 69623a31 622f3a6e
0000060 2f3a6e69 6e696273 6c6f6e2f 6e69676f
0000100 6561640a 3a6e6f6d 3a323a78 61643a32
0000120 6e6f6d65 62732f3a 2f3a6e69 6e696273
0000140 6c6f6e2f 6e69676f 6d64610a 333a783a
0000160 613a343a 2f3a6d64 2f726176 3a6d6461
0000200 6962732f 6f6e2f6e 69676f6c 706c0a6e
0000220 343a783a 6c3a373a 762f3a70 732f7261

A flag -f é interessante. Com ela podemos, por exemplo, obter a representação IEEE 754 [3] de ponto flutuante dos bytes de entrada.

De acordo com [4], o número binário 10001001001101010101011100101011 (ou 0x8935572B em hexa) representa o número -2.1828069E-33. Vamos conferir:

$ printf '\x2B\x57\x35\x89' | od -f
0000000  -2.1828069e-33

Se você já precisou converter valores para o padrão IEEE 754 sabe o trabalho que dá fazer isto sem um conversor em mãos, mas agora com o comando od podemos realizar essa tarefa facilmente.

Para mais opções flags do od, consulte a man page do programa ou a referência [2].

Mudando a representação dos dados com a opção -t

Essa opção recebe como argumento um tipo e também um modificador de tamanho como sufixo. A tabela abaixo resume alguns desses tipos:

+------+-----------------------------------------+
| Tipo |               Descrição                 |
+------+-----------------------------------------+
|  a   |           Byte como caractere.          |
+------+-----------------------------------------+
|  c   |           Byte como caractere.          |
+------+-----------------------------------------+
|  d   |      Bytes como decimal com sinal.      |
+------+-----------------------------------------+
|  u   |      Bytes como decimal sem sinal       |
+------+-----------------------------------------+
|  f   | Bytes como número em ponto flutuante.   |
+------+-----------------------------------------+
|  o   |             Bytes em octal.             |
+------+-----------------------------------------+
|  x   |          Bytes em hexadecimal.          |
+------+-----------------------------------------+

Os tipos podem vir com um sufixo de tamanho:

+-------------+----------------------------------------+
|    Tipos    |                 Sufixo                 |
+-------------+----------------------------------------+
| d, u, o, x  | C (char), I (int), L (long), S (short) |
+-------------+----------------------------------------+
|      f      | F (float), D (double), L (long double) |
+-------------+----------------------------------------+

O tamanho de cada sufixo é o mesmo que aplicar sizeof no tipo correspondente em C. Isso é, o sufixo S indica uma leitura de sizeof(short) = 2 bytes.

O uso dessas opções é o mesmo que das opções flag:

$ od -txL /etc/passwd | head
0000000 746f6f72 303a783a 723a303a 3a746f6f
0000020 6f6f722f 622f3a74 622f6e69 0a687361
0000040 3a6e6962 3a313a78 69623a31 622f3a6e
0000060 2f3a6e69 6e696273 6c6f6e2f 6e69676f
0000100 6561640a 3a6e6f6d 3a323a78 61643a32
0000120 6e6f6d65 62732f3a 2f3a6e69 6e696273
0000140 6c6f6e2f 6e69676f 6d64610a 333a783a
0000160 613a343a 2f3a6d64 2f726176 3a6d6461
0000200 6962732f 6f6e2f6e 69676f6c 706c0a6e
0000220 343a783a 6c3a373a 762f3a70 732f7261

A saída acima é a mesma quando executamos od -X. O ‘x’ indica hexa e o ‘L’ a quantidade de bytes a ler, que nesse caso é sizeof(long) = 4 bytes.

O nosso exemplo do padrão IEEE 754 fica assim:

$ printf '\x2B\x57\x35\x89' | od -tfF
0000000  -2.1828069e-33

O ‘f’ representa float e o ‘F’ quantos bytes a ler, que nesse caso é sizeof(float) = 4 bytes. Para se ter um resultado em double, troque ‘F’ por ‘D’.

Conclusão

O od tem uma maneira bem diferente de dar dump em arquivos. A diferença dele para outros aplicativos do gênero é que ele fornece um maior controle em relação a quantidade de dados lidos. Esse comando se torna muito útil para análise de binários, como por exemplo, leitura de campos específicos e de informações textuais. Um exemplo interessante abordado foi em relação a transformação dos bytes de entrada em um número em ponto flutuante no padrão IEEE 754.

Referências

[1] Hex dump com o xxd by Daemonio (Acessado em: Agosto/2012)
https://daemoniolabs.wordpress.com/2011/07/07/hex-dump-com-o-xxd/

[2] od Command by IBM (Acessado em: Agosto/2012)
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.cmds/doc/aixcmds4/od.htm

[3] Ponto flutuante by wikipedia (Acessado em: Agosto/2012)
http://pt.wikipedia.org/wiki/Ponto_flutuante

[4] IEEE 754 Converter by h-schmidt (Acessado em: Agosto/2012)
http://www.h-schmidt.net/FloatConverter/IEEE754.html

Deixe um comentário