After my previous adventures with firmware unpacking, I decided to take a closer look at the DCS-2130 IP camera that I have. I looked for a way to extend the camera’s firmware to allow streaming in .flv format. I don’t know if I can accomplish this and have time for it, but as a first step, I wanted to build my own firmware, as D-Link thankfully offered GPL-sources for it.
However these sources are hard to get as download nearly always aborts after approx. 500 MB. Thankfully, this fella was so kind to mirror the firmware on his FTP, so that I was able to download it.
After having a closer look at the sources, I saw that they were nearly complete, so one should be able to modify and extend the firmware, as it really compiles to a full image. The NIPCA-Streaming services and other things are directly built into the boa HTTP server (look at DCS-2130/apps/public/boa-0.94.13/src).
When I tried to compile the firmware, I noticed that the mtdutils supplied didn’t compile because of some missing acl.h header file. I fixed the problem by editing the Makefile and adding -DWITHOUT_XATTR to the CPPFLAGS.
Now I was able to complie the firmware, but I noticed, that it just shipped with a firmware upload page, not with the real D-Link webinterface. This isn’t really dramatic, as all NIPCA-Services are directly built into the boa HTTP server, but what if you want to use the original webinterface as a starting point for your extensions?
So I had a look at their original firmware file, as I thought that I may be able to extract it from there. Unfortunately it was crypted (I don’t know why they are doing this…)!
But as the GPL firmware toolkit was able to build such a crypted image, I examined the host_mkfm tool from the firmware/ subdirectory, which creates the firmware images. From the firmware.conf file in this directory, I learned, that the firmware image consists of multiple sections with a certain section type (AUTO, JFFS2, YAFFS or NONE).
I disassembled the host_mkfm utility and checked the routines for creating the firmware image. It seems that they are using a vernam-chiffre with one-time-pads hardcoded into the file for encryption. The encryption works on WORDs (=2 bytes). They have 3 tables of variable length that they are iterating through and XORing the contents of each of them. This value then gets XORed with a special scramble-value (random seed) which can differ between the firmware files. It can be passed to the utility via the -s parameter. There is another parameter, the so-called machine_code which can also be passed to the utility via -m. Per default, both the values are 0×2021.
After XORing it with this machine code, it’s finally XORed with the respective WORD in the firmware to be scrambled.
A firmware has a 16byte header, which consists of a 4byte Signature (also seems to differ for various firmware versions, for this firmware, it’s 0xAA7EC55B), followed by 4 bytes that specify the size. Then there are 2 bytes for a checksum, 2 bytes for the scramble value mentioned above, some unknown 2 byte value and finally 2 bytes for the machine_code.
As the header contains the encryption parameters scramble and machine_code, it’s encoded differently: It’s just XORed with the word that is at the machine_code position in the header. If there is a 0 at this position, it’s XORed with the default 0×2021. After decrypting the header, you can decode the rest of the file using the algorithm I described.
After the file is decrypted, you can parse its contents. As mentioned above, it is divided into multiple sections. The file starts with a section list where each entry has a total of 64 bytes. Each section header starts with a 0xA55A signature. The next byte is the mtd specified in the firmware.conf file. The next byte is specifying the section type (see my list if section types above). The next 4 bytes specify the size of the section. The next 4 bytes specify the offset mentioned in the firmware.conf in 16k blocks. The rest of the section header is probably unused and usually padded with nulls.
Armed with this knowledge I gathered from reverse-engineering the utility, I was able to write my own unpacker for their firmware: decode_fw.c
The next challenge was to be able to mount the JFFS2 file system image. I found a shellscript on the maemo-page which did the job (but only the kernel-memory version works, the block device version results in garbled images). I modified the script a bit and it works: mount_jffs2.sh
Create an fs-Subdirectory and mount the image with
Be sure to unmount the image correctly with
./mount_jffs2.sh <Imagename> unmount
after you are done, don’t just use umount!
So now I was able to extract the original D-Link webinterface. The firmware installer usually copies the stuff from apps/fs/www.7315 to firmware/www, then packs it into www.jffs2 and the firmware packager host_mkfm then creates a section for it in the firmware image according the the firmware.conf file. Therefore the contents of the mounted jffs2-image need to be put into apps/fs/www.7315
Unfortunately the Makefile doesn’t do a recursive copy and the original image contains a subdirectory, so I also needed to modify the cp to cp -r in the www.7315: section of the ~/gpl/DCS-2130/apps/fs/Makefile
Now that I fixed that, I was finally able to build my own firmware image using the original D-Link webinterface. But I didn’t dare to flash it into my device yet, so use it at your own risk!
I wrote a little shellscript that should do most of the work described here, to make it easier for you: build_2130.sh.txt
After this journey into firmware modding, I examined a lot of D-Link and TRENDnet firmwares and made unpacker scripts for them. It’s interesting the TRENDnet and D-Link cameras are mostly based on the same development systems, so the table may also be an interesting comparison between the models of the 2 manufacturers.
I published the table and my unpacker scripts at http://dose.0wnz.at/ipcams/
I’d love to hear from your unpacking experiences in the comments section. Have fun!
3 comments | Add One