Android Hardware Reverse Engineering

Reverse Engineering Android Firmware: Analyzing NAND Flash Dumps and ECC Metadata

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Unlocking the Secrets of Android NAND Flash

Reverse engineering Android firmware from raw NAND flash dumps is a challenging yet highly rewarding endeavor. It’s a critical skill for security researchers, digital forensics experts, and hardware hackers aiming to recover data, uncover hidden functionalities, or bypass security mechanisms. Unlike simpler storage media, NAND flash introduces complexities like Out-of-Band (OOB) data, Error Correction Code (ECC) metadata, and vendor-specific layouts, which demand specialized tools and a deep understanding of flash memory operations. This guide delves into the intricate process of analyzing raw NAND dumps, focusing specifically on deciphering and correcting ECC metadata to reconstruct a usable filesystem.

Understanding NAND Flash Fundamentals

NAND Architecture Overview

NAND flash memory is organized into pages, which are grouped into blocks. Data is read and programmed page-by-page, but erased block-by-block. A typical page size can range from 512 bytes to 4KB or 8KB in modern devices. Crucially, each page has an associated ‘spare’ or ‘Out-of-Band’ (OOB) area, typically 16 bytes per 512 bytes of main data. This OOB area is not directly accessible by the filesystem and is used by the flash controller to store vital metadata.

The Role of Out-of-Band (OOB) Data and ECC

The OOB area serves several purposes:

  • Bad Block Marking: Identifies blocks that have become unreliable.
  • Logical-to-Physical Block Mapping: For wear-leveling algorithms.
  • Filesystem Metadata: For filesystems like YAFFS2.
  • Error Correction Code (ECC) Data: This is paramount for NAND reliability. As NAND cells are prone to bit flips during read/write operations and over time, ECC algorithms (like BCH or Reed-Solomon) are employed to detect and correct these errors. The ECC syndrome bytes, generated from the main page data, are stored in the OOB area.

Without properly correcting these errors using the ECC metadata, a raw NAND dump will be corrupted and unreadable, rendering filesystem analysis impossible.

Acquiring the Raw NAND Dump

Direct Chip Access (DCA)

The most common and reliable method for acquiring a raw NAND dump for detailed reverse engineering is Direct Chip Access (DCA). This involves:

  1. Physical Disassembly: Carefully open the Android device.
  2. Locate and Identify the NAND Chip: Often an eMMC, eMCP, or raw NAND chip. Note its manufacturer and part number.
  3. Desoldering: Using a hot air station, precisely desolder the NAND chip from the PCB. This requires skill and proper equipment to avoid damaging the chip or the board.
  4. Chip Reader: Place the desoldered chip into a universal NAND programmer/reader (e.g., RT809H, TL866II Plus with appropriate adapters, or a specialized professional reader like those from SoftCenter or PC-3000 Flash).
  5. Dump Acquisition: Use the programmer’s software to read the entire contents of the chip, typically saving it as a raw binary file (e.g., nand_dump.bin).

While JTAG, ISP (In-System Programming), or software-based dumps via bootloader exploits are alternatives, DCA provides the most complete and unadulterated raw data, including OOB areas.

Initial Dump Analysis and Identifying ECC Parameters

Raw Data Inspection

Once you have the raw dump, the first step is to analyze its structure. Open the dump in a hex editor (e.g., HxD, 010 Editor) or use command-line tools:

hexdump -C nand_dump.bin | head

You’ll observe a repeating pattern of main data followed by OOB data. Determining the exact page size and OOB size is crucial. Common values are 2048+64 bytes (2KB page + 64 bytes OOB) or 4096+128 bytes (4KB page + 128 bytes OOB). Search for known magic strings or filesystem headers (e.g., "UBI!", "YFFS", "ANDROID!") which can give clues about the overall structure and OOB layout.

Deciphering OOB Layout and ECC Placement

This is often the most challenging part, as OOB layouts are highly vendor-specific. The ECC metadata is usually placed in specific offsets within the OOB area. You might need to infer these parameters by:

  • Manufacturer Datasheets: If available for the specific NAND chip.
  • Firmware Analysis: Examine the bootloader (e.g., U-Boot) or Linux kernel source code for the device, which often defines NAND controller configurations and ECC parameters.
  • Trial and Error: Use tools that allow specifying page size, OOB size, and different ECC parameters.

A hypothetical OOB layout might look like this:

OOB Byte Layout (example for 64-byte OOB): 0-1: Bad Block Marker 2-15: YAFFS2 Tags (if applicable) 16-27: ECC bytes for data chunk 1 (e.g., 512B) 28-39: ECC bytes for data chunk 2 (e.g., 512B) 40-51: ECC bytes for data chunk 3 (e.g., 512B) 52-63: ECC bytes for data chunk 4 (e.g., 512B)

In a 2KB page (2048 bytes) with 64 bytes OOB, the 2048 data bytes might be divided into four 512-byte chunks, each with its own 12-byte ECC syndrome in the OOB.

The Art of ECC Correction

ECC Algorithms and Parameters

Modern NAND flash predominantly uses BCH (Bose-Chaudhuri-Hocquenghem) codes, which are highly efficient at correcting multiple random bit errors. Key parameters for BCH correction are:

  • Data Chunk Size (N): The number of data bytes for which ECC is calculated.
  • ECC Bytes (K): The number of ECC syndrome bytes generated for N data bytes.
  • Error Correction Capability (t): The number of correctable bits per N data bytes + K ECC bytes.

Manual ECC Reconstruction with bchlib (Python)

Let’s assume we’ve determined a page size of 2048 bytes, OOB of 64 bytes. The OOB contains four 13-byte ECC syndromes, each protecting a 512-byte data chunk, with a correction capability (t) of 4 bits. We can use a Python library like bchlib to perform the correction:

