Skip to content

Upgrade.bin format

Kate Temkin edited this page Dec 9, 2020 · 9 revisions

Data format

The FLIR Upgrade.bin format is almost a plain binary format: is just has periodic checksums inserted every kilobyte. Its general format is:

[ 2 bytes    ] CRC-16 checksum (xmodem spec; so IV of 0x0000 and polynomial of 0x1021) of the following data section
[ 2 bytes    ] zeroes
[ 1024 bytes ] The data segment protected by the CRC.

The last segment (and only the last segment) can be shorter than 1024 bytes, in which case the CRC guards the shorter section. So, if the last section had 324 bytes of data, it'd look like:

[ 2 bytes    ] CRC-16 checksum (xmodem spec; so IV of 0x0000 and polynomial of 0x1021) of the following data section
[ 2 bytes    ] zeroes
[ 324 bytes  ] The data segment protected by the CRC.

Reverse Engineering

This is a pretty straightforward format to reverse engineer, especially if you know that meta-data occurs at the start of every 1024KiB block. Looking at the file, there are two features that really make this stand out.

First, we can see that this file looks a lot like a normal Cortex-M3 firmware image:

00000000  50 24 00 00 78 35 00 20  b5 36 01 08 39 8f 01 08  |P$..x5. .6..9...|
00000010  3d 8f 01 08 43 8f 01 08  49 8f 01 08 4f 8f 01 08  |=...C...I...O...|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  55 8f 01 08 57 8f 01 08  00 00 00 00 59 8f 01 08  |U...W.......Y...|
00000040  5b 8f 01 08 cf 36 01 08  cf 36 01 08 cf 36 01 08  |[....6...6...6..|
00000050  cf 36 01 08 cf 36 01 08  cf 36 01 08 cf 36 01 08  |.6...6...6...6..|

The first section looks a lot like a vector table: but with one little difference. Given the format of the Cortex-M3 vector table, we'd expect the first entry to be the initial stack pointer (RAM address) and the rest to be code pointers (which are always odd-valued).

STM32F103VE memory map

If we consider this to be a raw binary, this doesn't make sense-- we have two even addresses in a row. Since these are even, they can't both be code pointer addresses. If we skip the first word (four bytes), things start to make a lot more sense: the first address is in RAM, and all of the subsequent ones are odd addresses in Flash. This suggests we have at least some metadata words hiding in our binary!

Another hint comes if we try to visualize the binary as an image. We can use ImageMagick to interpret the binary as a 8-bit depth image:

$ convert -size 32x7374 gray:Upgrade.orig.bin as_an_image.png

Looking at the resultant image, we notice pictures that seem fine for a few rows, but which have obvious discontinuities:

Image with discontinuities Image with discontinuities

This isn't a stride ("pixels per row") issue: we can see that the images are fine for a few lines, and then are disrupted by a few pixels that don't seem to belong. This is particularly obvious in the dark areas of the image on the right- we can see the stray discontinuity pixels! If we zoom out a little, we can even see that these disruptions are evenly spaced: they're visible as a diagonal line of discontinuities cutting from the upper left to the bottom right:

If we measure the distance between each discontinuity, we'll see that 1024 image bytes exist between each four-byte discontinuity. Surely enough, extracting the data bytes from the meta-data results in much nicer-looking images:

Fixed!

This is enough for us to extract the firmware from one of the FLIR upgrade images, but if we want to be able to produce new images for the camera, we're going to have to figure out what the metadata means. Luckily, this is also fairly straightforward. If we look at the metadata words, they all share a common format:

  XX YY 00 00 # General format
  50 24 00 00 # first metadata word
  42 08 00 00 # second metadata word
  65 9d 00 00 # third metadata word
  ...

Now, we just need to figure out the relationship between the two bytes in the metadata word and the data nearby. Since we have 16-bits of metadata, this is likely to be either a 16-bit checksum or a CRC-16. After trying various algorithms, it quickly became apparent that this was CRC-16/XMODEM:

  0x2450 # CRC-16 of first 1024-byte block
  0x0842 # CRC-16 of second 1024-byte block
  0x9d65 # CRC-16 of third 1024-byte block
  ...
Clone this wiki locally