Usando dados binários como um parâmetro no bash - qualquer maneira de permitir nuls?

6

Portanto, gostaria de passar os primeiros 512 bytes de binaryFile.dd como o segundo parâmetro para myProgram, mas bash retira todos os caracteres NUL. Existe alguma maneira de evitar isso em bash ou estou em um esconderijo para nada?

myProgram parameter1 "$(head -c 512 binaryFile.dd)"
    
por technicalbloke 14.02.2017 / 08:51

3 respostas

4

Não há como passar um byte nulo no parâmetro de um comando. Isto não é devido a uma limitação do bash, embora o bash também tenha essa limitação. Esta é uma limitação da interface para executar um comando: trata um byte nulo como o final do parâmetro. Não há mecanismo de escape.

A maioria dos shells não suporta bytes nulos em variáveis ou nos argumentos de funções e builtins. Zsh é uma exceção notável.

$ ksh -c 'a=$(printf foo\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f
0000003
$ bash -c 'a=$(printf foo\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f 62 61 72
0000006
$ zsh -c 'a=$(printf foo\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f 00 62 61 72
0000007

Mas, mesmo com zsh, se você tentar passar um parâmetro para um comando externo, qualquer coisa após um byte nulo será ignorada - não pelo zsh, mas pelo kernel.

$ zsh -c 'a=$(printf foo\0bar); /usr/bin/printf "$a"' | od -t x1
0000000 66 6f 6f
0000003

Se você quiser passar bytes nulos para um programa, você precisa encontrar alguma outra maneira que não seja um parâmetro de linha de comando.

head -c 512 binaryFile.dd | myProgram --read-parameter2-from-stdin parameter1
myProgram --read-parameter2-from-file=<(head -c 512 binaryFile.dd) parameter1
    
por 15.02.2017 / 01:30
3

bash não é mais adequado para lidar diretamente com dados binários.

Use os dados binários com um arquivo ou use a string hexadecimal que representa seus dados.

Para converter em hexadecimal, você pode usar hexdump , xxd , od .

Por exemplo, para converter 512 bytes em uma string hexadecimal, use

xxd -ps -c 512 file.bin

Para convertê-lo de volta para uso binário

echo "$myhexstring" | xxd -r -ps > file.bin
    
por 14.02.2017 / 09:20
1

Não, não há como uma string no bash conter um NUL ( c ).
Portanto, uma variável (pois contém uma string) não pôde conter um NUL.

A razão é que o bash é escrito com o paradigma $1 de "uma string termina em um NUL". [1] O kernel do Linux também impõe essa limitação. [2] Mas mesmo que o kernel permitisse NULs em strings [3] (argumentos), a maioria dos shells e bash em particular, não poderiam incluir NULs dentro de variáveis [4] .

Parâmetros posicionais ( $2 , | , etc.) são equivalentes a variáveis e também não podem conter NULs.

No entanto, nuls pode existir em arquivos, em streams e em printf:

