Alcançando formato hexdump, que inclui strings binárias, na linha de comando?

Alcançando formato hexdump, que inclui strings binárias, na linha de comando?

Eu gosto muito do hexdump, principalmente porque você pode definir um formato personalizado; dizer:

$ 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          : .

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

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

Claro, então provavelmente seria necessário especificar endianness (se grupos de mais de um byte deveriam ser formatados) etc... Mas em qualquer caso, esse tipo de formatação não existe, pelo que posso ver no hexdumpmanual .

Então, minha pergunta é: quais alternativas eu tenho em uma linha de comando do Linux, para que eu possa obter um dump formatado que inclua strings binárias como acima, e ainda assim preservar ao máximo a capacidade de personalização do hexdumpprograma (em termos de agrupamento de bytes ) ao usar sua -eopção?

Responder1

Na falha de um programa de despejo com opções de despejo adequadas, você sempre pode remendar algo usando hexdumpe xdde juntando a saída com colar. Não é bonito, mas usar um shell que suporte a substituição de processos ( bashvai servir):

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                             .

Responder2

Aqui está minha sugestão usando 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 - por exemplo, você pode agrupar dois bytes e interpretá-los como números 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á 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);

Responder3

Aqui estão alguns sedpara persuadir dca traduzir oda saída de 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 sem falar que é mais rápido - mas ainda não é uma rainha da beleza. Ele também imprime os valores decimais e de base 2 de todos os bytes.

Quando executo, recebo:

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 dos campos precisam de um pouco de trabalho, mas são todas suas. Você não precisa da -N12opção - eu apenas usei isso para não engasgar com um canal interminável de dados pseudo-aleatórios. E -w4especifica 4 bytes por linha, mas você deve poder usar qualquer número de bytes. Além disso, o 1i2o sedcomando é uma dcinstrução referente à sua base de saída - 2para binário - mas qualquer base entre 2 e 16 deve funcionar igualmente bem. Se desejar ver, por exemplo, saída hexadecimal e base 2, você precisará adicionar '16i' à primeira sedinstrução e alterar a opção odde para .-t d1zt x1z

Outras opções incluem...

printf faz isso:

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

200

80

...até...

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

141

61

O binário não é tão simples, mas bcpode fazer tudo isso se você definir 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

SAÍDA

1000000
200
41
1100001

dcnão é tão falador:

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

SAÍDA

1000000
200
41
1100001

Faça man dc bcpara obter 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

SAÍDA

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

Com^isso^um que eu digo odpara não imprimir deslocamentos - o que agora estou duvidando - que quero saídas de -type o, dou xum byte por vez e que quero a representação ASCII zde cada byte anexada ao final da linha, -verroneamente(então não apenas me imprime um 0*for 0000)em -w4bytes por linha.

Sem -A nele 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 um arquivo |pipeline.

informação relacionada