Thursday, December 13, 2012

WDTV Live -- Firmware Hacking Series -- Part 3

Patching custom rootfs onto existing firmware


Once you have the serial cable built as mentioned in my previous post, it is possible to patch the current firmware installation with custom rootfs, without having to upgrade/reinstall the whole firmware with custom one.

These are the high-level steps that we need to do:
  1. Discover the partition table and identify where the rootfs is stored in the flash storage.
  2. Read the rootfs of the current firmware and push it to a different machine.
  3. Modify the rootfs to your needs, on the other machine.
  4. Upload it back to WDTV and write the flash partition appropriately.

Discovering the rootfs partition

~ # mount
rootfs on / type rootfs (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
/dev/sigmblockh on / type cramfs (ro)
none on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
usb on /proc/bus/usb type usbfs (rw)
mdev on /dev type tmpfs (rw)
devpts on /dev/pts type devpts (rw)
none on /tmp type tmpfs (rw)
/dev/loop0 on /tmp/static_config type minix (rw)

Note the highlighted line. /dev/sigmblockh device is the one mounted as / partition. To confirm print the first 100 bytes on the device and you could notice the CRAMFS header.

~ # hexdump -c -n 100 /dev/sigmblockh

0000000   E   = 315   (  \0   @ 021 003 003  \0  \0  \0  \0  \0  \0  \0
0000010   C   o   m   p   r   e   s   s   e   d       R   O   M   F   S
0000020 234 333 016 324  \0  \0  \0  \0   k   &  \0  \0 313 016  \0  \0
0000030   C   o   m   p   r   e   s   s   e   d  \0  \0  \0  \0  \0  \0
0000040 355   A 354 003   D 001  \0   d 300 004  \0  \0 355   A 354 003
0000050 354  \v  \0   d 001 031  \0  \0   b   i   n  \0 377 241 354 003
0000060  \t  \0  \0   d                                                
0000064
~ # 
We still need to know the exact flash disk and the partition to which this device points to.

~ # ls /dev/sigm* -l
brw-rw----    1 root     root     254,   0 Jan  1  2000 /dev/sigmblocka
brw-rw----    1 root     root     254,   1 Jan  1  2000 /dev/sigmblockb
brw-rw----    1 root     root     254,   2 Jan  1  2000 /dev/sigmblockc
brw-rw----    1 root     root     254,   3 Jan  1  2000 /dev/sigmblockd
brw-rw----    1 root     root     254,   4 Jan  1  2000 /dev/sigmblocke
brw-rw----    1 root     root     254,   5 Jan  1  2000 /dev/sigmblockf
brw-rw----    1 root     root     254,   6 Jan  1  2000 /dev/sigmblockg
brw-rw----    1 root     root     254,   7 Jan  1  2000 /dev/sigmblockh
brw-rw----    1 root     root     254,   8 Jan  1  2000 /dev/sigmblocki
brw-rw----    1 root     root     254,   9 Jan  1  2000 /dev/sigmblockj
brw-rw----    1 root     root     254,  10 Jan  1  2000 /dev/sigmblockk
brw-rw----    1 root     root     254,  11 Jan  1  2000 /dev/sigmblockl

The highlighted line shows the major, minor numbers for this device. As you might know, the minor number is the partition number on the disk. In this case, the rootfs is stored in the 7th partition.

To understand where is the 7th partition stored in the flash disk, we need to know the partition information of the internal flash disk. This could be obtained in two different ways. Either by looking at the logs during WDTV boot (via serial port) or by issuing the following command at the terminal (via serial again).

~ # cat /proc/sigminfo 

dev:       size     offset   name        CS
sigmblk0:  0ff80000 00000000 "CS0-Device" 0
sigmblk1:  00080000 00000000 "CS0-Part1"  0
sigmblk2:  00040000 00080000 "CS0-Part2"  0
sigmblk3:  00300000 000c0000 "CS0-Part3"  0
sigmblk4:  00300000 003c0000 "CS0-Part4"  0
sigmblk5:  01000000 006c0000 "CS0-Part5"  0
sigmblk6:  00800000 016c0000 "CS0-Part6"  0
sigmblk7:  05a00000 01ec0000 "CS0-Part7"  0
sigmblk8:  05a00000 078c0000 "CS0-Part8"  0
sigmblk9:  00020000 0d2c0000 "CS0-Part9"  0
sigmblk10: 00020000 0d2e0000 "CS0-Part10" 0
sigmblk11: 00020000 0d300000 "CS0-Part11" 0

We are almost there now. This shows the starting offset (0x01ec0000) and the size (0x05a00000) of the partition. That is a 90MB partition reserved for rootfs ie., the compressed cramfs partition cannot exceed 90MB.

Read rootfs and export to different machine


This is required as we need to patch the rootfs of the installed firmware. Press 0 (via the serial port) and restart WDTV Live. This will prevent loading the firmware and will stop boot at booloader YAMON. 

Follow my inline comments in the script below.
[Gerald] Plugin an Ethernet cable connected to a valid network. 
[Gerald] net init, initializes the interface and runs DHCP client.
YAMON> net init

Ethernet driver for SMP86XX (v1.0)
(MAC 00:90:xx:xx:xx:xx)
em86xx_eth0 - full-duplex mode
em86xx_eth0 - 100 Mbit/s
em86xx_eth0 ethernet start
DHCP was successfully configured.
ipaddr:     192.168.8.104
subnetmask: 255.255.255.0
gateway:    192.168.8.2 
[Gerald] 'nflash read' command is used to read from the flash memory.
[Gerald] Based on the discovery earlier, the following command reads
[Gerald] first 0x1e00000 bytes into memory at 0x8400000.
[Gerald] syntax: nflash read flash_address memory_address size chip_select
[Gerald] chip_select (CS) is available in the partition info at /proc/sigminfo.
[Gerald] Note: Use only 0x84000000 as target. Other addresses didn't let me read
[Gerald] 0x1e00000 bytes at once.Even here, I couldn't read the complete 0x5a00000 
[Gerald] bytes at once.
YAMON> nflash read 0x01ec0000 0x84000000 0x1e00000 0 

[Gerald] Verify if this has indeed read the CRAMFS from flash. 
YAMON> dump 0x84000000 100

84000000: 45 3D CD 28 00 40 11 03 03 00 00 00 00 00 00 00  E=�(.@..........
84000010: 43 6F 6D 70 72 65 73 73 65 64 20 52 4F 4D 46 53  Compressed.ROMFS
84000020: 9C DB 0E D4 00 00 00 00 6B 26 00 00 CB 0E 00 00  .�.�....k&..�...
84000030: 43 6F 6D 70 72 65 73 73 65 64 00 00 00 00 00 00  Compressed......
84000040: ED 41 EC 03 44 01 00 64 C0 04 00 00 ED 41 EC 03  .A..D..d�....A..
84000050: EC 0B 00 64 01 19 00 00 62 69 6E 00 FF A1 EC 03  ...d....bin..�..
84000060: 09 00 00 64                                      ...d 

[Gerald] upload the file to a tftp server of your choice.
[Gerald] syntax: fwrite tftp_path memory_address size 
YAMON> fwrite tftp://192.168.8.101/sigh/sigh.dump.1 0x84000000 0x1e00000

About to binary write tftp://192.168.8.101/sigh/sigh.dump.1

Successfully transferred 0x1e00000 (10'31457280) bytes 

[Gerald] Read the next 0x1e00000 bytes from flash and upload to TFTP
YAMON> nflash read 0x03cc0000 0x84000000 0x1e00000 0

YAMON> fwrite tftp://192.168.8.101/sigh/sigh.dump.2

About to binary write tftp://192.168.8.101/sigh/sigh.dump.2

Successfully transferred 0x1e00000 (10'31457280) bytes

[Gerald] Read the next 0x1e00000 bytes from flash and upload to TFTP 
YAMON> nflash read 0x05ac0000 0x84000000 0x1e00000 0

YAMON> fwrite tftp://192.168.8.101/sigh/sigh.dump.3

About to binary write tftp://192.168.8.101/sigh/sigh.dump.3

Successfully transferred 0x1e00000 (10'31457280) bytes

YAMON> 

Modify the rootfs to your needs


At this point, we have three 30MB files and concatenating them in order, should give a complete cramfs partition. You could now extract the cramfs partition (using modified cramfsck, as mentioned in my earlier post) and make the necessary modification.

I have the following section of script added to the /init script, that gets control to my USB script as root. This lets me run scripts on startup and modify them as required, without having to reflash anything. Don't forget to update the /md5sums.txt file with your updated md5sum.
# Gerald - update - start
if [ -f /tmp/media/usb/wdtv/gerald_init ]; then
    /tmp/media/usb/wdtv/gerald_init &
# Gerald - update - end 
Use the modified mkcramfs (as mentioned in my earlier post) to repack the rootfs into a cramfs filesystem file. Split the file into three 30MB files (note: you may need to append zeros at the end to fill up to 30MB in the third part -- your cramfs filesystem is likely to be < 90MB).

Upload back to WDTV and write the flash partition appropriately


Boot into YAMON. Use 'fread' to read from tftp to memory at 0x84000000. Use 'nflash write' to write the parts into their correct flash locations starting at 0x01ec00000. Once all 3 partitions are written, just reboot. Your new rootfs should be active.

By now, you would no longer be worried about what to do if that doesn't boot. Good luck!

14 comments:

  1. I'm very impressed with what you've managed to achieve so far & look forward to future updates on your blog!

    The ultimate would be XBMC running on this awesome little box!

    ReplyDelete
  2. Thanks.

    XBMC requires some GPU support, which the WDTV Live hardware doesn't have. Had tried it already.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I have bricked my WD Unit. I have access only to YAMON. I tried to repair it, but It goes from bad to worst. Reading your post I think about the option to write the memory. What I want to do is what you said:
    - "Use 'fread' to read from tftp to memory at 0x84000000. Use 'nflash write' to write the parts into their correct flash locations starting at 0x01ec00000"
    But for that, I need the original dump files.
    Can you please extract the bin files of your device and upload them to the web? I think that can save my and other's people device.
    Thank you in advance!!

    ReplyDelete
    Replies
    1. You could extract rootfs out of any standard WDTV Live image. Search for "Unpack wdtv firmware". Not sure, if I can distribute such image files.

      Delete
    2. At the end I find those files here

      http://masu.6f.sk/index.php/Wdtv_kernel#Booting

      I success in unbrickin my device.

      THANKS

      Delete
  5. Regarding: XBMC requires some GPU support, which the WDTV Live hardware doesn't have. Had tried it already.

    I read about lack of OpenGL2.0 support literally moments after I posted this, it seems others had hopes too.

    Even so, your findings open up possibilities for implementing a newer Linux Kernel, Bluetooth, DVB-T tuner support etc..


    SF

    ReplyDelete
  6. I've been trying to figure out exactly what the 16 byte signature is. Could you please explain where the signature is in the hexdump table?
    Thanks

    ReplyDelete
    Replies
    1. The signature is at the end of the cramfs image in the packed firmware. Apparently these are used for verification only if you install through the built-in installer. For flashing the rootfs directly, you only need to flash the cramfs (excluding header, signature); no need to worry about the signature. If you look at the hexdump, the flash partition for rootfs does not have the md5sum either at the header.

      Delete
    2. Basically, I just need to know how to extract the last 16 bytes from the cramfs image. I'm reading about how to do this but have not yet figured it out. I'm using ubuntu. Any help would be greatly appreciated.
      Thanks

      Delete
  7. I have a WDTV Live Plus. I believe the signature is different from the WDTV Live??

    ReplyDelete
    Replies
    1. I have WDTV Live only. I have no idea about the signature on WDTV Live Plus.

      Delete
  8. Very interesting articles. I have one of these devices and wanted to use it to run Debian for MIPS development. I looked at the forum.wdlxtv.com firmwares and found what I wanted here http://forum.wdlxtv.com/viewtopic.php?f=43&t=494.

    Basically it is a Debian root that you can boot from a USB. The problem is it requires you flash the hacked wdlxtv firmware.

    What I'd like is to just change the boot loader, or a similar small change to the firmware, that allows me to boot from USB, and leave the the rest of the system stock. That way I can use if for development and still run it as a media box.

    Do you have any ideas if this is possible for the studies you have done? BTW, very impressive work :-)

    Bill

    ReplyDelete
  9. respected sir,
    i'm s.shivasurya , 2nd year IT , Thiagarajar College Of Engineering,
    i would like to chat with you about Networking and Applications .. you have recently attended as chief guest at TCE! and got your blog address!

    ReplyDelete