GDB (GNU’s DeBugger) is an open-source debugger.

Recommendation

Install pwndbg (see pwndbg setup) or gef if you intend to use GDB for CTF challenges. See Pwndbg Features

Multi-line commands

gdb has several multi-line commands. Once you finish inputting a multi-line command, use end to issue it. You can also exit out of the multiline input with Ctrl-C.

Vanilla GDB Usage

I compiled a list of commands that I have found useful.

Command-line

$ gdb /path/to/program [ /path/to/coredump ]
$ gdb -p `pidof /path/to/program`

If the program segfaults but doesn’t dump corefile, consider running ulimit -c unlimited to bypass corefile size limits, or add <your-username> - core unlimited in /etc/security/limits.conf to persist this setting.

Common Configuration

Place the following in ~/.gdbinit or /etc/gdb/gdbinit to persist across debugging sessions.

set disassembly-flavor intel # a must on x86 for my sanity
set pagination off # disable "Type <RET> for more, ..."

Place the following in .gdbearlyinit

set startup-quietly on # hide startup banner; GDB v11+

Disassembly

disass main # prints disassembly of main()
disass /m main # also prints source code if available
disass *0xdeadbeef # disassemble function at 0xdeadbeef

Breakpoints

b <*addr> # create breakpoint at address, supports symbol offset (e.g., break *(main+47))
b <symbol> # create breakpoint at symbol (e.g., break main)
b <*addr|symbol> if <condition> # conditional breakpoint (supports C expressions)
 
ctx # show current program state; runs on every bp
command hook-stop # MULTILINE: run something every time the program stops (e.g., breakpoint, nexti, stepi, etc)
 
i b # list breakpoints
en <bp> # enable breakpoint
dis <bp> # disable breakpoint
d <bp> # delete breakpoint
comm[and] <bp> # MULTILINE: enter commands to be run on <bp>
 
tb <*addr|symbol> # temporary breakpoint
rb <regex> # break at symbols matching regex
save break [file] # save bp's; recover with `source file`

Watchpoint

A watchpoint can be define to monitor the value of an expression (function calls allowed probably) and pause when the value changes.

wa <expr> # add watchpoint to stop if expr change
i b # also lists watchpoints
rw <expr> # read watchpoints: triggers when value of expression is read
rw -l <expr> # evaluates expr as an address and watches the pointed memory 

Execution

r # run program
r < file # redirect file to stdin
start # temporarily break at main and run program
ni # next instruction
si # next instruction, but step into function calls
fin # continue until after ret (step out of function)
c # continue (until breakpoint)
n # next source line
s # next source line, but step into function call
until <line-nr> # execute until given source line
<ENTER> # repeat last command (can be used repeatedly)

Reversible Debugging

Reversible debugging is a powerful feature that allows you to run the program backwards, but you must begin recording process state first.

record # enable full reversible debugging for the process
record stop
# see `help record` for partial recording options
rsi [n] # reverse-stepi, optionally n times
rni [n] # reverse-nexti, optionally n times
rc # reverse-continue
# note that breakpoints and watchpoints also work backwards!

Inspect Memory

x/[num][size][fmt] *<addr> # examine `num` of `size` at `addr`
x/10gx $rsp # prints 10 elements on stack, top to bottom
p <value> # evaluate value and print
p *arr[i]@n # print n elements starting from arr[i] (note: for multidimension arrays use arr[i][j][...])
disp <expr> # same as p, but displays value of expr on every breakpoint

For x:

  • num is 1 by default
  • size could be
    • b (bytes)
    • h (half word / two bytes)
    • w (word / four bytes - default)
    • g (giant word / eight bytes)
    • Notes
      • Note that gdb assumes the memory to be integers of the size you requested, so be aware of little endianness!
      • GDB’s word size (32-bit) does not match convention for x86 (16-bit).
      • Naturally, size does not matter when fmt is not s or i.
      • The order of size and fmt does not matter, since the sets of letters used are disjoint.
  • fmt could be
    • x (hexadecimal - default)
    • i (instruction)
    • s (null-terminated string)
    • d (signed decimal numbers)
    • u (unsigned decimal numbers)
    • See full list of format specifiers here
    • Note: Often times, gef and pwndbg’s telescope command is helpful equivalent of examining pointer-sized hex values (e.g., telescope $rsp 20 is the same as x/20gx $rsp, but also automatically and recursively dereference valid pointers for you)

Program / Memory Info

# mt / maint / maintenance
mt i sec # show all sections (current binary only)
 
# i / inf / info
i fi # show sections for all linked object files
i proc map # show all segments
i f # show stack frame
i fu [ filter ] # show functions
i args # list command-line arguments
i reg # show register contents
i var # show all global variables
i locals # show local variables
 
# gdb disable ASLR when debuggin by default
# you might want to undo this sometimes
set disable-randomization off
 
# auto-attach to chlid process when forking
set follow-fork-mode child

Backtrace

bt # print bracktrace
up <n> # in a backtrace, move to caller recursively n times
down <n> # in a backtrace, move to callee recursively n times

Find byte sequence in memory

find <start>,<end>,<string> # search literal string in given memory
find <start>,+<offset>,<string> # search from start to start+offset
find /b <start>,<end>,<hexbyte...> # search byte sequence in given memory range
# e.g. find /b 0xb7e97000,0xb7fd5000,0xff,0xe4
# you can also use p/x "string" to convert string to hex

Alternatively, to find offset of a string in a library, e.g., libc, you can use grep -Rabo /bin/sh /lib/libc.so.6.

Python

# You can interact with gdb via python
# .gdbinit also supports python
python <code> # evaluate python code
python # multi-line supported (use `end` to stop)
python print(gdb.breakpoints()[0].location)
python gdb.Breakpoint("7") # create break on line 7

Source

For debug builds (e.g., gcc -g):

l # list 10 more source lines
l - # list previous 10 source lines

TUI

  • Use Ctrl-X A to enter TUI mode. The default view is source code.
  • Use Ctrl-L to rerender the view (e.g., in case some printf statements from the debugged binary mess up the UI).
  • Use Ctrl-X 2 to add a second window.
  • Use the same keybind to switch between views (e.g., registers, source code, etc).
  • tui reg float to show floating registers
  • Ctrl-<P/N> to view previous commands (up and down arrows now scroll the source code view)