Lion Cipher Lab
Write a boot sector from scratch and make QEMU jump into 32-bit protected mode
- Foundations
- Advanced
- ~90 min
- Free · no account required
Why it matters
Every computer you have ever used started the same way: firmware powered on, read a single 512-byte sector off a disk into a fixed address in memory, and jumped to it -- in a primitive 16-bit mode with no memory protection and a 1MB ceiling. That tiny program's whole job is to climb the CPU out of that mode and hand control to something bigger. Almost nobody has watched it happen a byte at a time, which is exactly why the boot process feels like magic and why bootkits, Secure Boot, and 'measured boot' sound like abstractions instead of mechanisms. Writing the boot sector yourself dissolves…
What you'll learn
- The exact contract a legacy BIOS hands your code: after POST it loads the first 512-byte sector of the boot device to physical 0x0000:0x7C00 and jumps there IN 16-BIT REAL MODE -- but ONLY if the sector's last two bytes are 0x55 then 0xAA (the little-endian word 0xAA55); without that signature the firmware reports 'no bootable device' and never runs your code
- Why `[org 0x7c00]` is not optional: NASM resolves label addresses relative to the org, so telling the assembler the code will live at 0x7C00 makes `mov si, msg` compute the RIGHT absolute address -- omit it (default org 0) and every memory reference points 0x7C00 bytes too low, which is the classic 'it prints garbage / triple-faults instantly' bug
- Why you must zero DS/ES/SS yourself and place a stack: the BIOS leaves the segment registers and SP in an undefined state, so any `lodsb`, `int 0x10` string, or `push` reads/writes a random segment until you `xor ax,ax` / `mov ds,ax` and point SP somewhere safe (below 0x7C00) -- an unzeroed-segment bug produces intermittent, machine-dependent corruption
- The four things the real-mode -> 32-bit protected-mode transition actually requires, in order: enable the A20 line (undo the 8086 1MB address wraparound), `lgdt` a Global Descriptor Table with a flat 4GB code and data descriptor, set bit 0 (PE) of cr0, then FAR-JUMP to a `code_selector:label` target -- because you cannot `mov` into CS, and the far jump is what loads CS with the new selector and flushes the prefetch pipeline of stale 16-bit decodes
- The GDT descriptor byte layout by hand: a null descriptor first, then a code descriptor (access 10011010b = present/ring0/code/executable-readable) and a data descriptor (10010010b = present/ring0/data/writable), each with the 32-bit/4KB-granularity flags 11001111b, and a `gdt_descriptor` record of `dw size-1` (limit) + `dd base` that `lgdt` consumes
- Writing to hardware directly: in 32-bit protected mode with a flat data segment you store character+attribute word pairs straight into the VGA text buffer at physical 0xb8000 (low byte = ASCII, high byte = colour attribute like 0x0f), the same memory the BIOS was printing through -- proof you are running your own 32-bit code with your own descriptors
- Debugging the un-debuggable: QEMU's `-d int` logs every interrupt/exception the guest takes, so a bad protected-mode transition shows its true cause as an exception CASCADE (a #GP fault, then a #DF double fault, then a reset = triple fault) instead of a silent reboot loop; `-no-reboot -no-shutdown` freeze the machine so you can read the log, and `-s -S` expose a gdb stub to single-step the exact instruction that faults
Steps in this lab
- Read this first: why an emulator, and the mental model of a boot sector
- The contract: what the firmware gives you, and the one byte-pair it demands
- Install the toolchain and open a terminal
- Act 1: write the minimal boot sector (boot.asm)
- Assemble, boot it, and deliberately break the signature
- Act 2: why real mode is a dead end, and what the climb costs
- Enable the A20 line (and know why it exists)
- Build the GDT: the table protected mode reads to define your segments
- Make the jump and write to the screen's memory (boot32.asm)
- Catch a triple fault: read QEMU's -d int log and step it in gdb
- When it fails, and do it better