Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I need a large number of damaged .png files in order to test my project. To that end, I need to set all bytes from the 0x054-th to the 0xa00-th to 0.

.png files contain chunks with checksums, I want to alter the image data chunk (IDAT), without updating the checksum. Furthermore, I want to damage a lot of bytes, so that a visible (black) area appears when displaying the image (provided the viewing program ignores the checksum mismatch).

Here is what I have got so far:


# This script reads all .png or .PNG files in the current folder,
# sets all bytes at offsets [0x054, 0xa00] to 0
# and overwrites the files back.


for file in $(find "$target_dir" -name "*.png")         #TODO: -name "*.PNG")
    # Copy first part of the file unchanged.
    xxd -p -s $start_bytes "$file" > $temp_file_ascii

    # Create some zero bytes, followed by 'a',
    # because I don't know how to add just zeroes.
    echo "$len_bytes: 41" | xxd -r >> $temp_file_ascii

    # Copy the rest of the input file.
    # ??

    mv outfile.png "$file"

EDIT: the finished script, using the accepted answer:


if [ "$#" != 3 ]
    echo "Usage: "
    echo "break_png.sh <target_dir> <start_offset> <num_zeroed_bytes>"

for file in $(find "$1" -name "*.png")
    dd if=/dev/zero of=$file bs=1 seek=$(($2)) count=$(($3)) conv=notrunc
share|improve this question
Note that xxd -p -s 0x54 would start at the 0x55th byte (xxd -p -s 0 starts at the first byte), not 0x54th. You may want to clarify your question to state which one you want. – Stéphane Chazelas Mar 1 at 11:20
@StéphaneChazelas, I didn't know that. I need the first modified byte to be 0x54-th and the last to be (0xa00-0x001)-th. – Vorac Mar 1 at 11:31
up vote 7 down vote accepted

You can do something much simpler with dd:

dd if=/dev/zero \
   of="$your_target_file" \
   bs=1 \
   seek="$((start_offset))" \
   count="$((num_zeros))" \

With $start_offset being the start of your range of bytes to zero out (zero-based, as in to erase from the nth byte, use n-1), and $num_zeros the length of that range. The $((...)) will take care of converting hexadecimal to decimal.

(Other tests you could run would be to set if to /dev/urandom rather than /dev/zero, or overwrite the checksums with random data too.)

share|improve this answer
Note that 0x54 makes $start_offset=84 and ending 0xa00 makes $num_zeros=2476. – Ralph Rönnquist Mar 1 at 9:55
@RalphRönnquist, if that's meant to be from the 0x054-th to the 0xa00-th, then start_offset would be 83 (0x54 - 1) and num_zeros would be 2477 (0xa00 - 0x54 + 1). – Stéphane Chazelas Mar 1 at 10:58
Thanks for the edit, @Stéphane. – Mat Mar 1 at 11:02
Thanks, I was sure someone would come out with a one liner for the task I have been trying to solve for an hour. AND I learned about $((...)). – Vorac Mar 1 at 11:19

With ksh93:

 PATH=/opt/ast/bin:$PATH find "$targetdir" -name '*.png' -type f -exec ksh93 -c '
   for file do
      head -c "$((0xa00 - 0x54 + 1))" < /dev/zero 1<> "$file" >#((0x54 - 1))
   done' ksh {} +

>#((...)) being the seek operator in ksh93. That's relatively efficient in that it runs as few ksh93 as possible and all the commands are built-in.

With recent versions of zsh:

find "$targetdir" -name '*.png' -type f -exec zsh -c '
  zmodload zsh/system
  for file do
    {sysseek -u1 0x54-1 && print -rn $z} 1<> $file
  done' zsh {} +

Those change the 0x54th to 0xa00th bytes (2477 bytes). It seems like you actually want to change the 0x55th to 0xa00th ones though. If that's the case, just remove the + 1s and - 1s in the codes above.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.