Conseguindo formato semelhante a hexdump, que inclui strings binárias, na linha de comando?

6

Eu realmente gosto de hexdump , especialmente porque você pode definir um formato personalizado; diga:

$ echo -e '\x00\x01\x02\x03' | hexdump -v -e '1/1 "%_ad: "' -e '4/1 "%02X "' -e '1/1 " : "' -e '4/1 "%_p"' -e '1/1 "\n"'
0: 00 01 02 03 : ....
4: 0A          : .

Então, eu posso escolher ter, digamos, 4 bytes por linha, escritos como primeiro hexadecimal, depois como caracteres. Mas, o que estou perdendo aqui, é um caractere de formatação "string binária" (ou "string de bits"); por exemplo. Gostaria de escrever algo como -e '4/1 "%08b "' em algum lugar nessa linha de comando e obter, por exemplo:

0: 00 01 02 03 : 00000000 00000001 00000010 00000011 : ....
4: 0A          : 00001010 : .

Claro, então provavelmente alguém teria que especificar endianness (se grupos de mais de um byte deveriam ser formatados) etc ... Mas em qualquer caso, este tipo de formatação não existe, tanto quanto eu posso ver no manual hexdump .

Portanto, minha pergunta é: quais alternativas eu tenho em uma linha de comando do Linux, para obter um dump formatado que inclua strings binárias como acima e, no máximo, preservar a possibilidade de personalização do programa hexdump (em termos de agrupamento de bytes) ao usar sua opção -e ?

    
por sdaau 12.06.2014 / 20:32

3 respostas

3

Ao falhar um programa de dump com opções de dump adequadas, você sempre pode compilar algo juntos usando hexdump e xdd e, em seguida, unindo a saída com a pasta. Não é bonito, mas usando um shell que suporta a substituição de processos ( bash fará):

mkfifo fifo
echo -e '\x00\x01\x02\x03' |
  tee fifo |
  paste -d' ' \
    <(hexdump -v -e '1/1 "%_ad: "' -e '4/1 "%02X "' -e '1/1 " :\n"') \
    <(xxd -b -c 4 fifo | cut -d' ' -f 2-)

Saída:

0: 00 01 02 03 : 00000000 00000001 00000010 00000011  ....
4: 0A          : 00001010                             .
    
por 12.06.2014 / 21:00
3

Esta é minha sugestão usando o Perl, utilizando seus especificadores de formatação para a função pack() / unpack() ; a chamada de teste seria como:

$ echo -e '\x00\x01\x02\x03' | perl hexdump-00.pl --offset 120 --group 4 --add '(H2)*' --add '(B8)*' 
Opening '' STDIN
Cannot seek!
0
00000000: 00 01 02 03 00000000 00000001 00000010 00000011  '....'
00000004: 0a 00001010  '.'

É meio difícil inserir marcadores de string no meio - mas o bom é que você ainda pode "agrupar" bytes dentro - por exemplo, você pode agrupar dois bytes e interpretá-los como inteiros assinados (curtos), exemplo:

$ perl -e 'print pack("s*\n", (-124))' | hexdump -C
00000000  84 ff                                             |..|
00000002

$ echo -e '\x00\x01\x84\xff' | perl hexdump.pl \
  --offset 120 --group 4 \
  --add '(H2)*' \
  --add '(B8)*' \
  --add '(s2)*'
Opening '' STDIN
Cannot seek!
0
00000000: 00 01 84 ff 00000000 00000001 10000100 11111111 256 -124  '....'
00000004: 0a 00001010  '.'

Aqui está o hexdump-00.pl :

#!/usr/bin/perl

# perl hexdump-00.pl --offset 120 --group 4 --add '(H2)*' --add '(B8)*' test.file

use strict;
use warnings;
use Getopt::Long;
use Fcntl qw(SEEK_CUR SEEK_SET);
my $offset = 0;
my $groupsize = 1;
my $length = 128;
my @list=();
my $result = GetOptions (
  "offset=i" => \$offset,
  "group=i"   => \$groupsize,
  "length=i"   => \$length,
  "add=s" => \@list,
);
my $inputfname="";
my $inputfh;
$inputfname = $ARGV[0] if defined $ARGV[0];
if (($inputfname eq "") || ($inputfname eq "-")) {
  printf(STDERR "Opening '%s' STDIN\n", $inputfname);
  $inputfh = *STDIN;
} else {
  printf(STDERR "Opening '%s'\n", $inputfname);
  open ($inputfh, "<$inputfname");
}

