
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 hexdump
manual .
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 hexdump
programa (em termos de agrupamento de bytes ) ao usar sua -e
opção?
Responder1
Na falha de um programa de despejo com opções de despejo adequadas, você sempre pode remendar algo usando hexdump
e xdd
e juntando a saída com colar. Não é bonito, mas usar um shell que suporte a substituição de processos ( bash
vai 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 sed
para persuadir dc
a traduzir od
a 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 -N12
opção - eu apenas usei isso para não engasgar com um canal interminável de dados pseudo-aleatórios. E -w4
especifica 4 bytes por linha, mas você deve poder usar qualquer número de bytes. Além disso, o 1i2o
sed
comando é uma dc
instrução referente à sua base de saída - 2
para 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 sed
instrução e alterar a opção od
de para .-t d1z
t 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 bc
pode 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
dc
nã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 bc
para 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 od
para não imprimir deslocamentos - o que agora estou duvidando - que quero saídas de -t
ype o
, d
ou x
um byte por vez e que quero a representação ASCII z
de cada byte anexada ao final da linha, -v
erroneamente(então não apenas me imprime um 0*
for 0000
)em -w4
bytes por linha.
Sem -A n
ele 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
.