čtvrtek 2. října 2014

TP-Link recovery

Flashing routers with custom firmware if fun. Especially a firmware compiled by yourself. But it may occur that things goes wrong and you will brick you router. If you have untouched a bootloader, unbricking process is really easy.

This article is about unbricking routers with working bootloader, in this case a TP-Link TL-WR703N which is using U-Boot. If your router has other bootloader, following procedure probably will not work.

For recovery you must have access to device serial console. I'm using a Debian machine.

Get information about your memory

To safely brick your device, you have to perform some terrain reconnaissance. You have to know how flash memory of your router looks like and which blocks can be rewritten without causing serious damage to device functionality.

Flash memory is divided into parts like hard drive is partitioned in traditional computers. But in this case, we are not dealing with hard drive and real partitions, but with mtd and named memory offsets instead. That means that these “partitions” doesn't contains any meta data about them, can overlap each other and also writing operations can overrun their boundaries.

Let's look how many partitions are there and how long they are.

root@OpenWrt:/# cat /proc/mtd 
dev:    size   erasesize  name 
mtd0: 00020000 00010000 "u-boot" 
mtd1: 00102050 00010000 "kernel" 
mtd2: 002cdfb0 00010000 "rootfs" 
mtd3: 000c0000 00010000 "rootfs_data" 
mtd4: 00010000 00010000 "art" 
mtd5: 003d0000 00010000 "firmware"

Ok, there are six partitions. Surely at the zero offset starts “u-boot” partition, because this one contains bootloader, which is executed at first, when device is turned on. With the rest we can only guess for now, where they are placed.

You can get more complete view, how they are arranged by checking boot log.

root@OpenWrt:/# dmesg

This command spits out lot of output, but we are looking for something like this.

[    0.490000] 5 tp-link partitions found on MTD device spi0.0 
[    0.490000] Creating 5 MTD partitions on "spi0.0":
[    0.500000] 0x000000000000-0x000000020000 : "u-boot" 
[    0.510000] 0x000000020000-0x000000122050 : "kernel" 
[    0.520000] 0x000000122050-0x0000003f0000 : "rootfs" 
[    0.550000] 0x000000330000-0x0000003f0000 : "rootfs_data" 
[    0.560000] 0x0000003f0000-0x000000400000 : "art" 
[    0.560000] 0x000000020000-0x0000003f0000 : "firmware" 

Nice, now we have exact position of each individual region, so we can set up complete view.

Regions which have to be untouched are u-boot and art. There contains critical data to keep device working. Firmware partition between them can be rewritten as you like.

Make a brick

First step is bricking a device. You probably will not do that intentionally but it can happen. For example by flashing a file, which you shouldn't.

root@OpenWrt:/# cd /tmp
root@OpenWrt:/tmp# wget http://192.168.1.1/openwrt-ar71xx-generic-root.squashfs
root@OpenWrt:/tmp# mtd -r write openwrt-ar71xx-generic-root.squashfs firmware

Yes, this is how I bricked my TP-Link. I build image which was too large and therefore factory.bin file doesn't appear in my bin directory. I didn't realized that problem, so I tried another file and it of course didn't work.

openwrt-ar71xx-generic-root.squashfs is only file with filesystem. It is really not good idea to flash it at the firmware offset. If you want to browse its content, you can install squashfs-tools package and extract it.

buben@debian:~$ unsquashfs openwrt-ar71xx-generic-root.squashfs
buben@debian:~$ ls squashfs-root/ 
bin  dev  etc  lib  mnt  overlay  proc  rom  root  sbin  sys  tmp  usr  var  www

Issued command mtd above rewrites only mtd5 partition, where firmware is located, so mtd0 with bootloader leave untouched and fully working.

There is one big catch. If you write bigger file than 3904 KiB, you will rewrite also mtd4. In this location it stored calibration data for your wireless hardware and it is device specific. If you don't have backup of art for restore, your router can't use WiFi anymore.

Principe

In normal conditions, when device is turned on, first what is executed is a bootloader. Bootloader perform some hardware check, known as POST, copies kernel into RAM and executes it. When firmware is broken, router restart itself and these things are repeated in infinite loop.

Some bootloaders, such as U-Boot, have implemented rescue mechanisms, for example loading new firmware using TFTP.

Install TFTP server

For installing TFTP server in Debian machine, issue following command.

buben@debian:~$ sudo apt-get install tftpd-hpa

