Solution to U-Boot Firmware Extraction Issues

Author: Waterdrop Lab Heng An Jiaxin
1. Background

With the development of the technological era, Internet of Things (IoT) devices are rapidly developing across various industries, driving industrial upgrades and the arrival of the intelligent era. However, their security is particularly important, raising requirements for security research and improving protection. IoT security involves many aspects, and below we will share firmware extraction technology – solving the problem of not being able to correctly obtain data in U-Boot mode.

Note:This article is based on experiments conducted on my own development board to extract NAND Flash data. The methods introduced may not be universally applicable and are for reference only. Please forgive any shortcomings!

2. Common Issues in Firmware Extraction

AThe NAND FLASH chip has read protection, making it impossible to correctly extract data from the chip using conventional operations such as JTAG/SWD.

Solution:

Please refer to the article:

Bypassing Read Protection Technology – Firmware Extraction from a Shared Bike Bluetooth Lock

Website link:

https://mp.weixin.qq.com/s/U83j3rzJwSp-3d6sDtgU1w

BThe extracted root filesystem is encrypted, and tools like Binwalk cannot parse it.

Encrypting the filesystem protects code security and increases the difficulty of extracting and analyzing firmware. This method is increasingly adopted by manufacturers. However, from a security perspective, it is necessary to correctly decrypt the firmware, obtain the filesystem, and then conduct vulnerability mining.

Solution:

First, extract the BootLoader partition data and kernel partition data from the NAND FLASH, then perform reverse analysis to identify the decryption program.

3. Related Knowledge Points

ABootLoader

In simple terms, the BootLoader is a small piece of code that starts executing when the system powers on, initializes hardware devices, prepares the software environment, and finally calls the operating system kernel.

The BootLoader runs first, copying the kernel file from the NAND FLASH to memory, and passing the configured parameters to the kernel to run it.

The BootLoader decompresses the compressed files on FLASH before running them. (Specifically decrypting the kernel and filesystem stored in FLASH is crucial for later firmware cracking).

BLinux Kernel

Parsing the startup parameters passed by U-Boot

Mounting the root filesystem

Starting the first application

CFilesystem

Includes the root filesystem and the filesystem built on the Flash memory device. It contains the applications, libraries, and other components necessary for the Linux system to operate.(The applications in the system are the targets for vulnerability analysis and mining.)

DCommon Commands

mtdpart: View partitions

md: Display memory contents

md command usage: md start_address length (addresses and lengths are generally in hexadecimal format)

nand read: Read data from NAND Flash to memory

nand read[.jffs2] addr off size: Read size bytes of data from the NAND Flash starting at offset addr into memory.

nand bad: View bad blocks in NAND Flash

ENAND Flash Introduction: K9F2G08U0A Model

i. NAND Flash Address Mechanism (Very Important):

1. The storage unit of NAND Flash is composed of Blocks, which are made up of Pages, and data is stored on Pages.

2. NAND Flash reads and writes data on a page (Page) basis.

3. See the K9F2G08U0A datasheet for information on address cycles:

Solution to U-Boot Firmware Extraction Issues

From the above image, it can be seen that K9F2G08U0A is implemented using 5 cycles. Since NAND Flash reads data on a page basis and erases data on a block basis, there are three types of addresses:

  • Column Address: Column address, the low 8 bits of the address, i.e., the offset address within the page.

  • Page Address: Page address.

  • Block Address: Block address.

Taking address 0xBB8CCB8 as an example, the address transfer process requires five address cycles:

0xBB8CCB8 = 00001011 1011 1000 1100 1100 1011 1000

  • First cycle: A[0:7] which is B8

  • Second cycle: A[8:11] takes four bits 1100, then adds 4 bits 0, which is 0000 1100, i.e., 0C

  • Third cycle: A[12:19] takes eight bits 1000 1100, i.e., 8C

  • Fourth cycle: A[20:27] takes eight bits 1011 1011, i.e., BB

  • Fifth cycle: A[28] takes one bit 0, filling to eight bits, which is 0000 0000, i.e., 00

Analyzing back, NAND Flash receives the first two cycles of address A[0:11], which is 0CB8, i.e., 1100 1011 1000. Analysis: A[10:0]=100 1011 1000=1208, indicating the 1208th Byte.

ii. NAND Flash Read Commands

1. Refer to the K9F2G08U0A manual.

Solution to U-Boot Firmware Extraction Issues

2. Based on the K9F2G08U0A address cycle and Read command, write the following code to read NAND Flash data:

