Skip to content
The library

Notes on a book

From Day Zero to Zero Day

Eugene Lim · No Starch Press, 2025

A working method for vulnerability research: read the code sink-first, map it onto the binary, then hand the search to a fuzzer that measures its own progress.

Lim structures the book as three passes over the same problem. Part one reads source code, part two does the same work against a binary with no source, and part three hands it to a fuzzer. The organising idea is that these are not alternatives but stages of one process, and that each one tells the next where to look.

Two modules cite it - reversing and fuzzing - and it is the most current book on this shelf by some years, which matters here because tooling for automated analysis has moved more in the last five years than in the previous fifteen.

Why it is on this shelf

Because it answers the question a pwn player eventually hits: given a program and no hint, where is the bug. The answer here is a method rather than an intuition - find the sinks, work backwards to reachable sources, then automate the walk between them - and a method can be practised.

Chapter 3, Automated Variant Analysis

The chapter about scaling a single finding into every instance of it. Once you have one bug, you have a pattern, and a pattern can be written as a query: CodeQL treats the codebase as a database of program facts, Semgrep matches syntactic shapes with much less setup.

It is cited from the fuzzing module because it makes a point people miss in competitions too. When a challenge gives you a large codebase and one obviously deliberate mistake, the same mistake is often present twice, and the second one is the intended path. Writing the query is faster than reading the file when the file is large.

Chapter 6, Hybrid Analysis in Reverse Engineering

Where static and dynamic stop being separate activities. Emulation to run a fragment without the rest of the program, coverage measurement to see which paths an input actually took, and symbolic analysis to solve for an input that reaches a target block.

The reason to prefer this over a purely symbolic treatment is that he is honest about combining them. Pure symbolic execution dies of path explosion on any real binary; pure dynamic tracing only ever tells you about the input you had. The productive loop is to run concretely, find where you stop making progress, and go symbolic for that one constraint. Practical Binary Analysis gives the theory of the symbolic half, and dynamic binary analysis is the practical version here.

Chapter 7, Quick and Dirty Fuzzing

Deliberately unambitious and extremely useful: fuzzing files and protocols with almost no setup, using existing inputs as templates and mutating them. No instrumentation, no harness, no build changes.

This is the right first move in a competition, because the setup cost of the sophisticated approach is often longer than the challenge is open. A parser given a few thousand mutated versions of its own sample input crashes surprisingly often, and a crash tells you where to read. The same one-line random fuzzer appears in Attacking Network Protocols for the same reason: it costs nothing to try.

Chapter 8, Coverage-Guided Fuzzing

The chapter that explains why modern fuzzing works at all. A blind fuzzer is a random search over an enormous input space and will never guess a magic value. A coverage-guided fuzzer instruments the target, notices when an input reaches a basic block nothing has reached before, and keeps that input as a seed - which turns the random search into a hill climb over the program's own control flow.

He walks AFL++ properly: writing a harness, choosing a seed corpus, dictionaries for formats with keywords, and reading the statistics to tell a fuzzer that is making progress from one that is spinning. The harness is the part worth practising, because it is where judgment lives - a harness that reinitialises too much is slow, one that reinitialises too little produces crashes that do not reproduce.

Coverage-guided fuzzing here covers the same ground with CTF-sized examples, and the crashes it produces feed straight into stack overflow to shell and integer bugs and type confusion.

Where it stops

It is a discovery book, not an exploitation book. It will find you a crash and it will not turn one into a shell - there is no heap grooming, no ROP chain construction and no mitigation bypass here. Pair it with Hunting Security Bugs for bug classes and the ROP payload builder for what comes after.

It also assumes a research setting: a target you chose, time to build a harness, and a disclosure process at the end. A four-hour challenge changes which of its techniques are worth the setup.

What to take into a challenge

Work sink to source. Start from the dangerous operation - the copy, the allocation, the format string, the deserialise - and ask what would have to be true for attacker data to reach it. That search is bounded, whereas reading forwards from every input is not, and it is why experienced players find the bug in a large binary faster than a careful reader gets through the first function.