Before we go too far, let’s handle some basic setup. Everything after this lesson assumes the settings below, so the output you see will match the output printed here.
Out of the box GDB has no command history between sessions, prints structures as one unreadable line, stops every twenty lines to ask if you want to see more, and shows you AT&T assembly syntax. All four are one line each to fix.
GDB settings
Put this in ~/.gdbinit:
# Remember what I typed, across sessions.
set history save on
set history size 10000
set history filename ~/.gdb_history
# Intel syntax. Matches Ghidra, IDA, and every exploit write-up you will read.
set disassembly-flavor intel
# Print structures across multiple lines instead of one 400-column smear.
set print pretty on
# Follow pointers one level when printing a struct that contains them.
set print object on
# Stop asking "---Type <return> to continue---" every screenful.
set pagination off
# Stop asking "are you sure" for things I clearly meant.
set confirm off
# Show 8 disassembled instructions around the stop, always.
set disassemble-next-line on
What is disassembly flavour?. Everything else in the reverse-engineering world
speaks Intel syntax as god intended. With intel syntax the destination comes
first: mov rbp, rsp. GDB defaults to AT&T, where the destination comes
last: mov %rsp, %rbp. Reading one while writing the other will cost you an
hour eventually.
$ gdb -q ./hello
Reading symbols from ./hello...
(gdb) disassemble check
Dump of assembler code for function check:
0x0000000000401176 <+0>: endbr64
0x000000000040117a <+4>: push rbp
0x000000000040117b <+5>: mov rbp,rsp
0x000000000040117e <+8>: sub rsp,0x10
That is with set disassembly-flavor intel. Without it the third line reads
mov %rsp,%rbp, which means the same thing backwards.
Per-project config
GDB also reads .gdbinit from the current directory. That is where
project-specific setup belongs, such as the breakpoints you always want and the
pretty printers for your own types:
file ./hello
break check
run CONTEXT
Modern GDB refuses to auto-load a .gdbinit from a directory you do not own,
because otherwise cloning a repository would be enough to run arbitrary commands
on your machine. When it declines, it tells you exactly what to add:
add-auto-load-safe-path /home/you/projects
Add the paths you trust. Do not set set auto-load safe-path / and forget about
it. That is the setting whose absence is protecting you.
The TUI
GDB has a built-in split-screen mode. Ctrl-X A toggles it, or start with
gdb -tui.
(gdb) layout src # source over command line
(gdb) layout asm # disassembly instead
(gdb) layout split # both at once
(gdb) layout regs # add a register pane
(gdb) focus cmd # arrow keys go to the prompt again
The TUI is useful and mildly cursed. It redraws badly over slow SSH, and any
program that writes to the terminal will scribble on your panes. Ctrl-L
repaints. If it gets stuck, tui disable gets you back.
Keep that annoyance in mind. There is a better answer than the TUI, and we get to it in the last lesson.
One command worth knowing early
start is break main plus run in a single word, which is how most sessions
actually begin.
That is stock GDB configured about as well as it goes.
Next: what break actually did to your program.