CorelDRAW is a popular vector graphics program. Its native .CDR file format has gone through many different versions. I want to focus on the files written by CorelDRAW version 2. These are the .CDR files that begin with “WLl” or “WLm“. Even though it dates from 1989 or so, and has been obsolete for most of the time since then, I had little trouble finding quite a few example files in this format.
Each of these files contains what I assume is a “preview” image: a small black-and-white bitmap representation of the main (vector-based) image, probably intended for use in file-selection dialogs, and things like that. In some files the image is 90×90 pixels in size, and in others it is 128×128 pixels.
I decoded some of these preview images, and I saw that nearly every one of them has something wrong with the the rightmost column of pixels, and the bottom row of pixels. Here are some examples:
At first I thought I was decoding them incorrectly, but on further analysis I’m quite sure that’s not the case. The pixels that are part of the foreground image are always well-defined, which would make no sense if viewers were supposed to ignore those pixels.
There’s something else we can look at. The 90×90 images require 11.25 bytes per row, but a full 12 bytes are stored in the file. What’s in the unused 6 bits (0.75 bytes) in each row? If I pretend the images are actually 96×90, this is what the examples above look like:
There’s a clear correlation between the two kinds of uninitialized bits.
While this is not the point of this post (it actually has no point), since I’m on the topic I’ll point out that writing random memory to a file is kind of a bad thing, for security and privacy reasons. But this practice is not uncommon, especially in old files like this. It’s no more than 16 contiguous bytes in this case, but that’s enough to leak a password, if you’re really unlucky.
From this evidence, how much can we surmise about CorelDRAW v2’s internal operation? I speculate that this is pretty much what it does:
- When the user saves a file, it allocates some memory in which to render the preview image. This memory includes any needed row-padding.
- It initializes the in-memory image’s background to white.
- … But it has an off-by-one bug that causes it to fail to initialize the last row and column. Often, the uninitialized pixels happen to be black (0) or mostly black; other times they are seemingly random.
- It draws the foreground image onto this canvas. Some of uninitialized pixels may get overwritten by this image; others remain in their uninitialized state.
- Then the in-memory image is copied/translated to the .CDR disk file. It might be converted to 1 bit/pixel at this point, from an in-memory format with more bits/pixel.
I do have to wonder how this bug made it into at least two editions of CorelDRAW 2 — though I acknowledge that software development was very different in those days. Apparently, either nothing ever used the preview images, or whatever did use them didn’t display the last row and column.