Wiki / Concepts

Bootloader

The first code to run after reset. It brings up the chip and hands control to the OS, which makes it the root of the boot chain and a prime place to break or enforce trust.

Multi-stage boot chain
1st-stage: mask ROM
2nd-stage bootloader (U-Boot / ESP)
Kernel / application
Each stage locates, optionally verifies, and jumps to the next. The mask ROM is immutable; the 2nd-stage loader and its environment usually live in writable flash, which is where an attacker breaks in.

A bootloader is the small program that runs immediately after CPU reset. It initialises clocks, RAM, and flash, then loads and hands control to the next stage, whether that is a Linux kernel or a bare-metal application.

What it is

Boot is almost never a single step. A tiny immutable first stage in mask ROM starts, finds a known second stage in flash, and jumps to it; that second stage (U-Boot on Linux SoCs, the ESP second-stage loader on ESP32) does the heavy lifting and finally launches the OS. The first stage cannot be changed because it is fixed in silicon; the later stages usually sit in writable flash, which is exactly why they are the interesting target.

Why it matters

The bootloader sits at the root of the boot chain, and whoever controls it controls what runs next. That makes it the natural place both to enforce trust (see Secure Boot) and to break it. A bootloader that exposes an interactive console, accepts unsigned images, or lets you edit its environment usually hands you the whole device.

How it works

Mask ROM code locates a known stage from flash, optionally verifies a signature, then jumps to it. U-Boot in particular exposes a rich console: it reads and writes memory (md, mw), manages an environment of variables (the crucial bootargs and bootcmd), and can fetch a kernel over the network with tftp. The kernel it launches is told what root filesystem to mount and what init to run through the kernel command line, and that command line comes straight from the bootloader environment.

How to inspect or attack it

The classic move is to interrupt boot over UART and drop into the U-Boot prompt.

picocom -b 115200 /dev/ttyUSB0     # attach to the console; spam a key at power-on
# at the U-Boot prompt:
printenv                            # dump the environment: bootargs, bootcmd
setenv bootargs 'console=ttyS0,115200 root=/dev/mtdblock2 init=/bin/sh'
saveenv && boot                     # boot straight to a root shell
md 0x80000000 40                    # read memory

Setting init=/bin/sh (or single) on the kernel command line boots straight to a root shell with no login. If the environment is writable and persisted with saveenv, that is often a full, permanent compromise. Even a locked console leaks the memory map and boot arguments through printenv.

Pitfalls

  • A vendor may strip or password-protect the U-Boot console, or set the autoboot delay to zero. Look for hidden interrupt keys, or glitch the boot to force the prompt.
  • Editing bootargs on a real target and running saveenv is persistent. Note the original value first so you can restore it.
  • Getting a shell is not the same as defeating Secure Boot: if the next stage is signature-checked, tampering downstream may just brick the boot.

Further reading