$ printf 'test
$ echo $(printf 'test
$ zsh -c ' echo $(printf "test
$ printf 'test
$ myProgram "$parameter1" "$(xxd -ps -c 512 binaryFile.dd)"
nuls
$ zsh -c 'a=$(printf "included
$ zsh -c 'a=$(printf "included
$ bash -c 'a=$(printf "included
$ printf 'included
$ zsh -c 'printf "included
$ printf 'test
$ echo $(printf 'test
$ zsh -c ' echo $(printf "test
$ printf 'test
$ myProgram "$parameter1" "$(xxd -ps -c 512 binaryFile.dd)"
nuls
$ zsh -c 'a=$(printf "included
$ zsh -c 'a=$(printf "included
$ bash -c 'a=$(printf "included
$ printf 'included
$ zsh -c 'printf "included%pre%null" | ( read a; printf "%s\n" "$a" | od -vAn -tx1c )'
  69  6e  63  6c  75  64  65  64  00  6e  75  6c  6c  0a
   i   n   c   l   u   d   e   d  %pre%   n   u   l   l  \n
null' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 00 6e 75 6c 6c i n c l u d e d %pre% n u l l $ printf 'included%pre%null' | ( read a; printf '%s\n' "$a" | od -vAn -tx1c ) 69 6e 63 6c 75 64 65 64 6e 75 6c 6c i n c l u d e d n u l l
null"); /usr/bin/printf "$a"' | od -vAn -tx1c bash: warning: command substitution: ignored null byte in input 69 6e 63 6c 75 64 65 64 6e 75 6c 6c i n c l u d e d n u l l
null"); /usr/bin/printf "$a"' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 i n c l u d e d
null"); printf "$a"' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 00 6e 75 6c 6c i n c l u d e d %pre% n u l l
in%pre%files%pre%\n' > testnul.bin $ cat testnul.bin | xxd -ps 74657374006e756c7300696e0066696c6573000a $ head -c 7 testnul.bin | xxd -ps 74657374006e75 $ dd if=testnul.bin bs=7 count=1 | xxd -ps 74657374006e75 1+0 records in 1+0 records out 7 bytes copied, 0.000655689 s, 10.7 kB/s $ dd if=testnul.bin bs=7 count=1 2>/dev/null| xxd -ps 74657374006e75
nuls\n") | od -vAn -tx1c' 74 65 73 74 20 6e 75 6c 73 0a t e s t n u l s \n
nuls\n') | od -vAn -tx1c bash: warning: command substitution: ignored null byte in input 74 65 73 74 6e 75 6c 73 0a t e s t n u l s \n
nuls\n' | od -vAn -tx1c 74 65 73 74 00 6e 75 6c 73 0a t e s t %pre% n u l s \n
null" | ( read a; printf "%s\n" "$a" | od -vAn -tx1c )' 69 6e 63 6c 75 64 65 64 00 6e 75 6c 6c 0a i n c l u d e d %pre% n u l l \n
null' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 00 6e 75 6c 6c i n c l u d e d %pre% n u l l $ printf 'included%pre%null' | ( read a; printf '%s\n' "$a" | od -vAn -tx1c ) 69 6e 63 6c 75 64 65 64 6e 75 6c 6c i n c l u d e d n u l l
null"); /usr/bin/printf "$a"' | od -vAn -tx1c bash: warning: command substitution: ignored null byte in input 69 6e 63 6c 75 64 65 64 6e 75 6c 6c i n c l u d e d n u l l
null"); /usr/bin/printf "$a"' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 i n c l u d e d
null"); printf "$a"' | od -vAn -tx1c 69 6e 63 6c 75 64 65 64 00 6e 75 6c 6c i n c l u d e d %pre% n u l l
in%pre%files%pre%\n' > testnul.bin $ cat testnul.bin | xxd -ps 74657374006e756c7300696e0066696c6573000a $ head -c 7 testnul.bin | xxd -ps 74657374006e75 $ dd if=testnul.bin bs=7 count=1 | xxd -ps 74657374006e75 1+0 records in 1+0 records out 7 bytes copied, 0.000655689 s, 10.7 kB/s $ dd if=testnul.bin bs=7 count=1 2>/dev/null| xxd -ps 74657374006e75
nuls\n") | od -vAn -tx1c' 74 65 73 74 20 6e 75 6c 73 0a t e s t n u l s \n
nuls\n') | od -vAn -tx1c bash: warning: command substitution: ignored null byte in input 74 65 73 74 6e 75 6c 73 0a t e s t n u l s \n
nuls\n' | od -vAn -tx1c 74 65 73 74 00 6e 75 6c 73 0a t e s t %pre% n u l s \n

Como você pode ver, o printf cria um NUL e flui pelo pipe ( cat ). Mas as NULs são separadas das "execuções de comandos":

%pre%

No bash 4.4, ele emite um aviso. zsh neste caso, substitua silenciosamente os NULs por um espaço:

%pre%

Podemos criar um arquivo que contenha NULs com printf e head , tail , C string ou dd parte do arquivo que inclui os NULs:

%pre%

No seu caso, não existe uma maneira simples de [5] ter o conteúdo de um arquivo binário como argumento. Talvez a representação hex possa funcionar:

%pre%

Obrigado a @Gilles por todo o trabalho adicional (e detalhes) abaixo.

1

[1] Tudo se resume à antiga definição de C que "strings terminam em um NUL ( strcpy )». Este paradigma foi codificado em várias bibliotecas e ferramentas execve() , das quais POSIX tem vários exemplos. Como execve() aqui que estados (ênfase minha):

The strcpy() function shall copy the string pointed to by s2 (including the terminating NUL character) into the array pointed to by s1.

Isso significa que é assumido que uma cadeia é terminada por um NUL.
Ou, em outras palavras, pode haver apenas um NUL, o último.

2

[2] A chamada do sistema $'%code%' , também definida no POSIX , espera que as strings (argumentos de comando) terminem em um NUL. É por isso que mesmo shells podem funcionar com NULs (a maioria não, com a notável exceção do zsh):

%pre%

Pode não usar NULs em argumentos passados pela chamada %code% :

%pre%

3

[3] Mas mesmo que o kernel seja capaz de incluir NULs em argumentos, o bash não permitirá:

%pre%

No bash 4.4, ele emite um aviso quando um NUL é removido.

4

[4] A maioria das shells, e bash em particular, não poderiam incluir NULs dentro de variáveis.

%pre%

Se o shell em execução for zsh, isso (em vez disso) funcionará com um nulo:

%pre%

5

[5] Significa que uma inclusão "direta" (simples) de um byte de valor 0 ( %code% ) é impossível. Mas um codificado (complexo), seja usando C-string %code% , em hexadecimal, base 64 ou algum equivalente, o valor de zero poderia ser incluído.

    
por 14.02.2017 / 16:56