Master the fundamental concepts of debugging mastery through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksptrace (process trace) is the Linux system call that underpins GDB, strace, and every other debugger on Linux. It allows one process to observe and control the execution of another process.
PTRACE_TRACEME: Child declares it should be traced by its parentPTRACE_PEEKDATA / PTRACE_POKEDATA: Read/write child memoryPTRACE_SINGLESTEP: Execute exactly one instructionPTRACE_CONT: Continue executionPTRACE_GETREGS / PTRACE_SETREGS: Read/write registersSetting breakpoints with INT3:
PTRACE_PEEKDATA0xCC (INT3) using PTRACE_POKEDATA0xCCYou will implement a simple debugger using ptrace that forks a child, sets a breakpoint, and single-steps. Building a minimal debugger from scratch teaches you exactly what GDB does under the hood when you set a breakpoint or step through code.
The parent calls waitpid after each PTRACE_CONT or PTRACE_SINGLESTEP. When the child stops on SIGTRAP (breakpoint hit), the parent reads registers with PTRACE_GETREGS, inspects state, and decides whether to continue or step. GDB wraps this loop with user-friendly commands, but the ptrace syscall sequence is identical. Security policies like YAMA ptrace_scope restrict which processes can be traced on hardened systems.
A debugger sets a breakpoint by writing INT3 (0xCC, one byte) over an instruction. When the CPU executes it, the trap leaves RIP one byte past the breakpoint. The debugger reports the stop, restores the original byte, and rewinds RIP by 1 so that the instruction runs.
Simulate this for a program whose N instructions sit at base, base + 0x10, base + 0x20, ... and run once each, in order. A breakpoint is hit when its address is one of those instruction addresses; an address that appears twice is still one breakpoint. N can be as large as 10^9, so decide from the breakpoint addresses rather than by stepping through every instruction.
BASE N (BASE in hex), then B (0 to 8), then B breakpoint addresses in hex.
For each breakpoint hit, in address order, two lines:
cLoading…
then
cLoading…
Addresses are lowercase hex without leading zeros.