binmode($inputfh);
my $startaddr=0;
if( not(defined($startaddr = sysseek($inputfh, $offset-1, SEEK_SET))) ) {
  printf(STDERR "Cannot seek!\n");
  #~ $startaddr = sysseek($inputfh, 0, 0); // cannot reset like this
  $startaddr = 0; # just avoid errors
}
print(STDERR $startaddr . "\n");

my $buffer=undef;
my $nread;
my $total=0;
while (($nread=sysread($inputfh, $buffer, $groupsize)) > 0) { # , $startaddr
  #~ printf("%08X: nr: %d, buf '%s'\n",$startaddr,$nread,$buffer);
  printf("%08X: ", $startaddr);
  foreach my $tformat (@list) {
    foreach my $tentry (unpack($tformat, $buffer)) {
      printf("%s ", $tentry);
    }
  }
  (my $newbuf = $buffer) =~ s/[^[:print:]]/./g; # make non-printable into '.'
  printf(" '%s'", $newbuf);
  print("\n");
  $startaddr += $nread;
  $total += $nread;
  if ($total > $length) { last; }
}

close($inputfh);
    
por 12.06.2014 / 23:21
2

Veja alguns sed para coaxar dc para traduzir a saída de od para a base 2:

od -t d1z -w4 -v -N12 </dev/urandom |
sed -e '1i2o' -e 's/.*/[&]p/p;$d
    s/>/]n [>/;s/[^ ]*/&]n [/;h;s/>.*//;
    s/ -/ _/g;s/ [^] [][^ ]*/ ]n&n [ /g;G
    s/\n[^>]*//' | 
dc

É um pouco mais simples agora - o que não é mais rápido - mas ainda não é uma rainha da beleza. Também imprime os valores decimais e base 2 de todos os bytes.

Quando eu corro, fico:

0000000  -43  125 -117  -39  >.}..<
0000000  -101011   1111101  -1110101   -100111   >.}..<
0000004   62   28   80   61  >>.P=<
0000004   111110    11100    1010000    111101   >>.P=<
0000010    6   14  120  -16  >..x.<
0000010    110    1110   1111000   -10000   >..x.<
0000014

Ou ...

echo aBcD | od -t d1z -w4 -v | sed ... | dc


0000000   97   66   99   68  >aBcD<
0000000   1100001    1000010    1100011    1000100   >aBcD<
0000004   10                 >.<
0000004   1010                  >.<
0000005

As larguras de campo podem usar um pouco de trabalho, mas são todas suas. Você não precisa da opção -N12 - eu apenas usei isso para não engasgar com um interminável canal de dados pseudo-aleatórios. E o -w4 especifica 4 bytes por linha, mas você deve ser capaz de usar qualquer número de bytes. Além disso, o comando 1i2o sed é uma instrução dc em relação à sua base de saída - 2 para binário - mas qualquer base entre 2 e 16 deve funcionar da mesma forma. Se desejar ver, por exemplo, saída hexadecimal e base 2, você precisará adicionar '16i' à primeira instrução sed e alterar a opção od -t d1z para t x1z .

Outras opções incluem ...

printf faz isso:

printf '%o\n%x\n' 128 128

200

80

... até mesmo ...

printf '%o\n%x\n' "'a" "'a"

141

61

O binário não é tão simples, mas bc pode fazer tudo isso se você definir seu obase= de acordo com suas especificações:

printf 'obase=2;%d
        obase=8;%d
        obase=16;%d
        obase=2;%d
        ' 64 128 "'A" "'a" |
bc

OUTPUT

1000000
200
41
1100001

dc não é tão falado:

printf '%do%dn10P' 2 64 8 128 16 "'A" 2 "'a" |dc

OUTPUT

1000000
200
41
1100001

Faça man dc bc para mais informações.

E, novamente, para fluxos de arquivos, você sempre pode usar od :

for o in o d x ; do
    echo aBcD | 
    od -A n -t ${o}1z -v -w4
done

OUTPUT

 141 102 143 104  >aBcD<
 012              >.<
  97  66  99  68  >aBcD<
  10              >.<
  61  42  63  44  >aBcD<
  0a              >.<

Com ^ que ^ eu digo a od para não imprimir deslocamentos - que eu agora estou criticando - que eu quero saídas de -t ype o , d ou x um byte de cada vez e que eu quero a representação ASCII z de cada byte anexado ao final da linha, -v erbosely (para que não apenas me imprima 0* para 0000 ) em -w4 bytes por linha.

Sem -A n , imprime:

0000000 141 102 143 104  >aBcD<
0000004 012              >.<
0000005
0000000  97  66  99  68  >aBcD<
0000004  10              >.<
0000005
0000000  61  42  63  44  >aBcD<
0000004  0a              >.<
0000005

E qualquer combinação de dc bc od é obviamente possível em |pipeline .

    
por 12.06.2014 / 23:00