On my Sansa v2 web page, I’m collecting firmware binaries for these new targets in order to figure them out and kickstart the Rockbox effort for them. All firmware files have a .bin extension.
It is quite clear (by simple human inspection) that the first 0x400 bytes in each .bin file is a header (padded with 0xff bytes), as on the 0x400 index there is the ARM exception vector and then there’s ARM code following.
In the header there are numerous values, but the 32 bit value at index 4 immediately looked like it could be a checksum of some sorts.
We found two very similar firmwares for the M200 model, one for the European and one for the American in which the “checksum” values only differed by 2 even though there were clearly multiple (although not extensive) differences in the files.
A checksum that differs with so little indicates a simple algorithm. With something more fancy, like CRC32 or similar, a very small change in the files would cause a major change in the checksum value. Two checksum values near each other rather hinted on a simple addition, subtraction, xor or similar.
So I did a hexdump of the two files, cut off the headers and ran a ‘diff -u’ on them. That showed me that the first lines that differed (on index 0x15990) looked like this in the euro version:
00 00 a0 e3 a4 40 9f e5 00 00 c1 e5 04 00 a0 e1
00 fb ff eb 04 00 80 e0 45 10 a0 e3 01 10 40 e5
And like this in the US version.
02 00 a0 e3 a4 40 9f e5 00 00 c1 e5 04 00 a0 e1
00 fb ff eb 04 00 80 e0 41 10 a0 e3 01 10 40 e5
The differences are shown in bold above to make them more obvious. Plus 4, minus 2… Or the other way, minus 4 plus 2. That was almost too good to be true! The fact that these particular differences seemed to be 2 when the values were added just have to mean that the checksum is done with addition (if I was lucky). And if so, this was the only change that mattered to the header so therefore the checksum didn’t take the whole file into account…
I wrote up a small tool that would try out some variations of an “addition algorithm” with 32 bit adds and with 8 bit adds and then I tried with XORs instead to the same effect. Then it struck me that the value in the header at index 0x0c was not changing by a lot between firmwares and it had a number which was an index after the change I mentioned above, but before the subsequent changes…
The program still didn’t spit out the right value when I restricted the algorithm to the size mentioned in the header… until I realized my tool didn’t skip the header when it did the checksum, and when I added a 0x400 bytes skip the values matched! It was as simple as that. Here’s checksum.c.
There are still a few other unidentified fields in the header.