9

I need to modify a byte in a binary file at a certain offset.

Example:

  • Input file: A.bin
  • Output file: B.bin

I need to read a byte at the offset 0x40c from A.bin, clear to 0 least significant 2 bits of this byte, and then write file B.bin equal to A.bin, but with the calculated byte at offset 0x40c.

Modify a byte in a binary file using standard Linux command line tools.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mastupristi
  • 1,240
  • 1
  • 13
  • 29
  • http://unix.stackexchange.com/questions/155085/fetching-individual-bytes-from-a-binary-file-into-a-variable-with-bash? – Freek Wiedijk Jan 23 '17 at 12:25
  • Possible duplicate of [CLI: Write byte at address (hexedit/modify binary from the command line)](https://stackoverflow.com/questions/4783657/cli-write-byte-at-address-hexedit-modify-binary-from-the-command-line) – eadmaster Jan 13 '18 at 03:49
  • What is the question? It is stated as homework or a work order. – Peter Mortensen May 20 '22 at 19:17

1 Answers1

10
# Read one byte at offset 40C
b_hex=$(xxd -seek $((16#40C)) -l 1 -ps A.bin -)

# Delete the three least significant bits
b_dec=$(($((16#$b_hex)) & $((2#11111000))))
cp A.bin B.bin

# Write one byte back at offset 40C
printf "00040c: %02x" $b_dec | xxd -r - B.bin

It was tested in Bash and Z shell (zsh) on OS X and Linux.

The last line explained:

  • 00040c: is the offset xxd should write to
  • %02x converts $b from decimal to hexadecimal
  • xxd -r - B.bin: reverse hexadecimal dump (xxd -r) — take the byte number and the hexadecimal value from standard input (-) and write to B.bin
hansaplast
  • 11,007
  • 2
  • 61
  • 75
  • I have upvoted your answer because I like it, but the OP asked for two different files, so `cp A.bin B.bin` should be put somewhere before the final `dd`, which would operate on `B.bin`; and, more importantly, this doesn’t work if `$b` equals ASCII NUL ... – Dario Jan 23 '17 at 15:44
  • @Dario thanks for pointing out that it should operate on a new file, added that in – hansaplast Jan 23 '17 at 15:55
  • @Dario woult it not work on `NUL`? `b=$(chr 0) && printf $b | dd of=B.bin seek=$((16#40C)) bs=1 count=1 conv=notrunc` works for me – hansaplast Jan 23 '17 at 15:56
  • I’m running BASH 4.3.30(1) on a Debian Jessie. `echo abc >B.bin`; `xxd -ps B.bin` returns `6162630a`; `printf` (both builtin and `/usr/bin/printf`) gives error if I don’t quote the argument and `b=$(chr 0) && printf "$b" | dd of=B.bin seek=2 bs=1 count=1 conv=notrunc`; `xxd -ps B.bin` returns same as before. It also says `0 bytes (0 B) copied` which is the problem. There used to be no way to pass NULs either by `echo` or `printf`. Maybe this changed with later bash versions? – Dario Jan 23 '17 at 16:15
  • If I replace the last line of your script with `printf "00040c: %02x" $b | xxd -r - B.bin` it works in all cases, because if `$b` is NUL (or, equivalently for my shell, an empty string) it gets expanded by `%02x` as `00`. `xxd` is a standard command line tool as `dd` is. – Dario Jan 23 '17 at 16:45
  • @Dario thanks for the `xxd` suggestion. I altered the last line and also the reading. Now I don't need the `ord` function anymore so the code is now stripped down to 4 lines. Can you proofread that I didn't add any errors? – hansaplast Jan 23 '17 at 17:52
  • Seems to be OK. – Dario Jan 23 '17 at 18:05
  • 1
    Re *"take the line number"*: But it is a binary file(?). – Peter Mortensen May 20 '22 at 19:29
  • @PeterMortensen correct, I fixed it in the answer. Also: Thanks for the edit! – hansaplast May 24 '22 at 04:01