web-dev-qa-db-ja.com

ファイルの1ビットのみを変更する

ハッシュ関数をテストする必要があり、特定のファイルの1ビットのみを変更したい。

Ddコマンドで試してみました。これでうまくいきますが、変更できるのはバイトではなく、バイト全体です。

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

また、正規表現を使用してsedコマンドを試しましたが、ファイルの内容がわからないため、「a」を「b」に変更することはできません。

誰かがこれを行うためのコマンドを知っていますか?

9
Kantium

ファイルにはnullが含まれている可能性があるため、sedのようなテキスト指向のフィルターは失敗します。ただし、Perlpythonなどのnullを処理できるプログラミング言語を使用できます。 Python 3.の解決策です。読みやすくするために、厳密に必要な長さよりも数行長くなっています。

#!/usr/bin/env python3
"""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()

ファイル(bitflipなど)に保存し、実行可能にして、変更するファイル名とオフセットビット単位で実行します。ファイルがその場で変更されることに注意してください。同じオフセットで2回実行すると、ファイルが復元されます。

3
alexis

単一のコマンドがあるとは思わない。以下は簡単なスクリプトです。「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;

テスト:

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

これにより、3番目の文字の下位ビット(0)が反転し、「b」が「c」に変更されました。

単一行コマンドとして:

Perl -e '$byte=shift(@ARGV);$bit=shift(@ARGV);undef $/; $file=<>; substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); print $file'
3
toddkaufmann

最後に、xxdddの解決策を見つけました。

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

しかし、これは醜いです。

2
Kantium

本当にddを使用したい場合は、次のabominationを使用して、指定されたバイトの最上位ビットをフリップすることでトリックを実行します。 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 '\000-\377' '\200-\377\000-\177' ; 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  |
1
roaima

ここに2つのPerlワンライナーがあります。最初はファイルbarをその場で変更します:

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

2番目は、ファイルfooを読み取り、barを書き込みます

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

アプリケーションに適応するには:2はどのバイトを選択します:0..file_length-1、つまり2は3番目のバイトです。 5はフリップするビットを選択します。0〜7、つまり5は6番目のビットです。これは、メモリに収まるファイルに対してのみ機能します。

説明

-pファイルを反復処理し、各反復処理の後に出力します
-0777各反復でファイル全体をメモリに読み込みます(したがって、反復は1つだけになります)
-eループ内で次のPerlコードを実行します
substrインデックス2から始まるファイル内の1文字を選択します
^=chr XOR 1が5シフトした文字、つまり2 ^ 5

この答えは toddkaufmann's の簡略版です。

0
Eponymous

headtailxxdを使用した簡単なソリューション。以下の例では、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
0
juppman