Search code examples
bashhashtogglemd5bit

what is the quickest command to reverse bits in file using bash


I want to write a bash script that changes bits in file. I created txt file then used md5 and now I want to change one or two bits ( I made a command to run okteta and change there but I want to try without it )to run md5 on updated file and see if hashes are different

#!/bin/bash
echo "input file:"
read file_input
echo "choose algorythm ( md5(1) or sha256(2) )"
read choice
if [ $choice -eq 1 ]
then
    echo "running md5"
    openssl dgst -md5 $file_input > md5.txt
    FILENAME=/home/lukasz/$file_input
    FILESIZE=$(stat -c%s "$FILENAME")
    echo "size of the input file : $FILESIZE"
    hexdump -C $file_input
    echo "process of changing bits..."
    echo "how many bit do u want to change?"
    read num
    while [ $num -gt 0 ]
    do
        echo "which bit do u want to change?"
        read number
        # no idea 
        num=$(( $num - 1 ))
   done
   # okteta $file_input > testowyMD5.    
   cmp -l md5.txt md5PO.txt | grep "" -c

else
   echo 'hello'
fi

Solution

  • Would you please try the following bash script. It inverts the bit'th bit of the pos'th byte in file. It will be easy to embed the commands in your bash script. Please note you need to assign pos to some value even if you don't intend to let the user input it.

    #!/bin/bash
    
    # these values are for demonstration
    pos=16                  # position (address) of the byte to modify. 0 <= $pos <= filesize - 1
    bit=2                   # bit slice to flip. 0 <= $bit <= 7
    file="thisfile"         # filename to modify
    
    int=$(( 16#$(dd if="$file" bs=1 skip="$pos" count=1 2> /dev/null | xxd -p) ))
    (( mask = 1 << bit ))
    (( int = int ^ mask ))
    printf "%02x" "$int" | xxd -r -p | dd of="$file" bs=1 seek="$pos" conv=notrunc 2> /dev/null
    
    • dd if="$file" bs=1 skip="$pos" count=1 reads a byte of the specified address.
    • xxd -p converts the byte (character) to the hexadecimal string.
    • 16#$( commands above ) converts the hex string into integer value between 0 and 255.
    • (( mask = 1 << bit )) generates a bitmask to invert the specified bit.
    • (( int = int ^ mask )) inverts the bit.
    • The final printf ... writes the byte in the reversed procedures.

    [EDIT]
    Here is the functionalized version for easy handling:

    #!/bin/bash
    
    # usage: bitflip filename pos bit ...
    #        bit is variable length arguments and can be put as many as you want
    bitflip() {
        local file=$1; shift        # 1st argument
        local pos=$1; shift         # 2nd argument
        local -a bits=("$@")        # 3rd and remaining arguments (if any)
    
        local int mask bit
    
        int=$(( 16#$(dd if="$file" bs=1 skip="$pos" count=1 2> /dev/null | xxd -p) ))
        for bit in "${bits[@]}"; do
            (( mask = 1 << bit ))
            (( int = int ^ mask ))
        done
        printf "%02x" "$int" | xxd -r -p | dd of="$file" bs=1 seek="$pos" conv=notrunc 2> /dev/null
    }
    
    # demonstration of simple usage
    bitflip thisfile 16 2
    
    # multiple bit parameters to invert 0th and 2nd bit at once
    bitflip thisfile 16 0 2
    
    # you can also call it multiple times in a loop
    for i in 0 2; do
        bitflip thisfile 16 "$i"
    done
    

    Then your main code will look like:

    echo "how many bits do you want to change"
    read NUMBER
    while (( NUMBER-- > 0 ))
    do
        echo "put the number of the byte in which you want to change a bit ( 0 <= X <= $MAX_VALUE)"
        read BYTE_NUMBER
        echo "now choose the bit you want to reverse ( 0 <= X <= 7 )"
        read BIT_NUMBER
        bitflip "copy_org.txt" "$BYTE_NUMBER" "$BIT_NUMBER"
    done
    

    Please note it always overwrites the file. You may need to make a backup in advance, if needed.