Nikon Firmware Insights #01

Here’s the first post in my Nikon Firmware investigations.

Firstly after removing the encryption from the Nikon DSLR firmware bundle file, we should extract each file from the bundle.

Form my looking at the files I have, there is 0x20 bytes of fluff, then there is a file count, header length, and a couple of dummy int32’s. Then there’s 0x10 bytes of file name, file start, length, and two more dummy int32’s. After that there’s a word ‘checksum’ and some padding bytes.

Thus this code (C#) pulls the files out of the bundle file decoded by the previous post:

static void ExactFirmware3(string fileName)  
{  
  if (File.Exists(fileName))  
  {  
    BinaryReader br = null;  

    try  
    {  
      br = new BinaryReader(File.Open(fileName + ".out.bin",  
      FileMode.Open, FileAccess.Read, FileShare.ReadWrite));  
      br.BaseStream.Seek(0x20, SeekOrigin.Begin);  

      uint count = ReadUint32(br);  
      uint headerlen = ReadUint32(br);  
      uint dummy1 = ReadUint32(br);  
      uint dummy2 = ReadUint32(br);  

      var header = new List<Tuple<string,uint,uint>>();  

      // Read Header  
      for (int c = 0; c < count; c++)  
      {  
        string firmwareName = Path.Combine( Path.GetDirectoryName(fileName),  
        ReadString(br, 16));  
        uint start = ReadUint32(br);  
        uint len = ReadUint32(br);  
        uint hdummy1 = ReadUint32(br);  
        uint hdummy2 = ReadUint32(br);  

        header.Add(new Tuple<string, uint, uint>(firmwareName, start, len));  
      }  

      foreach (var t in header)  
      {  
        DumpFile(br, t.Item1, t.Item2, t.Item3);  
      }     
    }  
    finally  
    {  
      if (br != null)  
        br.Close();  
    }  
  }  
}  

static void DumpFile(BinaryReader br, string fileName, uint start, uint len)  
{  
  BinaryWriter bw = null;  

  try  
  {  
    bw = new BinaryWriter(File.Open(fileName, FileMode.Create, FileAccess.Write,  
    FileShare.ReadWrite));  

    br.BaseStream.Seek(start, SeekOrigin.Begin);  

    var data = br.ReadBytes((int)len);  

    bw.Write(data);  
  }  
  finally  
  {  
    if (bw != null)  
      bw.Close();  
  }  
}   

Now for the D5100 we have two files, a640m010100.bin and b640101b.bin. The D7000 firmware is x75xxxx.BIN, the D3100 is x74xxxx.BIN, D300S is x81xxx.BIN and the D3S is xD3Sxxx.BIN.

All these systems are running the ‘Softune REALOS/FR is Realtime OS for FR Framily’, the Axxxx.BIN firmware is for the IO control CPU (metering, focus, buttons) and the larger Bxxxxx.BIN is the main processor firmware, with the main UI and processing (Fujitsu FR CPU).

Some great insight was found from the D70 Hack Project, which was the only remaining information I found. It’s in German, so thank you Google Translate. Also D7000 tear by by Chipworks was very inspiring.

Interested in more, come join the us at Nikon Hacker, or use the Online Patch Tool (Help)

Share

Comments:

Max 2011-11-20 12:22:42

Fantastic Work! Any idea of the same algorithm and/or same keys are used in other Nikon models?


arm.indy 2011-11-20 12:31:14

Hi

I/O controller CPU is certainly the Toshiba TMP19A CPU, like on Canon DSLR
See http://magiclantern.wikia.com/wiki/Tx19a
and http://www.chipworks.com/en/technical-competitive-analysis/resources/recent-teardowns/2011/01/teardown-of-the-nikon-d7000-dslr/
“Toshiba microcontroller with package markings TOSHIBA/TMP19A44FEXBG”

Indy


Simeon 2011-11-20 13:12:43

As noted this is how the D3S, D300S, D3100, D5100, D7000 firmwares are bundled.

The D700 uses are different format, that’s not encrypted, but the bundle file is packed differently.


Max 2011-11-20 13:24:35

Sorry, missed that. Any idea on how different the scheme is for the D90?


Simeon 2011-11-20 13:26:12

NP, the D90 has not had a update firmware released, so I have no clue.


Max 2011-11-20 13:33:09

Ahh, so your method requires two separately encrypted files with the same key… gotcha. I guess it wouldn’t hurt trying to decrypt the 1.00 d90 firmware with this keys for grins.


Simeon 2011-11-20 13:37:35

Well 80% of the decoding was done just from looking at a single D7000 firmware, but it was simpler in the end to using the many firmware files comparatively, to is the differences in alignment due to changes, to find common stuff at decoding boundaries.


Vicne 2011-11-26 09:25:23

Small question, Simeon : I don’t understand the end of your sentence “After that there’s a word ‘checksum’ and some padding bytes.” in your post above.
If I’m not mistaken, the header length is split into :

  • 0x20 fixed bytes (no idea what they mean, but they are constant across all models and versions),
  • then 0x10 bytes (4 int32s, that is, as you describe : number of files, header size, 0, 0)
  • then, for each file, a record of 0x20 bytes.
    So we have 0x20+0x10+2*0x20=0x70 bytes for the header, and then actual data begins, so there is no space left for a checksum or padding bytes…
    Is there something I’m missing ?

Simeon 2011-11-26 11:33:29

The package has three parts. The header. The contents. The footer. The footer has the checksum and padding. BUT the package checksum is not checked.

Each firmware file (referred to as A & B) has a checksum as the last two bytes of the file. Those are the checked checksums.


arm.indy 2011-11-26 13:24:02

$ python nikon_fir_dump.py D7000_0103_ext.bin

--- header ---  
0x0000: ? = 91873f9a04d225c0dc2abdbe4bb4e594ed1e372231432bcf4d8ef76be9e1fb45  
0x0020: nb_record = 2  
0x0024: 1st record offset = 0x70  
0x0030: --- record name | offset | length ---  
0x0030: a750010202.bin 0x00000070 0x000c0002 0 0  
0x0050: b750103a.bin 0x000c0072 0x00840000 0 0  
--- contents ---  
0x00000070: 3c1abfc0275a05000340000800000000 ...  
... ffffffffffff0102020011032418493a , length = 0xc0002  
0x000c0072: 9f8050000000c011150187109f8f6800 ...  
... ffffffffffffffffffffffffffff40c0 , length = 0x840000  
--- footer ---  
0x00900072: 608f0000000000000000000000000000  

arm.indy 2011-11-26 13:52:00

sorry, you must use a proportional font…


arm.indy 2011-11-26 13:54:52

did you finally found the checksum algorithm ? seems not remain bytes of md5, sha1, sha256, adler32 or crc32 …


arm.indy 2011-11-26 15:07:33

ok, found your update on the CRC16 checksum…


Vicne 2011-11-27 04:46:09

Oh, I see, you were talking about the end of the file while I was thinking “end of the header”. Thanks for clarifying.


arm.indy 2011-11-27 07:29:36

hi Simeon,

Thanks for the beautification. I still miss 2 things: I tried yesterday to compute the CRC for the package, using the same algorithm from offset 0x70 to offset of Footer (just before the chechsum). But it does not work. And why the CRC for firmware B of D5100 is wrong althougt it is OK for D7000 one ? You are producing 100% correct firmware (like us on Canon firmware, which uses AES 128 ;-) for your D5100, so you should have understood this point…
FYI, for Canon, package signatures (roughly HMAC-SHA1) were not checked… until 7D. They are now required.
Here are my results on D7000 and D5100

$ python nikon\_fir\_dump.py -c D7000\_0103\_ext.bin  
Dump decrypted Nikon firmwares v0.2  
based on work by http://simeonpilgrim.com  

--- header ---  
0x0000: ? = 91873f9a04d225c0dc2abdbe4bb4e594ed1e372231432bcf4d8ef76be9e1fb45  
0x0020: nb_record = 2  
0x0024: 1st record offset = 0x70  
0x0030: --- record name | offset | length ---  
0x0030: a750010202.bin 0x00000070 0x000c0002 0 0  
0x0050: b750103a.bin 0x000c0072 0x00840000 0 0  
--- contents ---  
0x00000070: 3c1abfc0275a05000340000800000000 ...  
... ffffffffffff0102020011032418 493a , length = 0xc0002, sum = 0x493a  
0x000c0072: 9f8050000000c011150187109f8f6800 ...  
... ffffffffffffffffffffffffffff 40c0 , length = 0x840000, sum = 0x40c0  
--- footer ---  
0x00900072: 608f 0000000000000000000000000000  
package sum=0x0  

and

$ python nikon_fir_dump.py -c ../d5100/D5100_0101_ext.bin  
Dump decrypted Nikon firmwares v0.2  
based on work by http://simeonpilgrim.com  

--- header ---  
0x0000: ? = 91873f9a04d225c0dc2abdbe4bb4e594ed1e372231432bcf4d8ef76be9e1fb45  
0x0020: nb_record = 2  
0x0024: 1st record offset = 0x70  
0x0030: --- record name | offset | length ---  
0x0030: a640m010100.bin 0x00000070 0x000c0002 0 0  
0x0050: b640101b.bin 0x000c0072 0x00a80000 0 0  
--- contents ---  
0x00000070: 3c1abfc0275a05000340000800000000 ...  
... ffffffffffff0101000011101116 a1c6 , length = 0xc0002, sum = 0xa1c6  
0x000c0072: 9f8050000000c01115019b0003e09f81 ...  
... ffffffffffffffffffffffffffff 7c09 , length = 0xa80000, sum = 0x30f2  
--- footer ---  
0x00b40072: 8a47 0000000000000000000000000000  
package sum=0x16d5  

I’ll publish my script when bugs will be fixed…


Simeon 2011-11-27 11:00:04

Your correct the 3rd order XOR table had three missing values, once there were set correct the D5100 & D3100 checksums are 100% same setup as D7000.

My bad.


arm.indy 2011-11-27 11:24:23

thanks…. one unique cause for 2 problems. and please where is the fixed version of Xor_Ord3_Eye[] or how to patch it ?
before using AES, Canon was using a similar XOR scheme with 2 tables of 512 and 513 bytes. See http://tech.groups.yahoo.com/group/canondigicamhacking/message/7883
Indy


arm.indy 2011-11-27 16:11:20

as I promised, scripts to decrypt and parse/extract/checksums are published here: http://chdk.setepontos.com/index.php?topic=7139.0
Tested on D5100, D7000, D3100 and D3S.
Thanks again Simeon for answering my questions!


Vicne 2011-11-29 17:58:19

OK. This time it’s for good : I think I have the params for the “bundle” checksum, and I think it is mandatory to compute it.
As I don’t want to spam Simeon’s blog more than it is already, I joined Max’s brand new nikonhacker forum and posted my thoughts here :
http://nikonhacker.com/viewtopic.php?f=2&t=8
Any comment is welcome - but on that thread please ;-)