void nand_select(void){  /* Enable chip select */  NFCONT &= ~(1 << 1);}void nand_deselect(void){  /* Disable chip select */  NFCONT |= (1 << 1);}void nand_cmd(unsigned char cmd){  volatile int i;  NFCCMD = cmd;  for(i=0; i < 10; i++);}void nand_addr_byte(unsigned char addr){  volatile int i;  NFADDR = addr;  for(i=0; i < 10; i++);}unsigned char nand_data(void){  return NFDATA;}void wait_ready(void){  while (!(NFSTAT & 1));}void nand_read(unsigned int addr, unsigned char *buf, unsigned int len){  int i = 0;  int page = addr / 2048;  int col  = addr & (2048 - 1);  nand_select();// Select chip  while (i < len){    /* Send 00h command */    nand_cmd(00);    /* Send address */    /* col addr */    nand_addr_byte(col & 0xff);    nand_addr_byte((col >> 8) & 0xff);    /* row/page addr */    nand_addr_byte(page & 0xff);    nand_addr_byte((page >> 8) & 0xff);    nand_addr_byte((page >> 16) & 0xff);    /* Send 30h command */    nand_cmd(0x30);    /* Wait for ready */    wait_ready();    /* Read data */    for (; (col < 2048) && (i < len); col++){      buf[i++] = nand_data();          }    if (i == len){      break;    }    col = 0;    page++;  }  nand_deselect();  }
4. Two Methods for Extracting Data from NAND Flash

AConventional Method – Extracting data through U-Boot mode

Enter U-Boot mode, use the nand read command to read the desired data into memory, then dump the memory.

BUnconventional Method – Directly sending commands to NAND Flash to read data

When reading data from NAND FLASH into memory, there is ECC verification to determine if the data is correct; if there are bad blocks in the data read, it will not be possible to successfully read the data into memory, making it impossible to extract data through U-Boot mode.

5. Extracting BootLoader Partition Data (with Bad Blocks)

AConventional Method: Extracting Data through U-Boot Mode

i. Enter U-Boot mode and use help to view commands

During system startup, press spacebar to enter U-Boot mode

Solution to U-Boot Firmware Extraction Issues

ii. Use mtdpart to view partitions

Solution to U-Boot Firmware Extraction Issues

It can be seen that NAND Flash has four partitions, and the size of each partition can be calculated.

iii. Use the nand read command to read BootLoader partition data into memory

Solution to U-Boot Firmware Extraction Issues

According to the prompt, it can be seen that the read failed due to the presence of bad blocks.

BBootLoader partition data has bad blocks, using an unconventional method to extract data

i. OpenOCD connects to the target device, opening port 4444

ii. Write the following script based on NAND Flash instructions and address cycle characteristics (5 cycles) to read data byte by byte:

#!/usr/bin/env rubyrequire 'net/telnet'# NFCONF    __REG(0x4E000000)  //NAND flash configuration             # NFCONT    __REG(0x4E000004)  //NAND flash control                   # NFCCMD    __REG_BYTE(0x4E000008)  //NAND flash command                   # NFADDR    __REG_BYTE(0x4E00000C)  //NAND flash address  # NFDATA    __REG_BYTE(0x4E000010)  //NAND flash datadebug = Net::Telnet::new("Host" => "localhost", "Port" => 4444)dumpfile = File.open("dump_bootloader.bin", "a+")debug.cmd("halt")debug.cmd("mww 0x4E000004  1")debug.cmd("mwb 0x4E000008 0x00")   # Commanddebug.cmd("mwb 0x4E00000C 0x00")   # 5 address cyclesdebug.cmd("mwb 0x4E00000C 0x00")debug.cmd("mwb 0x4E00000C 0x00")debug.cmd("mwb 0x4E00000C 0x00")debug.cmd("mwb 0x4E00000C 0x00")debug.cmd("mwb 0x4E000008 0x30")   # Command$i = 0$num = 0x1000while $i < $num  do  response =debug.cmd("mdb 0x4E000010 1")    # Read 1 byte  value = response.match(/0x4e000010: ([0-9a-fA-F]{2})/)[1].hex  dumpfile.write([value].pack("c"))  $i +=1end;dumpfile.closedebug.close

iii. Compare the data obtained through the script with the data seen using nand dump 0

1. nand dump 0 data

Solution to U-Boot Firmware Extraction Issues

2. Data read by the script dump_bootloader.bin

Solution to U-Boot Firmware Extraction Issues

By comparison, it can be seen that at position 0x125, the data in dump_bootloader.bin has an extra byte 0D, thus the data read using the nand read command cannot pass verification.

In the presence of bad blocks, successfully obtaining BootLoader partition data indicates that the method of obtaining data through chip instruction operations is feasible.

6. Extracting Kernel Partition Data

AReading kernel partition data into memory at address 0

nand read.jffs2 0 kernel

Solution to U-Boot Firmware Extraction Issues

Using a script, dump kernel partition data from memory

#!/usr/bin/env rubyrequire 'net/telnet'debug = Net::Telnet::new("Host" => "localhost", "Port" => 4444)debug.cmd("halt")debug.cmd("dump_image dump_kernel_part.bin 0x0 0x200000")debug.close

View the read data

Solution to U-Boot Firmware Extraction Issues

It can be seen that the data after 0x1000 is incorrect.

Reason:

This is because the on-chip memory is only 4k, so data exceeding 4k cannot be read into address 0.

Solution:

Read the kernel partition data into the flash SDRAM, starting at address 0x30000000 (refer to the manual).

Solution to U-Boot Firmware Extraction Issues

BReading kernel partition data into memory at the starting address of SDRAM

nand read.jffs2 0x30000000 kernel

Solution to U-Boot Firmware Extraction Issues

Using a script, dump kernel partition data from memory

require 'net/telnet'debug = Net::Telnet::new("Host" => "localhost", "Port" => 4444)debug.cmd("halt")debug.cmd("dump_image dump_kernel.bin 0x30000000 0x200000")debug.close

View the read data

Solution to U-Boot Firmware Extraction Issues

Correctly read data, method is feasible

7. Conclusion

Through practical operations, the problem of not being able to correctly extract NAND Flash partition data in U-Boot mode has been solved, and the feasibility of extracting NAND Flash partition data with bad blocks through chip instruction operations has been verified; the issue of incomplete extraction of kernel partition data larger than the on-chip memory size has also been properly addressed.

Successfully extracting BootLoader partition data and kernel partition data provides a basis for the next step of cracking the encrypted root filesystem.

Original source: Critical Infrastructure Security Emergency Response Center

Solution to U-Boot Firmware Extraction Issues

Leave a Comment