puck tools — test your egress policy against real C2 traffic →
#linux#gdb

Advanced config: pwndbg

We already got stock GDB into decent shape. Now lets make it zero cool.

Everything above makes stock GDB tolerable. pwndbg makes it a different tool. It is a Python extension that rebuilds the interface around what you actually want on every stop. Registers, disassembly, stack and backtrace, printed automatically. It also adds a pile of commands GDB has no answer for.

Install it into a directory you keep, because you will be pointing your .gdbinit at it:

$ git clone https://github.com/pwndbg/pwndbg ~/tools/pwndbg
$ cd ~/tools/pwndbg
$ ./setup.sh

setup.sh pulls the Python dependencies and appends one line to your ~/.gdbinit:

source ~/tools/pwndbg/gdbinit.py

What you get

The main feature is context. Stop anywhere and pwndbg prints registers, the next instructions, the source, the stack and the call chain in one screen, with pointers followed. You stop typing info registers, x/16gx $rsp and bt after every step.

This is the same break check / run Evan from the first lesson, with pwndbg loaded:

pwndbg stopped at a breakpoint in check(), showing five stacked panes: LEGEND, REGISTERS with every register’s value followed through memory, DISASM around the current instruction, SOURCE with line 5 marked, STACK, and BACKTRACE.

The LEGEND line at the top is the key: yellow is stack, blue is heap, red is code, purple is data. A register full of yellow is a pointer into the stack. A register full of red is a function pointer. You read the process state at a glance instead of chasing each value with x/.

Notice *RDI 0x7fffffffef1c ← 0x534f48006e617645 /* 'Evan' */. It followed the pointer and told you the argument is the string you passed. That is three commands in stock GDB, on every stop.

The commands worth knowing on day one:

Command What it answers
context Reprint the full picture. context regs, context stack for one pane
vmmap What is mapped where, with permissions. The map GDB never shows you
telescope <addr> Follow a chain of pointers and label what each one lands in
hexdump <addr> Annotated hex, rather than counting x/ format letters
search <pattern> Find a string or value anywhere in the address space
checksec NX, PIE, RELRO, canaries. The mitigations in force
piebase The runtime load base of a PIE binary
cyclic 200 / cyclic -l De Bruijn pattern, and the offset a crash landed at
heap, bins glibc allocator state, chunk by chunk

vmmap and piebase hand you the module base instead of making you dig out of info proc mappings before add-symbol-file will work. And checksec answers “what am I even up against” before you spend an hour on a mitigation that was never enabled.

Tuning it

pwndbg has its own config system, config to list and set to change:

# Which panes appear, and in what order.
set context-sections regs disasm code stack backtrace

# It prints a lot. Trim it on a small terminal.
set context-max-lines 8
set context-stack-lines 6

# Persist what you have changed.
configfile   # writes ~/.config/pwndbg/config.py
themefile    # writes the colour theme separately

theme lists the colour knobs, and everything is separately overridable. That matters if you work on a light terminal, because the defaults assume a dark one.

Further investigation

We didn’t even scratch the surface on what pwndbg enables make sure you check out the docs

If pwndbg isn’t your thing there are options. gef and gdb-dashboard do the similar with different opinions. They are all worth exploring in your debugging journey.

Up next

That is the course. You can start a program under GDB or attach to one that is already running, explain what the debugger did to it to make that possible, make custom functions, and read the whole process state on one screen.