import bchlib import os # Assume these parameters are determined from firmware/trial-and-error BCH_BITS = 512 * 8 # Data chunk size in bits (512 bytes) BCH_POLYNOMIAL = 0x2035 # Example: often specific to flash controller or 0x8613 for t=4 # For t=4, usually 13 ECC bytes are generated BCH_T = 4 # Error correction capability (bits) BCH_K = BCH_T * 13 # Approx number of ECC bytes. bchlib will calculate precise. bch = bchlib.BCH(BCH_BITS, BCH_T) def correct_nand_page(page_data, oob_data): corrected_data = bytearray(2048) # 2KB page size for chunk_idx in range(4): # For a 2KB page, split into four 512-byte chunks data_chunk = page_data[chunk_idx * 512 : (chunk_idx + 1) * 512] # ECC bytes for this chunk (assuming 13 bytes per chunk, starting at offset 16 in OOB) ecc_offset = 16 + (chunk_idx * 13) ecc_chunk = oob_data[ecc_offset : ecc_offset + 13] # Try to correct data_with_ecc = data_chunk + ecc_chunk # bchlib expects data + ecc to correct # The ecc must be the *raw* syndrome bytes, not encoded. # This is a simplified example; actual bchlib usage might vary based on how ECC is stored. # Often, bchlib.decode() takes data and calculates its ECC, then compares with stored. # Let's assume ecc_chunk are the raw syndrome bytes as expected by bchlib's decode. # More accurate: Calculate ECC for data_chunk and compare with ecc_chunk. # Then if mismatch, use data_chunk and ecc_chunk to find errors. # A direct example with bchlib: num_errors, corrected_chunk, calculated_ecc = bch.decode(data_chunk, ecc_chunk) if num_errors > 0: print(f"Corrected {num_errors} errors in chunk {chunk_idx}.") corrected_data[chunk_idx * 512 : (chunk_idx + 1) * 512] = corrected_chunk.data # If no errors, just use original data else: corrected_data[chunk_idx * 512 : (chunk_idx + 1) * 512] = data_chunk return bytes(corrected_data) # Example usage (assuming you read raw_dump_file page by page) # with open("nand_dump.bin", "rb") as f_in: # with open("corrected_nand_dump.bin", "wb") as f_out: # PAGE_SIZE = 2048 # OOB_SIZE = 64 # while True: # raw_page = f_in.read(PAGE_SIZE + OOB_SIZE) # if not raw_page: # break # main_data = raw_page[:PAGE_SIZE] # oob_data = raw_page[PAGE_SIZE:] # corrected_page = correct_nand_page(main_data, oob_data) # f_out.write(corrected_page) # print("NAND dump corrected and saved to corrected_nand_dump.bin")

The snippet above illustrates the logic. In a real scenario, accurately determining BCH_POLYNOMIAL and the exact OOB ECC layout is crucial. Tools like nanddump_parser (a specialized open-source tool) can automate some of this if your chip/layout is supported, or provide a framework for defining custom ECC parameters.

Reconstructing the Filesystem

Once the NAND dump is ECC-corrected, the next step is to identify and extract the filesystems contained within. Android devices typically use:

  • YAFFS2 (Yet Another Flash File System 2): Common in older Android versions for /system, /data, etc. It natively handles bad blocks and uses OOB for its own metadata.
  • UBI/UBIFS (Unsorted Block Images/UBI File System): Modern standard for NAND flash. UBI sits as a layer on top of raw NAND, providing wear leveling, bad block management, and logical volumes. UBIFS is then built on UBI volumes.
  • Raw EXT4: Less common directly on raw NAND due to its lack of native bad block management. Usually seen on eMMC devices where an FTL (Flash Translation Layer) handles bad blocks.

Identifying Filesystem Headers and Extracting

After correction, search the dump for specific magic bytes:

  • UBI: Look for UBI! (0x55424921) at the beginning of UBI erase blocks (typically 128KB or 256KB).
  • YAFFS2: Look for YFFS at the beginning of data pages or within the OOB area.

For UBI images, ubireader_py is an invaluable tool:

# Install ubireader_py pip install ubireader # Scan the corrected NAND dump for UBI images ubireader_extract_files -o output_dir corrected_nand_dump.bin

This command will scan the `corrected_nand_dump.bin`, identify UBI volumes, and extract their contents into the `output_dir`. For YAFFS2, tools like `yaffs2-utils` or specialized scripts can be used to parse the image and extract files.

# Example for YAFFS2 (may require specific utilities or custom parsing) yaffs2extract -i yaffs2_partition.img -o extracted_yaffs

Challenges and Advanced Techniques

Even with ECC correction, challenges persist:

  • Vendor-Specific Scrambling: Some manufacturers employ data scrambling before writing to NAND, which must be reversed.
  • Complex OOB Layouts: ECC parameters might change across different regions of the NAND or even within different parts of the OOB.
  • Bad Block Management: Understanding how bad blocks are skipped or remapped is crucial for correctly assembling the linear data.
  • Partition Tables: Identifying partition layouts (e.g., GPT, MBR, or custom Android schemes) from the raw data.

Conclusion

Reverse engineering Android firmware from raw NAND flash dumps, while demanding, offers unparalleled insights into a device’s operation, security, and data. The journey from a raw, error-ridden binary dump to a fully reconstructed filesystem is a testament to the power of meticulous analysis and specialized tools. By mastering the concepts of NAND architecture, OOB data, and especially ECC correction, researchers can unlock a wealth of information previously locked within the silicon, paving the way for advanced security research, forensic investigations, and deeper hardware understanding.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner