Re-enabling JTAG and Debugging the WRT120N

After de-obfuscating the WRT120N’s firmware, I started taking a closer look at the code, which runs the now-defunct SuperTask! RTOS.

Thanks in no small part to copious debug strings littered throughout the code and some leaked Atheros datasheets, I made good progress in statically disassembling the code. The next step was to start debugging the system while exercising some of the router’s services.

The WRT120N does have a JTAG port (labeled J8), which appears to conform to the MIPS EJTAG standard header:

The WRT120N JTAG header

The WRT120N JTAG header

It didn’t work right out of the box though:

$ sudo openocd -f flyswatter2.cfg -f wrt120n.cfg 
Open On-Chip Debugger 0.7.0 (2014-01-05-12:41)
Licensed under GNU GPL v2
For bug reports, read
Info : only one transport option; autoselect 'jtag'
adapter speed: 6000 kHz
trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
trst_and_srst separate srst_nogate trst_push_pull srst_open_drain connect_assert_srst
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
Info : max TCK change to: 30000 kHz
Info : clock speed 6000 kHz
Error: JTAG scan chain interrogation failed: all ones
Error: Check JTAG interface, timings, target power, etc.
Error: Trying to use configured scan chain anyway...
Error: mips.cpu: IR capture error; saw 0x1f not 0x01
Warn : Bypassing JTAG setup events due to errors
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff

It turns out that JTAG has been disabled in hardware and in software on the WRT120N. Luckily both were relatively easy to fix.

Patching the Hardware

One of the simplest ways to disable JTAG is to remove jumpers, and that’s exactly what has been done here:

Missing R356 0-ohm jumper

Missing R356 0-ohm jumper

The TDI pin on the JTAG header (pin 2) has been disconnected from the rest of the system by simply removing jumper R356. This was easily remedied with a quick solder blob:

R356 solder blob

R356 solder blob

With TDI re-connected, I was able to reset the system and halt the processor over JTAG:

Open On-Chip Debugger
> reset init
JTAG tap: mips.cpu tap/device found: 0x00000001 (mfg: 0x000, part: 0x0000, ver: 0x0)
target state: halted
target halted in MIPS32 mode due to debug-request, pc: 0xbfc00000

Unfortunately, I quickly lost control of execution:

> resume
> halt
Failed to enter Debug Mode!
Halt timed out, wake up GDB.
timed out while waiting for target halted
in procedure 'halt'

After some probing, I found that this is due to the hardware design of the WRT120N: the reset button has been connected to the JTAG TDI pin on the AR7240 SoC. It is common for systems to allow JTAG pins to be re-configured as GPIO pins, so this is not un-heard of. However, this means that JTAG is likely being disabled in software.

Additionally, when depressed, the reset button asserts this pin low; the TDI line driven from my JTAG adapter also idles low. This means that whenever the JTAG adapter was connected to the JTAG header, the system thought that the reset button had been pressed.

I obviously didn’t want JTAG to be disabled, nor did I want the system continually resetting during a debug session. But, since I couldn’t magically redefine hardware pins, these were issues that had to be addressed in software.

Patching the Bootloader

The first firmware patch I needed to make was to the bootloader.

As seen previously, the bootloader checks the reset pin, and if asserted, it boots into a recovery image instead of booting the main image:

if(check_reset_button() != 0) goto load_main_image;

if(check_reset_button() != 0) goto load_main_image;

Since the JTAG adapter pulls the TDI line low, the bootloader wouldn’t even boot the main OS with the JTAG adapter connected; it thought the reset button had been pushed and loaded the recovery image instead!

There were two solutions to this problem. First, I could simply set a breakpoint on this conditional branch and change the register contents so that the recovery image is never loaded.

Besides being a PITA, this approach turned out to be impractical due to the following piece of earlier code:

Setting the RESET_SWITCH bit in the CPU_CLOCK_CONTROL register

