How to edit ISO Images (Including Bootable ISOs)

ISO Master install isomaster

I have used ISO Master to add files to ISOs before. It is suitable for what you require because it retains the state of whether or not the ISO is bootable. I have used it in the past to add other content (like music) to a live disk. Note, however, that after making changes to an ISO file, you can only "Save As", i.e. another ISO file will be created, so make sure you have enough disk space for both.

isomaster


PowerISO just released a free Linux version of their ISO editing software1. Like ISO Master, it retains the state of whether or not the ISO is bootable. However, it allows you to save directly to the original file (by deleting it first), so there is no need to have enough disk space for both files. I also find its interface to be easier and more intuitive to use than ISO Master.

screenshot of PowerISO for Linux


1 Unlike their Windows program, the Linux version free to use, without any size limitations or the like.


The common answer to this is to unpack the iso file, modify it, and pack it again. It looks like "ISO Master", as mentioned in dv3500ea's answer, is a good front-end to do that.

If:

  • you don't have enough space for that
  • you only want to make a surgical modification instead of rewriting the whole thing
  • you want to modify a storage device that contains an isofs filesystem (a.k.a. iso9660) without copying the whole device, or
  • if you think this unpacking/repacking thing is just not hacky enough

Then this answer is for you!

In summary, we will replace an existing file in the isofs filesystem with our desired file. Our desired file must be smaller than the existing (target) file, and trailing whitespace (or garbage) must be acceptable. This actually requires only two commands, but be careful: a typo can destroy the target filesystem completely, or even overwrite the source file. Backups are your friend!

In my case, I wanted to store a script in a live boot so I don't have to retype it every time. The script is at script.py and my target (a USB stick) is at /dev/sdc. The size of the script is 202 bytes, so our first step is to find a file larger than 202 bytes, so we can overwrite it. After mounting it at /mnt, I found a suitable file at /mnt/info.txt.

We can't just overwrite info.txt on the mountpoint, it will complain that it's a read-only filesystem. We are root, though, so let's show 'em what that means! We need to figure out where info.txt is on the filesystem. Find some string that is (probably) unique to info.txt, for example This is the official distribution CD of X., and search for it on the disk:

$ sudo strings -a -t d /dev/sdc | grep 'CD of X.'
2573588480 This is the official distribution CD of X. See INSTALL for how to [...]

Alternatively, this can also be done with grep, which is a lot faster, but then you need to specify it from the beginning: $ sudo grep -oba 'This is ...' /dev/sdc.

Now that we know where it is, we just need to replace those bytes with our file:

$ sudo dd if=script.py of=/dev/sdc conv=notrunc bs=1 seek=2573588480 count=202

This line:

  • copies bytes from the input file (if) to the output file (of), and it does not care that the output file is actually a device, because "everything is a file".
  • conv=notrunc tells it not to truncate the output file, because we only want to overwrite a few bytes, not overwrite the file from a certain point onward.
  • bs=1 sets the block size to 1. You usually want a block size of 4k or higher, but this both avoids having to do (inline) math and lets us specify the location exact to the byte.
  • seek=N seeks to a certain point in the output file (note that seek=N is different from skip=N because skip skips bytes from the input file!). We set it, of course, to where the target text is.
  • count=N copy only this many bytes. I think this can be omitted because it will notice the end of the input file, but I left it in just to be sure.

And voila, the file is overwritten!

But wait, the target file was larger than our script, so on the USB stick, the file is now something like: "while do if run() blah; blah(); yright 2007 X Inc.". There is trailing garbage. Two ways to fix this: make our input file longer (add spaces), or add a comment symbol at the end. Note that many editors add a newline at the end, so you might want to set count= to N-1 bytes (if your file is now 203 bytes, and you notice that the last byte is a newline, set count to 202). You can check a file for newlines by using xxd script.py | tail and checking if the last byte is 0a (or, in weird cases, 0d).

The process is identical for an .iso file, just mentally replace /dev/sdc with your.iso.

Note that when you check the target in your mountpoint to see whether it worked, you might need to use strings again (this time searching for your script) since the file is probably still in read cache.