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:
É 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:
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