This Image Is Also a Valid Javascript File
Tags: javascript, html, webdev
Published: 2020-09-06 08:39:12

Images are usually stored as binary files, while a Javascript file is basically just text. Both have to follow their own rules: Images have a concrete file format, encoding the data in a certain way. Javascript files have to follow a specific syntax in order to be executable. I wondered: Can I create an image file that has valid Javascript syntax, so it is also executable?

Before you read on, I highly recommend checking out this code sandbox with the result of my experimentation: 

https://codesandbox.io/s/executable-gif-8yq0j?file=/index.html

If you want to check out the image and inspect it yourself, you can download it here: 

https://executable-gif.glitch.me/image.gif

# Choosing the Right Image Type

Unfortunately, images contain a lot of binary data which will throw an error if interpreted as Javascript. So my first idea was: What if we just put all the image data in a large comment, like this:

```javascript
/*ALL OF THE BINARY IMAGE DATA*/
```

That would be a valid Javascript file. However, image files need to start with a specific sequence of bytes; a file header that is specific to the image format. PNG files for example always have to start with the byte sequence _89 50 4E 47 0D 0A 1A 0A_. If an image would start with `/*`, it would not be a valid image file anymore.

This file header lead to the next idea: What if we could use this byte sequence as variable name and have a huge string assignment like this:

```javascript
PNG=`ALL OF THE BINARY IMAGE DATA`;
```

We are using template strings instead of the normal `"` or `'` strings because the binary data could contain linebreaks and template strings are better at dealing with those.

Unfortunately, most byte sequences for image file headers contain unprintable characters that are not allowed in variable names. But there is one image format we can use: GIF. The GIF header block is _47 49 46 38 39 61_, which conveniently spells **GIF89a** in ASCII, a perfectly legal variable name!

# Choosing the Right Image Dimensions

Now that we found an image format that starts with a valid variable name, we need to add the equals and backtick characters. The next four bytes of the file are therefore: _3D 09 60 04_

![First bytes of the image](https://dev-to-uploads.s3.amazonaws.com/i/0ogcfqgdgvlr7v0vw6kp.png)

In the gif format, the four bytes following the header specify the dimensions of the image. We have to fit _3D_ (the equals sign) and _60_ (the backtick that opens the string) in there. GIF uses little endian so the second and fourth character have a huge influence on image dimensions. We want to keep them as small as possible to not end up with an image that is tens of thousands pixels wide. Therefore we store the big bytes _3D_ and _60_ in the least significant bytes.

The second byte of the image width needs to be a valid whitespace character, because this would be the space between the equals sign and the beginning of the string ``GIF89a= `...``. Keep in mind that the hexcode of the characters should be as small as possible, or the image would be huge.

The smallest whitespace character is _09_, the horizontal tab. This gives us an image width of _3D 09_, which in little endian translates to 2365; a bit wider than I would have liked, but still reasonable.

For the second height byte, we can just choose something that produces a nice aspect ratio. I chose _04_, which produces a height of _60 04_, or 1120.

# Getting our own script in there

Right now, our executable gif does not really do anything. It just assigns a large string to the global variable `GIF89a`. We want something interesting to happen! Most of the data inside the GIF is for encoding the image, so if we try to add Javascript in there, we would probably end up with a very corrupted image. But for some reason, the GIF format contains something called a **Comment Extension**. This is a place to store some metadata that will not be interpreted by the GIF decoder - a perfect place for our Javascript logic.

This comment extension comes right after the GIF color table. Since we can put any content in there, we can easily close the **GIF89a** string, add all our Javascript and then start a multiline comment block, so the rest of the image does not interfere with the Javascript parser. 

All in all, our file could then look like this:

```javascript
GIF89a= ` BINARY COLOR TABLE DATA ... COMMENT BLOCK:

`;alert("Javascript!");/*

REST OF THE IMAGE */
```

There is a small restriction: While the comment block itself can be of any size, it is composed of multiple sub-blocks, each of which has a maximum size of 255. Between the subblocks is a single byte that indicates the length of the next subblock. So in order to fit a larger script in there, it has to be divided into smaller chunks, like this:

```javascript
alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...
```

The hexcodes in the comments are the bytes that indicate the size of the next subblock. They are irrelevant for the Javascript, but required for the GIF file format. In order to prevent them from interfering with the rest of the code, they have to be in comments. I wrote a small script that processes the script chunks and adds them to the image file: https://gist.github.com/SebastianStamm/c2433819cb9e2e5af84df0904aa43cb8

# Cleaning up the Binary

Now that we have the basic structure down, we need to make sure that the binary image data does not ruin our syntax. As explained in the previous section, the file has three sections: The first one is an assignment to the variable **GIF89a**, the second one is the Javascript code and the third one is a multiline comment.

Let's have a look at the first part, the variable assignment:

```javascript
GIF89a= ` BINARY DATA `;
```

If the binary data would contain the character `` ` `` or the character combination `${` we are in trouble because this would either end the template string or produce an invalid expression. The fix here is quite easy: Just change the binary data! E.g. instead of using the `` ` `` character (hexcode _60_), we could use the character `a` (hexcode _61_). Since this part of the file contains the color table, it would result in some of the colors being slightly off, e.g. using the color `#286148` instead of `#286048`. It is unlikely that someone will notice the difference.

## Fighting the corruption

At the end of the Javascript code, we opened a multiline comment in order to make sure the binary image data does not interfere with the Javascript parsing:

```javascript
alert("Script done");/*BINARY IMAGE DATA ...
```

If the image data would contain the character sequence `*/`, the comment would end prematurely, which would result in an invalid Javascript file. Here again we can manually change one of the two characters so they no longer end the comment. However, since we are now in the encoded image section, this will result in a corrupted image, like this:

![Corrupted Image](https://dev-to-uploads.s3.amazonaws.com/i/yyiadl1lq7y5pacghd7f.gif)

In extreme cases the image could not be displayed at all. By carefully choosing which bit to flip I was able to minimize the corruption. Fortunately there were only a handful of instances of the harmful `*/` combination to deal with. There is still a bit of corruption visible in the final image, e.g. at the bottom of the "Valid Javascript File" string, but overall I am pretty happy with the result.

## Ending the File

The last operation we have to perform is at the end of the file. The file has to end with the bytes _00 3B_. So we have to end our comment earlier. Since this is the end of the file and any potential corruption would not be very visible, I just ended the multi block comment and added a single line comment so that the end of the file would not cause any problems when parsing:

```javascript
/* BINARY DATA*/// 00 3B
```

# Convincing the Browser to Execute an Image

Now, after all this, we finally have a file that is both an image as well as a valid Javascript file. But there is one last challenge we have to overcome: If we upload the image to a server and try to use it in a script tag, we would most likely see an error like this:

> Refused to execute script from 'http://localhost:8080/image.gif' because its MIME type ('image/gif') is not executable.

So the browser rightfully says "That's an image! I am not going to execute that!". Which in most cases is a good mindset to have. But we want to execute it anyway. Our solution here is to just not tell the browser that it's an image. For that I wrote a small server that serves the image without any header information.

Without the MIME type information from the header, the browser does not know that it is an image and just does what fits the context best: Display it as image in an `<img>` tag, or execute it as Javascript in a `<script>` tag.

# But ... why?

That is something I have yet to figure out. It was a nice mental challenge to make this stuff work, but if you can think of any scenarios where this might actually be useful, please let me know!