Setting the RESET_SWITCH bit in the CPU_CLOCK_CONTROL register

This code is executed very early in the boot process and is in part responsible for configuring the system’s PLL clock. Specifically, the code snippet above sets bit 0 (the RESET_SWITCH bit) in the CPU_CLOCK_CONTROL register; according to the datasheet, this generates a CPU reset, causing the debugger to lose control of execution:

This register [CPU_CLOCK_CONTROL] controls the clock and reset to the CPU. These bits are controlled by driver software…RESET_SWITCH reset[s] during clock switch trigger.

What this means is that I would have to enter JTAG debug mode after the PLL was configured, but before the reset button was checked; a race condition that was difficult to reliably to win.

Instead, I opted to simply patch the bootloader on the flash chip. The check_reset_button function masks out bit 6 of the GPIO_IN register (aka, the TDI pin) by performing a logical AND with 0x40; if that pin is low, the function returns 0 (reset button depressed), else it returns non-zero:

The check_reset_button function

The check_reset_button function

I changed this from a logical AND to a logical OR, ensuring that check_reset_button always returns non-zero regardless of the actual state of the pin. This is just a one bit change to the instruction opcode:

The modified check_reset_button function

The modified check_reset_button function

Desoldering the flash chip and overwriting the bootloader with this patch got me past the bootloader and into the main OS:

> bp 0x800081ac 4 hw
breakpoint set at 0x800081ac
> resume
target state: halted
target halted in MIPS32 mode due to breakpoint, pc: 0x800081ac

However, JTAG debugging was killed shortly thereafter, and the serial console was spammed with “Reset button held…” messages:

Reset button held 0
Reset button held 1
Reset button held 2
Reset button held 3
Reset button held 4
Reset button held 5
Reset button held 6
Reset button held 7
Reset button held 8
Reset button held 9

Patching the OS

Something in the OS was disabling JTAG, and the culprit was found in the configure_peripherals function:



This code is responsible for setting the EJTAG_DISABLE bit inside the GPIO_FUNCTION_1 register. According to the datasheet, this will:

Disable EJTAG port functionality to enable GPIO functionality; can be set to 1 to enable using GPIO_5, GPIO_6, and GPIO_7 as GPIO pins

This was easily fixed by simply nopping out the ori instruction that was used to set the EJTAG_DISABLE bit:

EJTAG_DISABLE bit patched

EJTAG_DISABLE bit patched

For good measure, I also went ahead and nopped out the function call to gpio_tris that configures GPIO#6 (aka, TDI) as a GPIO input:

gpio_tris(GPIO6, INPUT);

gpio_tris(GPIO6, INPUT);

Call to gpio_tris nopped

Call to gpio_tris nopped

And got rid of that pesky reset button check that was spamming my serial console as well:

if(read_gpio_pin(6) != 0) goto reset_not_depressed;

if(read_gpio_pin(6) != 0) goto reset_not_depressed;

if(1 != 0) goto reset_not_depressed;

if(1 != 0) goto reset_not_depressed;

Re-building the Firmware Image

Having patched the OS, I needed to write it back to the flash chip. Not wanting to de-solder the flash chip yet again, I opted to apply the patches via a firmware update.

Since the OS image had been modified, I first needed to figure out the checksum for the firmware update file. It turns out that it is a standard CRC32 checksum that is stored in the firmware footer:

The crc32 function being called from cgi_upgrade

The crc32 function being called from cgi_upgrade

The checksum field itself is set to 0xFFFFFFFF at the time of calculation, and the checksum is calculated over the entire firmware update file, except for the board ID string at the very end.

So, putting everything together, I just needed to:

  • Re-compress the modified OS image
  • Concatenate the LZMA compressed web files and firmware footer with the compressed, modified OS image
  • Re-obfuscate the firmware image
  • Re-calculate and patch the checksum

I threw together a little script to automate this; it’s super hacky, but works so long as I don’t change the size of the decompressed OS image.

After buggering it up the first time, I recovered the system and flashed the modified firmware image through the router’s web interface. After a reboot, lo and behold, JTAG was up and running without issues:

> halt
target state: halted
target halted in MIPS32 mode due to debug-request, pc: 0x800045a4
> reg
===== mips32 registers
(0) zero (/32): 0x00000000
(1) at (/32): 0x804E0000
(2) v0 (/32): 0x00000000
(3) v1 (/32): 0x805BD6E4
(4) a0 (/32): 0x803D0000
(5) a1 (/32): 0x0000000A
(6) a2 (/32): 0x805BD57C
(7) a3 (/32): 0x806BD190
(8) t0 (/32): 0x80003F28
(9) t1 (/32): 0x00000000
(10) t2 (/32): 0x00000050
(11) t3 (/32): 0x807D7CA0
(12) t4 (/32): 0x00000109
(13) t5 (/32): 0x00000000
(14) t6 (/32): 0xEFFFFFFA
(15) t7 (/32): 0x00000001
(16) s0 (/32): 0x80A3E42C
(17) s1 (/32): 0x0000000A
(18) s2 (/32): 0x00000050
(19) s3 (/32): 0x80196100
(20) s4 (/32): 0xFEDFFE95
(21) s5 (/32): 0xB9DFDFDF
(22) s6 (/32): 0xFEFFDD37
(23) s7 (/32): 0xDEDFFBC9
(24) t8 (/32): 0x00000000
(25) t9 (/32): 0x3548A4A8
(26) k0 (/32): 0x0000FC03
(27) k1 (/32): 0xFFFFFFFE
(28) gp (/32): 0x804DA890
(29) sp (/32): 0x80820870
(30) fp (/32): 0x7EFFEFCF
(31) ra (/32): 0x80003FFC
(32) status (/32): 0x0000FC01
(33) lo (/32): 0x851EBB0A
(34) hi (/32): 0x0000031B
(35) badvaddr (/32): 0xACED8496
(36) cause (/32): 0x10004400
(37) pc (/32): 0x800045A4

I’ve placed a copy of the modified firmware image up on github, along with the script used to build it. Note that this will not patch the bootloader, although upgrading the bootloader via a firmware update does appear to be supported; I’ll leave that as an exercise to the reader. 😉

Bookmark the permalink.

19 Responses to Re-enabling JTAG and Debugging the WRT120N

  1. Vineel says:

    awesome….. work… keep it up.

  2. Timofey says:

    Hi Craig, what you are doing is awesome, indeed.
    I especially appreciate that you develop highly innovative open source software such as for example the reaver.
    Is there any way to make a donation to support your research? Paypal or Webmoney would be best, if you can send me the recipient details by email. Or I can buy any of your products, if you sell something. 🙂 Thank you!

  3. Pingback: Cracking Linksys “Encryption” - /dev/ttyS0

  4. Pingback: WRT120N fprintf Stack Overflow - /dev/ttyS0

  5. Pingback: [Перевод] Переполненяем стек в fprintf на Linksys WRT120N | Вести3.ру — Информационный журнал

  6. Pingback: [Перевод] Переполненяем стек в fprintf на Linksys WRT120N » CreativLabs

  7. Nony says:

    I’m trying to recreate what you did (I’m new to hardware hacking), and I’m curious as to what exactly you did when you said “Desoldering the flash chip and overwriting the bootloader with this patch got me past the bootloader and into the main OS”. Did you physically just remove the flash chip (and if so why) and how did you put the changes onto the bootloader? Also I’m assuming you reattached the flash chip?In the mean time I’ll be googling for more info but I figured it was worth a shot to ask.

    • Craig says:

      I used a hot air rework station to do the desoldering (highly recommend getting one of these if you’re doing a lot of desoldering; you can get them for $100 or less now).

      In some cases you don’t have to physically remove the chip, and can just attach to it in circuit with a test clip. In this case my programmer wasn’t playing nicely with the chip in circuit, which is why I physically removed it.

      The flash chip uses the Serial Peripheral Interface (SPI), so you can interface with it using variety of tools (microcontrollers, BusPirate, etc). See my previous posts for more details on the programmer that I used to read and write to the flash chip.

      And yes, I re-soldered the chip afterwards. The typical packages these types of flash chips come in (SOP-8 / SOIC-8) are very easy to solder, as their pins are not terribly close together.

  8. Pingback: Hacking the Linksys WRT120N Part 2 | Hack The Planet

  9. Pingback: Переполненяем стек в fprintf на Linksys WRT120N « Домик Миа

  10. Alex says:

    Hi Craig, thx for a cool write-up! I am trying to solve very similar problem, i have soho-router with MIPS micro controller and 14-pin JTAG port. As i’m experiencing kinda similar troubles your post was like a fresh air to me, but still I feel i don’t grasp it completely.
    1. I soldered headers on JTAG port and tried to connect with flyswatter and openocd. it didn’t work out of the box.
    2. I traversed paths from pins to micro controller. All pins are directly connected to the chip. Verified with multimeter.
    3. Hooked with logic analyser and noticed that there are signals on some pins of header and some of them correlate very well with LEDs blinking. So i think I have similar issue as you described. JTAG pins are used is GPIO.

    So i thought i gonna try what you mentioned: set a breakpoint and than trace boot loader to see where it turned off JTAG to use it as GPIO. what i don’t understand is how you managed to halt a processor on the very first instruction?
    target halted in MIPS32 mode due to debug-request, pc: 0xbfc00000

    i thought may be you powered up your board from flyswatter2 and it would have more precise control over when chip starts to execute. so i located a number of pins on the board signed as 3v3 and tried to connect flyswatter’s Vsupply and/or VREF to it. Neither of this seemed to work.

    is there a way to power the board from flyswatter2 at all?
    if not how have you managed to halt CPU at the very first instruction??

    I hope my writing got not very confusing 🙂 and i would really very much appreciate your help! if it’s not very clear fell free to ask or email me.

    Thanks a lot!!

  11. James Shaw says:

    Is there a way to remove the 2MB Flash and swap it out for a 8MB (or higher) chip?

    That way we can possibly run a better firmware, like dd-wrt or OpenWrt.

    • Anteros says:

      Hi, James Shaw,
      Sure it can.With the dump file and a simple s24/25 programmer,it can be running dd-wrt or Openwrt with 8MB and 16MB chip, and UBNT airos too.
      The only thing is we can only dump the ART from similar devices with AR9285 from TP-LINK,Netgear etc.
      No idea to copy the dump of The individually calibrated ART parttion.That’s why I posted in another thread…Need help from Craig.

  12. Anteros says:

    Another problem is after flashing 3rd party firmware, no idea how to make the use of the RF ap msc5511, maybe it’s GPIO activated.

    Also many models with AR7240+AR9285 is one stream only, one antenna.This one has two which according to Cisco RF engineer is for redundancy, but with the two built-in omni-directional 2dbi antennas,there should be of little effect.

  13. pant3k says:

    Hi Craig,
    I have ZyXEL P-320W and D-LINK DI-524 rev. B4. Both routers with RDC R2600 and both with same J7 jtag.
    I’ve checked voltage and everywhere I have 3.3V excluding TDO.
    On both routers i have 0.05V on TDO.
    On ZyXEl look’s like one jumper has been removed, but on D-LINK everything looks fine. After looking on traces.. looks like TDO goes to one of this two pins (picture below):
    One of this two pins have 3.3V(second one have less voltage).
    How to this with D-LINK? Just connect to this pin rather than TDO pin?

Leave a Reply

Your email address will not be published. Required fields are marked *