72

Is there any straightforward way to modify a binary from the commandline?

Let's say I know that my binary contains 1234ABCD and I want to change it to 12FFABCD or FFFFABCD or maybe even FF34FFABC0 (you get the idea) :-)

How might I achieve that without using any special purpose tools like Swiss File Knife or similar?

It would be great to do it just from the command line with only standard Linux tools.

Or maybe even better, instead for searching for the hexadecimal string I want to replace directly writing FF at offset 0x10000, 12 at offset 0x100001 and so on.

It should be scriptable and run directly from the command line. I am looking for something like "binary-which-is-included-in-the-distro --write AB --at-offset 100000 --file thebinary.bin". I am quite sure that it is possible with dd, but I wasn't able to wrap my head around the man page.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Josh
  • 721
  • 1
  • 5
  • 4

8 Answers8

127
printf '\x31\xc0\xc3' | dd of=test_blob bs=1 seek=100 count=3 conv=notrunc

dd arguments:

  • of | file to patch
  • bs | 1 byte at a time please
  • seek | go to position 100 (decimal)
  • conv=notrunc | don't truncate the output after the edit (which dd does by default)

One Josh looking out for another ;)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Josh
  • 6,155
  • 2
  • 22
  • 26
  • 4
    Beautiful elegant solution! Is it possible to insert bytes using this command as well? Or to output the patched file to a different filename? Or for the seek position to be based on a variable like the filesize? – ben Nov 02 '16 at 18:57
  • 1
    This is a great solution, but sadly, it doesn't work with BusyBox. BusyBox's implementation of dd doesn't support conv=notrunc (or conv=anything, for that matter). – Adam Haun May 25 '18 at 16:21
  • 1
    Be sure to note that seek may not be in bytes, but in the size of obs. – markzz May 13 '19 at 18:14
  • 1
    @AdamHaun BusyBox's dd does support some conv= options, including conv=notrunc, and I have verified that this solution works with BusyBox in its default configuration (BusyBox is very configurable and this feature can be disabled in custom builds). – Marcel May 03 '20 at 16:18
  • This overwrites data. To append data at a specific position instead, the second solution of this answer worked: https://askubuntu.com/questions/1276339/how-do-i-append-data-to-a-binary-file-with-an-hex-editor/1276363#1276363 – baptx Dec 08 '21 at 15:10
  • Great solution! However, how to patch a data going to another program using a pipe? E.g. in `echo "char* x = \"xxx\";" | gcc -xc - -c -std=c11 -pedantic -Wall -Wextra` how to patch `xxx` to `xx`? – pmor Apr 11 '22 at 21:33
12

The printf + dd based solutions do not seem to work for writing out zeros. Here is a generic solution in Python 3 (included in all modern distributions) which should work for all byte values...

#!/usr/bin/env python3
#file: set-byte

import sys

fileName = sys.argv[1]
offset = int(sys.argv[2], 0)
byte = int(sys.argv[3], 0)

with open(fileName, "r+b") as fh:
    fh.seek(offset)
    fh.write(bytes([byte]))

Usage...

set-byte eeprom_bad.bin 0x7D00 0
set-byte eeprom_bad.bin 1000 0xff

Note: This code can handle input numbers both in hexadecimal (prefixed by 0x) and decimal (no prefix).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sandeep Datta
  • 28,607
  • 15
  • 70
  • 90
  • 2
    Important you run this in python3 (not python2) or else you end up writing literal string representation of a list: `[byte]` – Eugene K Dec 01 '18 at 20:21
  • 1
    The query was for Shell script, not Python script. – s3n0 Mar 06 '20 at 16:18
  • 2
    The printf+dd works when writing out zeros, for this is mandatory to use the "conv=notrunc" option. – King Sumo Oct 16 '20 at 11:57
  • 2
    printf '\x00\x00\x00\x00' | dd of=blah bs=1 seek=24 count=4 conv=notrunc works (Linux Mint 20) – Agguro Dec 27 '20 at 09:19
  • Support for hex in printf depends on the printf implementation. Bash built-in printf supports it, as does GNU printf (the one found as /usr/bin/printf in many Linux). But the built-in printf in some sh variants (like dash) doesn't support it. Octal should be supported by all variants, though. See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html for the POSIX explanation. So an alternative is to use something else, e.g. xxd or perl, to output the stream to "poke" with dd. – Pedro Gimeno Apr 05 '21 at 23:39
  • This works in out-of-the-box Ubuntu 20: `printf '\x00\x00\x00\x00' | dd of=somefile bs=1 seek=4 count=4 conv=notrunc` – BuvinJ Sep 15 '21 at 14:48
10

Here's a Bash function replaceByte, which takes the following parameters:

  • the name of the file,
  • an offset of the byte in the file to rewrite, and
  • the new value of the byte (a number).
#!/bin/bash

# param 1: file
# param 2: offset
# param 3: value
function replaceByte() {
    printf "$(printf '\\x%02X' $3)" | dd of="$1" bs=1 seek=$2 count=1 conv=notrunc &> /dev/null
}

# Usage:
# replaceByte 'thefile' $offset 95
CoolOppo
  • 503
  • 9
  • 17
sinharaj
  • 1,093
  • 2
  • 11
  • 16
4

The xxd tool, which comes with Vim (and thus is quite likely to be available) allows to hex dump a binary file and construct a new binary file from a modified hex dump.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SK-logic
  • 9,605
  • 1
  • 23
  • 35
2

Some alternatives:

eadmaster
  • 1,347
  • 13
  • 23
2

Writing the same byte at two different positions in the same file with a one liner.

printf '\x00'| tee >(dd of=filename bs=1 count=1 seek=692 conv=notrunc status=none) \
    >(dd of=filename bs=1 count=1 seek=624 conv=notrunc status=none)

status=none very useful when you don't want any statistics out of dd.

memscan
  • 21
  • 1
1

If you don't need it to be scriptable, you could try the hexedit utility. It is available in many Linux distributions (if not installed by default, it can usually be found in the distribution's package repository).

If your distribution doesn't have it, you can build and install it from source.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dan Moulding
  • 211,373
  • 23
  • 97
  • 98
  • It should be scriptable and run directly from the commandline. I am looking for something like "binary-which-is-included-in-the-distro --write AB --at-offset 100000 --file thebinary.bin". I am quite sure that it is possible with "dd", but I wasn't able to wrap my head around the man page. – Josh Jan 24 '11 at 15:40
0

Regarding Josh's answer: In case you want to do it for a specific address -

hexdump -C {file location}

with some hexadecimal value you might have tried to add 0x, but it would fail:

dd: warning: ‘0x’ is a zero multiplier; use ‘00x’ if that is intended

You can achieve this by encapsulating it with $(()) that the terminal will translate as an integer value:

mybinary={file location}
printf '\x31\xc0\xc3' | dd of=$mybinary bs=1 seek=$((0x100)) count=3 conv=notrunc
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81