DI-604 Flash Reverse Engineering

Flash image with firmaware version 2.10 flash.bin

Encoding: little

Flash memory map:

Firmware Format

Info from Chris Windibank, 01/Mar/04

The firmware file is always 917,504 (0xe0000) bytes in length, presumably because it's a 1MB flash and the bootloader, backup firmware and settings area is 131,072 (0x20000) which leaves exacly that amount!

It is checksummed by the attached code. They decided to xor all bytes together and check it against a know value. I'm not really an expert in checksums so I can't say if this is unusual or not but it's certainly not a mainstream algorithm.

The first part of the file is the actual firmware in ARJ format using the -m1 compression mode and the archive consists of a single file named NML.MEM. The unused area after the firmware is zeroed and finally a signature and checksum is attached at the end. The signature is constant and the checksum can be calculated by xoring all the bytes in the file minus the checksum area and then xoring the result with 0xaabbbbaa.

The attached file can be used to generate a valid flash binary. I am a WindowsXP user and it was tested with Visual Studio 6.0 but it only contains C standard library calls so it should compile fine with GCC. It's usage is as follows:

mkflash outfile - where outfile is the name of the output file (I use di604_firmware_ucl.bin)

It expects to find a file name NML.ARJ in the current directory (the first version took and input file but I got tired of typing it). I have been renaming zImage NML.MEM and then using the following to make the archive for mkflash.

arj32 a -m1 NML.ARJ NML.MEM

I'm not sure if the firmware checks to see if the arjed file in the archive is actually name NML.MEM but that is the way DLink does it so I did the same.

The bootloader expands the archived firmware sitting at 0x20000 in flash ROM to 0x20000000 in RAM. It then flips the RAM/ROM address spaces and jumps to address 0 which is the begginning of RAM after the flip.

At this point the router will start to execute zImage which is a compressed kernal and ramdisk. It should decompress the kernal and ramdisk and then jump to the entry point of the decompressed kernal. I chose to go with the compressed kernal because it's an easy way to get the ramdisk into memory from a single arjed archive. It may be better to try something different later when the kernal is actually working!

Bootloader decoding

Address Description Calls Called by
0x0000 Exceptions vectors.
Reset/IRQ -
0x0048 - 0x007c Switch between 0 address from Flash mem to SDRAM.
- 0x2E0
0x0080 - 0x0254 Initialisation:
  • SDRAM memory controller
  • Flash memory contorller
  • GPIO

  • Pass contorol to 0x500
    0x500 0x0000
    0x0268 - 0x0268 Endless loop - 0x324
    0x026C - 0x028C read input value of the GPIO[6] (CONFIG RESET TO FACTORY). return 1 if input 0;
    - 0x324
    0x0290 - 0x02DC XOR starting from start_addr(r0) for bytes(r1) length
    Return 0 if ==0xaabbbbaa
    else return -1
    - 0x324
    0x02E0 - 0x320 memcpy(src,dst,len) - 0x324
    0x0324 - 0x03D0
    check SRC of the main firware (0x2000)
    check reset buttom
    
    load uncompression code to SDRAM
          if RESET load from      0x03000
          if no RESET load from   0x20000
    if uncompression return 1 - then switch memory and start from 0
    	   
    - 0x0874
    0x03D4 - 0x042c UART - 0x46C
    0x0430 - 0x043c Read UART0 flag register for "receive FIFO full" - 0x46C
    0x0440 - 0x0468 - - 0x46C
    0x046c - 0x04dc no direct call - call itself ??
    0x04E0 - 0x04fc no direct call!! - -
    0x0500 - 0x0870 Calculates relocated addreses
    Clean mem region 0x20380000(0x1477B bytes)
    memcpy(0x11a9,0x1145,0x24) copy to flash????
    call 0x0874
    indirect 0x0874 0x80
    0x0874 - 0x09c0 do nothing;
    call 0x324;
    call 0xA08;
    0x09c4 0x0e20 0x0db0 0x0324 0x0a08 indirect call
    0x0500
    0x09C4 - 0x09E0
    void  f_0x9c4()
    {
       f_0xebc(16,-1);
       return;
    }	   
    - 0x0874
    0x09E4 - 0x0A04 - - -
    0x0A08 - 0x0AC8 - - -
    0xacc - 0xae4 - - no direct call
    0xae8 - 0xd24 memcpy(src,dst,len) - indirect call
    0x500
    0xd28 - 0xdac memset(addres, char , len) - set memory region with a specific byte - indirect call
    0x500
    0x0db0 - return 20383884; - indirect call
    0xEDC
    0x0ebc - 0x0ed8
    *20383884 = r0;
    return -1;
    - 0x09c4
    0x0edc - 0x0f20
     if(!f_0x00db0)
           return 20383964;
     else
           return f_0x0db0(); // 20383884
    	   
    - 0x0f24
    0x0f24 - 0x0f44
    void f_0x0f24(word val){
    {
      word *addr;
      addr = f_0x0edc();
      if(addr!=0)
           *addr = val;
       return;
    }
    	  
    - 0x0ebc
    0x0f48 - - - -
    0x1134 indirect function call (call addr in IP register IP defined address multiple
    0x1138 function to do nothing???? OR first parameter to itself and exit. What the big deal?? - 0x0ebc
    - - - -
    0x29C4 - 0x2F9C last function - -

    indirectly called functions 0x4e0
    0xacc - 0xae4
    0xae8 - 0xd24
    0xd28 - 0xdac


    $Id: flash_mem.html,v 1.5 2004/03/14 11:07:16 bcabral Exp $