For security reasons, I recommend you to disable automatic starting of the server during the boot time. It is aways good idea to use some service only when you need it and not keep it running for all the time.

buben@debian:~$ sudo update-rc.d -f tftpd-hpa remove

Now all init scripts for tftpd-hpa are removed, but service is still running. You can test it by following command

buben@debian:~$ sudo service tftpd-hpa status 
[ ok ] in.tftpd is running.

Serial console

As I mentioned at the beginning, you need working serial console. My favorite program is minicom.

buben@debian:~$ sudo apt-get install minicom

To use it as a regular user, you have to be in dialout group.

buben@debian:~$ sudo usermod -a -G dialout buben

After that command you have to relog your account to take the effect.

After that you can connect to device.

buben@debian:~$ minicom -D /dev/ttyUSB0 -b 115200

On my bricked router, I have got the following output repeating over and over.

U-Boot 1.1.4 (Mar 18 2013 - 17:36:20) 

AP121 (ar9330) U-boot 

DRAM:  32 MB 
led turning on for 1s... 
id read 0x100000ff 
flash size 4194304, sector count = 64 
Flash:  4 MB 
Using default environment 

In:    serial 
Out:   serial 
Err:   serial 
Net:   ag7240_enet_initialize... 
No valid address in Flash. Using fixed address 
No valid address in Flash. Using fixed address 
: cfg1 0x5 cfg2 0x7114 
eth0: 00:03:7f:09:0b:ad 
ag7240_phy_setup 
eth0 up 
: cfg1 0xf cfg2 0x7214 
eth1: 00:03:7f:09:0b:ad 
athrs26_reg_init_lan 
ATHRS26: resetting s26 
ATHRS26: s26 reset done 
ag7240_phy_setup 
eth1 up 
eth0, eth1 
Autobooting in 1 seconds 
## Booting image at 9f020000 ... 
   Uncompressing Kernel Image ... Too big uncompressed streamLZMA ERROR 1 - must RESET

Uploading new firmware

At first, copy your working firmware file into tftpd directory.

buben@debian:~$ sudo cp openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin /srv/tftp/firmware

Link your computer with the router using ethernet cable and turn it on. U-Boot can be configured for another remote address where it expects TFTP server, default address is 192.168.1.100.

buben@debian:~$ sudo ifconfig eth0 192.168.1.100

Power on your router and type “tpl” into serial console during boot. You should get rescue hornet prompt.

hornet>
hornet> help 
?       - alias for 'help' 
bootm   - boot application image from memory 
cp      - memory copy 
erase   - erase FLASH memory 
help    - print online help 
md      - memory display 
mm      - memory modify (auto-incrementing) 
mtest   - simple RAM test 
mw      - memory write (fill) 
nm      - memory modify (constant address) 
printenv- print environment variables 
progmac - Set ethernet MAC addresses 
reset   - Perform RESET of the CPU 
setenv  - set environment variables 
tftpboot- boot image via network using TFTP protocol 
version - print monitor version

Now we are ready to upload new image into the device.

Following command contains memory offsets which I leave unexplained for now. These are basically addresses into memory and they are platform dependent (TL-WR703N uses MIPS architecture processor).

Download image file from remote server.

hornet> tftpboot 0x81000000 firmware 
dup 1 speed 1000 
Using eth1 device 
TFTP from server 192.168.1.100; our IP address is 192.168.1.111 
Filename 'firmware'. 
Load address: 0x81000000 
Loading: ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ################################################################# 
         ###################################################### 
done 
Bytes transferred = 3932160 (3c0000 hex)

Then erase memory where firmware will be stored.

hornet> erase 0x9f020000 +0x3c0000 

First 0x2 last 0x3d sector size 0x10000 
  61 
Erased 60 sectors

Copy firmware into cleaned memory. Notice “cp.b” command. Cp takes three arguments, first is source memory offset, second is destination offset and third one is count of units. By default, cp takes one unit of long word (32 bits), cp.b is a byte version of copy. Tftpboot command above tells us, that 3932160 bytes were transferred (3c0000 in hex), so let's copy them into flash memory.

hornet> cp.b 0x81000000 0x9f020000 0x3c0000 
Copy to Flash... write addr: 9f020000 
done

Now you can restart the router or boot system by following command.

hornet> bootm 9f020000

Now your router should work again.

At the end, remember to stop TFTP server.

buben@debian:~$ sudo service tftpd-hpa stop