Hardware Hacking: Dumping a W25N01GVZEIR NAND Flash


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.mode = 3
	spi.max_speed_hz = 1000000


	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])

	# 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:


def wait(spi):
	while not(check_status(spi) == 0):

def check_status(spi):
	status = spi.xfer2([0x0f, 0xc0] + [0x00])
	if status[2] == 0x01:
		return 1
		return 0

if __name__ == '__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 

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

Created: 2022-05-13 Fri 00:43