Why bother with pwndbg?
When I first started doing binary exploitation, I was using stock GDB. It works, but it's honestly awful for anything beyond basic stepping. You're constantly typing x/20gx $rsp manually, there's no automatic context, and figuring out heap state mid-exploit is a nightmare. A classmate pointed me to pwndbg during a CTF and I haven't looked back.
pwndbg is a GDB plugin written in Python. It doesn't replace GDB — it sits on top and adds a context panel that auto-prints on every break: registers, stack, disassembly, and code. For heap exploitation it adds dedicated heap inspection commands. If you're doing any kind of binary work — CTF, fuzzer debugging, reverse engineering — it's the first thing I install.
pwndbg is not the same as peda or GEF. They're alternatives. pwndbg tends to be more actively maintained and has better heap support, which is why I use it.
Installation
It's straightforward. You need GDB already installed (most distros ship it, Kali definitely has it):
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.shThe setup script installs the Python dependencies and writes a source line into your ~/.gdbinit. After that, every time you launch GDB, pwndbg loads automatically. If you have multiple GDB configs already, check your ~/.gdbinit to make sure nothing conflicts.
On Ubuntu/Debian you might need to install some extras first:
sudo apt install gdb python3-pip
# then run setup.shOn Kali it's usually clean out of the box.
What you see when it loads
Launch any binary with gdb ./binary and set a breakpoint. When execution stops, pwndbg auto-prints the context panel:
─────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────
RAX 0x0
RBX 0x0
RCX 0x7ffff7af2151 (write+17) ◂— cmp rax, -0x1000 /* 'H=' */
RDX 0x0
RDI 0x1
RSI 0x602260 ◂— 'Hello, world!\n'
─────────────────────────────[ STACK ]──────────────────────────────────────────
00:0000│ rsp 0x7fffffffe1e0 ◂— 0x1
01:0008│ 0x7fffffffe1e8 —▸ 0x7fffffffe424
─────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────
► 0x401176 <main+34> call puts@plt <puts@plt>That's the default output on every break. Registers, stack, and disasm — all in one shot, no manual commands needed.
The commands I actually use
context
Reprints the full context panel. Useful after you scroll up and want to see registers again without stepping.
pwndbg> contextheap
Dumps the current heap state — chunks, sizes, which bin they're in. Essential for any heap exploitation work:
pwndbg> heap
pwndbg> bins # shows fastbins, smallbins, tcache
pwndbg> malloc_chunk addr # inspect a specific chunkWhen I was debugging my black-box fuzzer, this is how I confirmed UAF crashes — pause on SIGSEGV, run heap, and you can see the chunk that got freed with a dangling reference pointing into it.
vmmap
Shows the full virtual memory map with permissions. Saves you constantly running info proc mappings:
pwndbg> vmmapEspecially useful for checking if a region is executable — important when you're checking ASLR layout or looking for ROP gadget regions.
telescope
Dereferences a chain of pointers automatically. Way better than manually chasing pointers:
pwndbg> telescope $rsp 20
pwndbg> telescope 0x602260search
Searches process memory for a pattern — string, bytes, or value. Useful for finding where your shellcode or padding landed:
pwndbg> search -s "AAAA"
pwndbg> search -x 0x41414141cyclic
Generates a De Bruijn pattern for offset finding, built right into pwndbg:
pwndbg> cyclic 200
pwndbg> cyclic -l 0x61616168 # find offset of that pattern in rbp/ripUsing it with pwntools
pwndbg pairs well with pwntools. When you attach GDB to a running process during exploit development:
from pwn import *
p = process('./binary')
gdb.attach(p, gdbscript='''
break *main+0x50
continue
''')The gdbscript runs pwndbg commands — you can automate breakpoints, telescope inspection, heap dumps, all of it.
Common gotchas
A few things that tripped me up early on. If context stops printing, just run context manually — sometimes it gets suppressed after certain signals. If heap commands fail, the binary might not be using glibc malloc — they won't work on jemalloc or custom allocators. And if you're on a system with GDB < 8.x, upgrade — older versions have plugin compatibility issues with pwndbg.
Final thought
pwndbg is not magic — it's still GDB underneath. But the difference in usability is real. Being able to see heap state, registers, and disasm in one glance instead of manually printing everything cuts debugging time significantly. If you're doing any binary exploitation and you're still on stock GDB, spend the five minutes to install it.