Patching a binary with dd

I have read this quote (below) several times, most recently here, and am continually puzzled at how dd can be used to patch anything let alone a compiler:

The Unix system I used at school, 30 years ago, was very limited in RAM and Disk space. Especially, the /usr/tmp file system was very small, which led to problems when someone tried to compile a large program. Of course, students weren’t supposed to write “large programs” anyway; large programs were typically source codes copied from “somewhere”. Many of us copied /usr/bin/cc to /home/<myname>/cc, and used dd to patch the binary to use /tmp instead of /usr/tmp, which was bigger. Of course, this just made the problem worse – the disk space occupied by these copies did matter those days, and now /tmp filled up regularly, preventing other users from even editing their files. After they found out what happened, the sysadmins did a chmod go-r /bin/* /usr/bin/* which “fixed” the problem, and deleted all our copies of the C compiler.

(Emphasis mine)

The dd man-page says nothing about patching and a don’t think it could be re-purposed to do this anyway.

Could binaries really be patched with dd? Is there any historical significance to this?

Asked By: amziraro

||

Let’s try it. Here’s a trivial C program:

#include <stdio.h>
int main(int argc, char **argv) {
    puts("/usr/tmp");
}

We’ll build that into test:

$ cc -o test test.c

If we run it, it prints “/usr/tmp”.

Let’s find out where “/usr/tmp” is in the binary:

$ strings -t d test | grep /usr/tmp
1460 /usr/tmp

-t d prints the offset in decimal into the file of each string it finds.

Now let’s make a temporary file with just “/tmp” in it:

$ printf "/tmpx00" > tmp

So now we have the binary, we know where the string we want to change is, and we have a file with the replacement string in it.

Now we can use dd:

$ dd if=tmp of=test obs=1 seek=1460 conv=notrunc

This reads data from tmp (our “/tmp” file), writing it into our binary, using an output block size of 1 byte, skipping to the offset we found earlier before it writes anything, and explicitly not truncating the file when it’s done.

We can run the patched executable:

$ ./test
/tmp

The string literal the program prints out has been changed, so it now contains “/tmptmp“, but the string functions stop as soon as they see the first null byte. This patching only allows making the string shorter or the same length, and not longer, but it’s adequate for these purposes.

So not only can we patch things using dd, we’ve just done it.

Answered By: Michael Homer

It depends on what you mean by “patch the binary”.

I change binaries using dd sometimes. Of course there is no such feature in dd, but it can open files, and read and write things at specific offsets, so if you know what to write where, voila there is your patch.

For example I had this binary that contained some PNG data. Use binwalk to find the offset, dd to extract it (usually binwalk also extracts things but my copy was buggy), edit it with gimp, make sure the edited file is same size or smaller than the original one (changing offsets is not something you can do easily), and then use dd to put the changed image back in place.

$ binwalk thebinary
[…]
4194643    0x400153     PNG image, 800 x 160, 8-bit/color RGB, non-interlaced
[…]
$ dd if=nickel bs=1 skip=4194641 count=2 conv=swab | od -i
21869 # file size in this case - depends on the binary format
$ dd if=thebinary bs=1 skip=4194643 count=21869 of=theimage.png
$ gimp theimage.png
$ pngcrush myimage.png myimage.crush.png
# make sure myimage.crush.png is smaller than the original
$ dd if=myimage.crush.png of=thebinary bs=1 seek=4194643 conv=notrunc

Sometimes I also wish to replace strings in binaries (such as path or variable names). While this could also be done using dd, it is simpler to do so using sed. You just have to make sure the string you replace with has the same length as the original string so you don’t end up changing offsets.

sed -e s@/the/old/save/path@/the/new/save/path@ -i thebinary

or to pick up @MichaelHomer’s example with a 0-byte added in:

sed -e 's@/usr/tmp@/tmpx00tmp@' -i test

Of course you have to verify whether it actually works afterwards.

Answered By: frostschutz

Yes, binaries can be patched with dd.

To patch a file with data from stdin:

dd of=file.bin bs=1 count=2 conv=notrunc

Then enter the (text) data to be patched with. The above only patches 2 bytes (from file beginning), even if more than 2 characters are entered at prompt.
To patch middle of file, supply position (address) of file data via seek to begin patching.
eg.

dd of=file.out bs=1 count=2 seek=2 conv=notrunc

or

echo "<address: data>" | xxd -r - file.out

If patching non-printable characters, make a binary temp file with data to be patched.
eg.

dd if=file.in of=file.out bs=1 count=2 seek=2 conv=notrunc

or

xxd -r hexoffsets.in file.out

or supply hex data with offset on stdin

echo "<address: hex-data>" | xxd -r - file.out

Definition:
Patch means to convert hexdump into binary (from xxd manpage)

Tested on Ubuntu 16.04.7

Answered By: Zimba
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.