Open Source Development Study: Pwndbg
Dominik 'Disconnect3d' Czarnota
@ Pykonik Tech Talks #76
2025.04.24, Cracow, Poland
1
# whoami
2
Blog: disconnect3d.pl
justCattheFish CTF team captain
Staff Security Engineer @
Maintainer of Pwndbg/Pwndbg
Pwndbg: what & why?
3
Pwndbg: what & why?
Plugin for GDB & LLDB
Adds many features missing from above debuggers useful for security research, reverse engineering, exploit development
4
Pwndbg �features
5
6
Can be also invoked with command
7
Shows color LEGEND first :)
8
Then it displays sections
9
Then it displays sections
REGISTERS
10
Then it displays sections
REGISTERS
DISASM
11
Then it displays sections
REGISTERS
DISASM
SOURCE CODE (if available, can also be decompiled from IDA Hex-Rays)
12
Then it displays sections
REGISTERS
DISASM
SOURCE CODE (if available, can also be decompiled from IDA Hex-Rays)
STACK memory contents
13
Then it displays sections
REGISTERS
DISASM
SOURCE CODE (if available, can also be decompiled from IDA Hex-Rays)
STACK memory contents
BACKTRACE
“telescope”
aka�“dereference everything pls”
int******
int*****
int****
int***
int**
int*
int
typedef struct Error {
char* msg;
int code;
int (*callback)(const char*);
} Error;
int main() {
Error e = {"DivByZero", 0x1, puts};
Error* ptr = &e;
// break here!
typedef struct Error {
char* msg;
int code;
int (*callback)(const char*);
} Error;
int main() {
Error e = {"DivByZero", 0x1, puts};
Error* ptr = &e;
// break here!
typedef struct Error {
char* msg;
int code;
int (*callback)(const char*);
} Error;
int main() {
Error e = {"DivByZero", 0x1, puts};
Error* ptr = &e;
// break here!
&ptr
typedef struct Error {
char* msg;
int code;
int (*callback)(const char*);
} Error;
int main() {
Error e = {"DivByZero", 0x1, puts};
Error* ptr = &e;
// break here!
&ptr
&e
typedef struct Error {
char* msg;
int code;
int (*callback)(const char*);
} Error;
int main() {
Error e = {"DivByZero", 0x1, puts};
Error* ptr = &e;
// break here!
&ptr
&e
Error e
Non-linear disassembly
Predicting values
statically
and by emulating code!
Predicting values
22
Non-linear disassembly
23
Non-linear disassembly
24
Non-linear disassembly
25
Non-linear disassembly
26
JE = "Jump If Equal" — but more precisely "Jump if ZF (Zero Flag) is set"
Non-linear disassembly
27
JE = "Jump If Equal" — but more precisely "Jump if ZF (Zero Flag) is set"
Non-linear disassembly
28
JE = "Jump If Equal" — but more precisely "Jump if ZF (Zero Flag) is set"
If we set ZF flag (setflag ZF 1):
Displaying function arguments
Proper hexdump o/
31
Vmmap: better than info proc mappings
32
Configure everything: config
33
Configure everything: theme
34
& really many many more…
& really many many more…
36
Pwndbg: history
37
PEDA
38
GEF
39
Pwndbg
Fork of GEF in 2015
Made by @ebeip90�(Zach Riggle)
Split into modules
+ Include batteries�(dependencies)
40
Pwndbg: history
41
Random stats
42
123 commits
Random stats
43
Random stats
44
Pwndbg: my first contribution
45
Pwndbg: my first contribution
46
Various aspects of maintaining an open source project
47
Getting users & contributors
48
Getting users & contributors
How to live?
49
Getting users & contributors
50
Good git workflow
* just keep it simple & stupid
51
Our git workflow
Initially: dev, beta, stable branches
52
(Imperfect ChatGPT generated image…)
Our git workflow
Initially: dev, beta, stable branches
Now: dev + releases
53
(Imperfect ChatGPT generated image…)
Release 2
Release 1
"Easy" setup / installation
54
How to install?
Pwndbg not a "normal" Python module:
55
How to install?
56
How to install?
Pwndbg not a "normal" Python module: you need GDB compiled with Python
57
pip install -r requirements.txt
echo "source gdbinit.py" >> ~/.gdbinit
How to install?
Pwndbg not a "normal" Python module: you need GDB compiled with Python
58
pip install -r requirements.txt
echo "source gdbinit.py" >> ~/.gdbinit
Nix to the rescue!
59
Nix to the rescue!
We build
That include everything: GDB, Python, native deps, Python deps, glibc etc.
See https://github.com/pwndbg/pwndbg/tree/dev/nix
60
Nix to the rescue!
61
But macOS quarantine…
* so we have a homebrew package too
62
But macOS quarantine…
63
But macOS quarantine…
64
But macOS quarantine…
65
But macOS quarantine…
66
But macOS quarantine…
67
Testing
68
Testing
69
Unit tests
70
Unit tests
71
GDB tests
72
GDB tests
73
QEMU tests
74
Pwndbg plugin: how
75
Pwndbg plugin: how
How?
76
Pwndbg and GDB
77
# gdb --quiet --nx
(gdb) !cat hello.py
print("Hello world")
(gdb) source hello.py
Hello world
(gdb) pi print(gdb)
<module 'gdb' from '/usr/share/gdb/python/gdb/__init__.py'>
(gdb) pi
>>> 1+1
2
>>>
Pwndbg and GDB
But the API is lacking so sometimes we have to parse strings…:
78
lang = gdb.execute("show language", to_string=True)
# lang = 'The current source language is "auto; currently c".'
Pwndbg & LLDB: How its made
86
while True:
…
line = input(PROMPT)
…
if not exec_repl_command(line, sys.stdout.buffer, dbg, driver, relay):
…
Pwndbg & LLDB: How its made
87
while True:
…
line = input(PROMPT)
…
if not exec_repl_command(line, sys.stdout.buffer, dbg, driver, relay):
…
Pwndbg & LLDB: How its made
^ Some "smart" parsing
88
while True:
…
line = input(PROMPT)
…
if not exec_repl_command(line, sys.stdout.buffer, dbg, driver, relay):
…
Pwndbg & LLDB: How its made
^ Some "smart" parsing
89
while True:
…
line = input(PROMPT)
…
if not exec_repl_command(line, sys.stdout.buffer, dbg, driver, relay):
…
Not cool contributions:�"can i get assigned" (???)
90
91
92
(Not cool contributions:�"vibe coding" (???)
93
94
95
Random stories:
ipdb import time …
96
ipdb import time …
$ python --version
Python 3.12.7
$ cat import-pdb.py
import pdb
$ cat import-ipdb.py
import ipdb
$ hyperfine "python import-pdb.py"
Benchmark 1: python import-pdb.py
Time (mean ± σ): 75.4 ms ± 3.8 ms [User: 49.0 ms, System: 17.1 ms]
Range (min … max): 69.6 ms … 90.4 ms 33 runs
$ hyperfine "python import-ipdb.py"
Benchmark 1: python import-ipdb.py
Time (mean ± σ): 422.4 ms ± 22.4 ms [User: 321.4 ms, System: 82.2 ms]
Range (min … max): 402.2 ms … 470.4 ms 10 runs
97
Reported on: https://github.com/gotcha/ipdb/issues/289
ipdb import time …
$ python --version
Python 3.12.7
$ cat import-pdb.py
import pdb
$ cat import-ipdb.py
import ipdb
$ hyperfine "python import-pdb.py"
Benchmark 1: python import-pdb.py
Time (mean ± σ): 75.4 ms ± 3.8 ms [User: 49.0 ms, System: 17.1 ms]
Range (min … max): 69.6 ms … 90.4 ms 33 runs
$ hyperfine "python import-ipdb.py"
Benchmark 1: python import-ipdb.py
Time (mean ± σ): 422.4 ms ± 22.4 ms [User: 321.4 ms, System: 82.2 ms]
Range (min … max): 402.2 ms … 470.4 ms 10 runs
98
Reported on: https://github.com/gotcha/ipdb/issues/289
ipdb import time …
$ python --version
Python 3.12.7
$ cat import-pdb.py
import pdb
$ cat import-ipdb.py
import ipdb
$ hyperfine "python import-pdb.py"
Benchmark 1: python import-pdb.py
Time (mean ± σ): 75.4 ms ± 3.8 ms [User: 49.0 ms, System: 17.1 ms]
Range (min … max): 69.6 ms … 90.4 ms 33 runs
$ hyperfine "python import-ipdb.py"
Benchmark 1: python import-ipdb.py
Time (mean ± σ): 422.4 ms ± 22.4 ms [User: 321.4 ms, System: 82.2 ms]
Range (min … max): 402.2 ms … 470.4 ms 10 runs
99
Reported on: https://github.com/gotcha/ipdb/issues/289
And that's it… Questions? :)
Repo: github.com/pwndbg/pwndbg
Docs: https://pwndbg.re/
Sponsor us: github.com/sponsors/pwndbg
Slides: ujeb.link/pypwn
100