Ändern Sie nur ein Bit in einer Datei

9

Ich muss eine Hash-Funktion testen und möchte nur ein einziges Bit einer bestimmten Datei ändern.

Ich habe es mit dem Befehl dd versucht. Das funktioniert, aber ich kann nur ein ganzes Byte ändern und nicht nur ein bisschen.

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

Ich habe auch den Befehl sed mit einem regulären Ausdruck ausprobiert, aber da ich den Inhalt der Datei nicht kenne, kann ich nicht einfach ein "a" in ein "b" ändern.

Kennt jemand einen Befehl dafür?

Kantium
quelle
1
Wenn Sie ein Kleinbuchstabenzeichen (ASCII oder Einzelbyte utf8) finden und in Großbuchstaben konvertieren können oder umgekehrt. Dann ist dies eine 1-Bit-Änderung. Dies ist keine sehr gute Lösung, daher nur ein Kommentar.
Strg-Alt-Delor
Ja, es ist wahrscheinlich die beste Option, aber kann ich nur das erste Vorkommen ändern? Und ich kann bei Erfolg ein Ergebnis erzielen / nicht gefunden? Vielleicht ist es mit einer XOR-Maske in der Datei direkt möglich?
Kantium
Wie bestimmen Sie, welches Bit geändert werden soll? Ist es Ihr Ziel, bei einem bestimmten Versatz ein wenig umzuschalten ? Und ändern Sie 0 zu 1 und 1 zu 0 oder (sagen wir) setzen Sie es immer auf 1? Endlich: Welche Art von Daten enthält die Datei? Könnte es "binär" sein, dh Null-Bytes enthalten?
Alexis
Im Idealfall ist das Umschalten eines zufälligen oder festen Bits hervorragend, aber wenn ich bereits ein Bit auf Null oder Eins erzwingen kann, ist das auch gut für mich. Und ja, es ist eine Binärdatei
Kantium
1
Ich würde einfach ein Programm schreiben, um das Byte zu lesen, das die ausgewählte Bitposition enthält, ein Byte konstruieren, das sich nur in dem einen Bit unterscheidet, und es ausschreiben. Da sich die Dateilänge nicht ändert, müssen Sie nur dieses eine Byte lesen und schreiben.
Alexis

Antworten:

3

Da die Datei möglicherweise Nullen enthält, schlagen textorientierte Filter wie sedfehl. Sie können jedoch eine Programmiersprache verwenden, die Nullen wie perloder verarbeiten kann python. Hier ist eine Lösung für Python 3. Aus Gründen der Lesbarkeit sind einige Zeilen länger als unbedingt erforderlich.

#!/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()

Speichern Sie es in einer Datei (z. B. bitflip), machen Sie es ausführbar und führen Sie es mit dem zu ändernden Dateinamen und dem Offset in Bits aus . Beachten Sie, dass die vorhandene Datei geändert wird. Führen Sie es zweimal mit demselben Versatz aus, und Ihre Datei wird wiederhergestellt.

alexis
quelle
Funktioniert wie ein Zauber, und es ist klug
Kantium
1
Dies funktioniert nicht, wenn es in Python2 ausgeführt wird (der Autor sollte aus Gründen der Übersichtlichkeit / usr / bin / env python3 verwenden! Erstellen Sie eine Datei mit 00000 (ascii). Ändern Sie Bit 2. Die Ausgabe sollte nur ein Zeichen ändern, ändert sich jedoch. 4. Stellen Sie sicher, dass sie ausgeführt wird Python 3 und nicht Python 2.
Jason Baumgartner
Danke für den Vorschlag, ich habe den Shebang geändert. (Der Text hat bereits gesagt, dass die Lösung "für Python 3" ist, aber das ist besser.)
alexis
3

Ich glaube nicht, dass es einen einzigen Befehl gibt. Hier ist ein einfaches Skript, speichern Sie es als " 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;

Prüfung:

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

Dadurch wurde das niederwertige Bit (0) des dritten Zeichens umgedreht und das 'b' in 'c' geändert.

Als einzeiliger Befehl:

perl -e '$byte=shift(@ARGV);$bit=shift(@ARGV);undef $/; $file=<>; substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); print $file'
toddkaufmann
quelle
Hinweis: Sie müssen stdoutin eine Datei umleiten , um das Ergebnis zu speichern. Beispiel:perl flipbit.pl 1000000 1 data.tgz > data_corrupt.tgz
Boris Däppen
2

Endlich habe ich mit xxdund eine Lösung gefunden 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

Aber das ist hässlich.

Kantium
quelle
Ich habe einen Bash-Fehler für ein Byte mit der führenden '0' erhalten, also habe ich den XOR-Teil inecho -e "\x$((${a#0}^${b}))"
MattSmith
Entschuldigung, es sollte ein0x${a}
MattSmith
1

Wenn Sie wirklich verwenden möchten, ddist hier ein Gräuel, der den Trick macht, indem er das höchste Bit im gegebenen Byte umdreht. Passen Sie die Einstellungen für den trBefehl an, um das ausgewählte Bit zu ändern.

# 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  |
Roaima
quelle
0

Einfache Lösung mit head, tail, xxd. Das folgende Beispiel kippt das niedrigstwertige Bit im letzten Byte von 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
Juppman
quelle
0

Hier sind zwei perlEinzeiler. Der erste ändert die Datei baran Ort und Stelle:

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

Der zweite liest Datei foound schreibtbar

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

So passen Sie sich an Ihre Anwendung an: Die 2 wählt das Byte aus: 0..file_length-1, dh 2 ist das 3. Byte. Die 5 wählt aus, welches Bit umgedreht werden soll: 0-7, dh 5 ist das 6. Bit. Dies funktioniert nur für Dateien, die in den Speicher passen.

Erläuterung

-pDurchlaufen Sie die Datei, drucken Sie nach jeder Iteration,
-0777lesen Sie die gesamte Datei bei jeder Iteration in den Speicher (es gibt also nur eine Iteration).
-eFühren Sie den folgenden Perl-Code in der Schleife aus.
substrWählen Sie ein einzelnes Zeichen in der Datei aus, beginnend mit Index 2
^=chrXOR dieses Zeichens mit 1 5 mal verschoben, dh 2 ^ 5

Diese Antwort ist eine vereinfachte Version von Toddkaufmann

Gleichnamig
quelle