Wiki / Concepts

Partition Table

The map of a flash chip. It tells you where the bootloader, app, OTA slots, and secret-bearing data partitions like NVS begin and end.

What the table maps (ESP32 flash)
2nd-stage bootloader 0x1000
Partition table 0x8000
nvs (data) 0x9000
otadata 0xd000
factory / ota_0 (app) 0x10000
ota_1 (app)
The 3 KB table at 0x8000 is the small structure that names every other region here. Read it first: its entries give the exact offset and size of the nvs, the OTA slots, and the app you carve next. Actual values come from the project's partitions.csv.

A partition table is the on-flash map describing how a chip's storage is divided into regions: bootloader, application, filesystem, and data partitions such as NVS. It is the index that turns an opaque flash dump into named, carve-able pieces.

What it is

Flash is a flat address space; the partition table is the small structure that gives it meaning. Each entry names a region by its type, offset, and size. On ESP32 the table is a fixed 3 KB structure, by default at offset 0x8000, and each entry records a name, a type (app or data), a subtype (factory, ota_0, ota_1, nvs, phy, otadata), an offset, and a size. On Linux SoCs the analogous map is the MTD partition list (from the kernel command line or device tree) or a GPT/MBR on eMMC.

Why it matters

Without the table, a flash dump is one opaque blob and you are guessing where anything starts. With it, you know exactly where the app, the OTA slots, and the secret-bearing data partitions live, so you can carve and parse each one correctly. The nvs entry in particular points you straight at the ESP32 credential store; the otadata entry tells you which app slot is currently active.

How it works

The second-stage bootloader reads the table at boot to find the app to launch, consulting otadata to choose between ota_0 and ota_1. The same structure is what tooling reads to interpret a dump. On ESP32, esptool.py reads the raw bytes and gen_esp32part.py converts between the binary table and the human-readable CSV form.

How to inspect it

Dump the table region, then decode it.

esptool.py -p /dev/ttyUSB0 read_flash 0x8000 0xc00 ptable.bin   # dump the 3 KB table
gen_esp32part.py ptable.bin                                     # decode to readable CSV

The decoded output lists every partition with its offset and size. Use those offsets to slice the full dump: carve the nvs region and parse it for Wi-Fi credentials and tokens, extract the active app slot for reverse engineering, and note any SPIFFS or FAT data partition that might hold logs or config. On a Linux target, cat /proc/mtd on a live device or the mtdparts= string in the boot arguments gives the same map.

Pitfalls

  • The default 0x8000 offset is only a default. A custom build can move the table, so confirm it rather than assuming.
  • The table describes intent, not integrity. A partition can be present but empty, encrypted, or larger than its used content.
  • Do not confuse the app partition size with the app size. Slots are usually padded; carve to the real image length, not the whole slot.
  • If Flash Encryption is on, the dump you carve from is ciphertext and the offsets still apply, but the contents will not parse until decrypted.

Further reading