Hardware Hacking: Dumping a W25N01GVZEIR NAND Flash
Introduction
One of my recent endeavours started when a good friend brought me an ISPs router model ZTE H268N V1.1. I opened it up and came across a Winbond W25N01GVZEIR NAND Flash WSON chip [1]. This was the first 1GBit/128MB flash chip that I have ever encountered. I was considering several avenues on how to dump its contents:
- Desolder the chip and use a suitable adapter
- Solder small cables on its slightly exposed pins, which is dangerous because it may damage the chip
- Search for a debug interface like UART or JTAG
Since there were no debug interfaces except for UART (which was useless since it booted straight to the linux OS and didn't allow me to easily interrupt the boot process) and I didn't have a hot-air gun and the adapter needed, I decided to solder some wires on its pins and try using flashrom.
I connected my Bus Pirate to the appropriate pins and tried using flashrom, but then I realized that my chip wasn't supported by flashrom. I tried adding support[2] for this chip by modifying flashrom's source code but still without any success.
At last, I decided to do some bit-banging by reading the datasheet and using the SPI interface offered by a Raspberry Pi. Both the chip and the Raspberry PI operated in the same voltage so there was no need to use any logic level translators. I ended up writing the following script that dumped the contents of the flash.
import spidev import time def main(): spi = spidev.SpiDev() spi.open(0,0) spi.mode = 3 spi.max_speed_hz = 1000000 spi.xfer([0xff]) wait(spi) for i in range(0, 65535): print("Page " + repr(i)) msb = (i >> 8) & 0xff lsb = i & 0xff # Load page into the data buffer spi.xfer([0x13, 0x00, msb, lsb]) wait(spi) # Read page from the data buffer data = spi.xfer3([0x03, 0x00, 0x00, 0x00] + [0x00]*2112) # Ignore the ECC (Error Correction Code) 64 bytes (2112 - 2052) with open("firmware.bin", "ab") as fp: fp.write(bytes(data[4:2052])) spi.close() def wait(spi): while not(check_status(spi) == 0): time.sleep(0.1) def check_status(spi): status = spi.xfer2([0x0f, 0xc0] + [0x00]) if status[2] == 0x01: return 1 else: return 0 if __name__ == '__main__': main()
This is a picture of the target
Figure 1: The target device (ZTE H268N V1.1)
Binwalk seems to give me the right results
$ binwalk ./firmware.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 131100 0x2001C CFE boot loader 365096 0x59228 Copyright string: "Copyright (C) 2000-2013 Broadcom Corporation." 366308 0x596E4 HTML document header 366443 0x5976B HTML document footer 387856 0x5EB10 CRC32 polynomial table, big endian 392720 0x5FE10 HTML document header 394229 0x603F5 HTML document footer 394244 0x60404 HTML document header 395670 0x60996 HTML document footer 1179676 0x12001C CFE boot loader 1413672 0x159228 Copyright string: "Copyright (C) 2000-2013 Broadcom Corporation." 1414884 0x1596E4 HTML document header 1415019 0x15976B HTML document footer 1436432 0x15EB10 CRC32 polynomial table, big endian 1441296 0x15FE10 HTML document header 1442805 0x1603F5 HTML document footer 1442820 0x160404 HTML document header 1444246 0x160996 HTML document footer 4587520 0x460000 JFFS2 filesystem, big endian 15728684 0xF0002C LZMA compressed data, properties: 0x6D, dictionary size: 4194304 bytes, uncompressed size: 5113360 bytes 37748780 0x240002C LZMA compressed data, properties: 0x6D, dictionary size: 4194304 bytes, uncompressed size: 5113360 bytes