Zip bins

June 24, 2024

I learned a cool things from Justine Tunney’s work (I’m a great admirer), in particular from her work on redbean. Zip files are recognized by a record at the end of a file, and are designed to be tacked on to other binary blobs. Many file types, including pretty well all binary formats for executables are recognized from records at the beginning of the binary (on linux and the BSDs, the ELF header for example). This means (and has been exploited wonderfully in redbean) that it’s possible to create a file which simultaneously operates as an executable, but can also be unzipped to produce data.

Why would you want to do this? Well, it’s just neat. But, for example

Debateably useful, but I like that it can be done, and knowing about it starts to unlock thinking about odd and different ways of doing common (and typically annoying) things.

Redbean has facilities for making this easy, but it can be done (in a fashion much more limited than Redbean) using fairly normal tools. Unfortunately, it looks like most unix zip tools expect the zip central directory record to exist in a file in order that it be treated as a zip file, and won’t let us append a fresh one to an existing file. Luckily, Python can be abused for the task.

First we need a binary to append to:

#include <stdio.h>

int
main()
{
        printf("zipadee doo daa\n");
        return 0;
}
$ ./zippy
zipadee doo daa

Unsurprisingly, this isn’t currently a zip file

$ unzip -l zippy
Archive:  zippy
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
note:  zippy may be a plain executable, not an archive
unzip:  cannot find zipfile directory in one of zippy or
        zippy.zip, and cannot find zippy.ZIP, period.

Let’s bundle the source file into the binary. Our python script is very simple:

import zipfile

with zipfile.ZipFile('zippy', mode='a') as z:
    z.write('zippy.c')

After running this, we can test this works.

$ ./zippy
zipadee doo daa

$ unzip -l zippy
Archive:  zippy
  Length      Date    Time    Name
---------  ---------- -----   ----
       76  06-24-2024 21:58   zippy.c
---------                     -------
       76                     1 file

Neat.

Zip is quite friendly to modifications, so files can be added and removed from this archive quite readily.