Altera apenas um bit em um arquivo

7

Eu tenho que testar uma função de hash e quero alterar apenas um único bit de um arquivo específico.

Eu tentei com o comando dd. Isso funciona, mas só posso alterar um byte inteiro e não apenas um pouco.

sudo dd if=/dev/zero of=/file.bin bs=1 seek=10 count=1 conv=notrunc

Eu também tentei o comando sed com um regex, mas como não sei o conteúdo do arquivo, não posso simplesmente alterar um "a" para um "b".

Alguém sabe um comando para fazer isso?

    
por Kantium 14.04.2015 / 22:17

6 respostas

3

Como o arquivo pode conter valores nulos, os filtros orientados a texto, como sed , falharão. Mas você pode usar uma linguagem de programação que possa manipular nulos, como perl ou python . Aqui está uma solução para o Python 3. São algumas linhas mais longas do que o estritamente necessário, para facilitar a leitura.

#!/usr/bin/python
"""Toggle the bit at the specified offset.
Syntax: <cmdname> filename bit-offset"""

import sys
fname = sys.argv[1]
# Convert bit offset to bytes + leftover bits
bitpos = int(sys.argv[2])
nbytes, nbits = divmod(bitpos, 8)

# Open in read+write, binary mode; read 1 byte 
fp = open(fname, "r+b")
fp.seek(nbytes, 0)
c = fp.read(1)

# Toggle bit at byte position 'nbits'
toggled = bytes( [ ord(c)^(1<<nbits) ] ) 
# print(toggled) # diagnostic output

# Back up one byte, write out the modified byte
fp.seek(-1, 1)  # or absolute: fp.seek(nbytes, 0)
fp.write(toggled)
fp.close()

Salve-o em um arquivo (por exemplo, bitflip ), torne-o executável e execute-o com o nome do arquivo a ser modificado e o deslocamento em bits . Observe que isso modifica o arquivo no lugar. Execute-o duas vezes com o mesmo offset e você terá seu arquivo restaurado.

    
por 14.04.2015 / 23:49
3

Eu não acho que haja um único comando. Aqui está um script simples, salve-o como " flipbit ":

#!/usr/bin/perl
# Arguments:   byte (starting from 0),  bit (0-7),  filename (otherwise stdin)
$byte = shift(@ARGV);
$bit = shift(@ARGV);
undef $/; 
$file=<>; 
substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); 
print $file;

teste:

$ echo abb | ~/bin/flip-bit.pl 2 0 | od -xa
0000000      6261    0a63                                                
       a   b   c  nl                                                

isso inverteu o bit de baixa ordem (0) do terceiro caractere, alterando o 'b' para 'c'.

Como um comando de linha única:

perl -e '$byte=shift(@ARGV);$bit=shift(@ARGV);undef $/; $file=<>; substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); print $file'
    
por 14.04.2015 / 23:40
2

Finalmente, encontrei uma solução com xxd e dd .

a=$(xxd -b -l 1 -seek 3 -p a.bin);b=1;echo -e "\x$((${a}^${b}))" | dd of=a.bin bs=1 seek=3 count=1 conv=notrunc

hexdump a.bin     v
0000000 61 39 73 36 36 64 66 38 61 39 73 64 35 36 66 35
0000010 37 61 73 64 37 66 74 75 61 67 73 0a 61 73 64 66

hexdump b.bin     v
0000000 61 39 73 37 36 64 66 38 61 39 73 64 35 36 66 35
0000010 37 61 73 64 37 66 74 75 61 67 73 0a 61 73 64 66

Mas isso é feio.

    
por 15.04.2015 / 00:12
1

Se você realmente quiser usar dd , aqui está uma abominação que fará o truque ao inverter o bit mais alto no byte dado. Ajuste as configurações para o comando tr para alterar o bit selecionado.

# Preparation
finger > original.txt
BYTE=3

# Here we go...
dd if=original.txt bs=1c 2>/dev/null | ( dd bs=1c count=$((BYTE-1)) ; dd bs=1c count=1 | tr '
# Preparation
finger > original.txt
BYTE=3

# Here we go...
dd if=original.txt bs=1c 2>/dev/null | ( dd bs=1c count=$((BYTE-1)) ; dd bs=1c count=1 | tr '%pre%0-7' '0-7%pre%0-7' ; dd bs=1c ) 2>/dev/null > flipped.txt

# Demonstrate the difference (byte 3: 67 → e7)
hexdump -C original.txt | head -1
00000000  4c 6f 67 69 6e 20 20 20  20 20 4e 61 6d 65 20 20  |Login     Name  |    
hexdump -C flipped.txt | head -1
00000000  4c 6f e7 69 6e 20 20 20  20 20 4e 61 6d 65 20 20  |Lo.in     Name  |
0-7' '0-7%pre%0-7' ; dd bs=1c ) 2>/dev/null > flipped.txt # Demonstrate the difference (byte 3: 67 → e7) hexdump -C original.txt | head -1 00000000 4c 6f 67 69 6e 20 20 20 20 20 4e 61 6d 65 20 20 |Login Name | hexdump -C flipped.txt | head -1 00000000 4c 6f e7 69 6e 20 20 20 20 20 4e 61 6d 65 20 20 |Lo.in Name |
    
por 14.04.2015 / 23:42
0

Solução simples usando head , tail , xxd . O exemplo abaixo mostra o bit menos significativo no último byte de file.bin .

head -c -1 file.bin > flipped.bin
LAST='tail -c 1 file.bin | xxd -ps'
printf "%02X" $(( $((16#$LAST)) ^ 1 )) | xxd -r -ps >> flipped.bin
    
por 12.01.2017 / 13:37
0

Aqui estão dois perl one-liners. O primeiro altera o arquivo bar in-place:

perl -p -0777 -i -e 'substr($_,2,1)^=chr(1<<5)' bar

O segundo lê o arquivo foo e escreve bar

perl -p -0777 -e 'substr($_,2,1)^=chr(1<<5)' < foo > bar

Para se adaptar à sua aplicação: o 2 seleciona qual byte: 0..file_length-1, ou seja, 2 é o terceiro byte. Os 5 selects que bit para virar: 0-7, ou seja, 5 é o sexto bit. Isso só funcionará para arquivos que caibam na memória.

Explicação

-p iterar pelo arquivo, imprimir após cada iteração
-0777 ler todo o arquivo na memória em cada iteração (portanto, haverá apenas uma iteração)
-e executar o seguinte código perl dentro do loop
substr seleciona um único caractere no arquivo iniciando no índice 2
^=chr XOR aquele caractere com 1 deslocado 5 vezes ie 2 ^ 5

Esta resposta é uma versão simplificada do de toddkaufmann

    
por 26.09.2017 / 16:55

Tags