Stepping and Continuing
Breakpoints, Watchpoints, Stack Traces, oh my!
This section is known as dynamic analysis. Dynamic analysis is actively running the binary and observing its behavior. This is done by watching the registers, the stack, and the instructions as they are executed.
Using Breakpoints
Breakpoints are how we can pause execution to analyze the binary. We can set a breakpoint using the break
command (b
for short). It takes a secondary argument of the address to break at. You have to dereference the address; otherwise, gdb
will assume you are attempting to name a function.
Moreover, we can also set breakpoints at the start of specific functions:
Or a function plus an offset. Since a function plus an offset returns an address, we must dereference it.
The Breakpoint List
You can use info breakpoints
(info break
for short) to see the current breakpoints set.
We need this menu to get the breakpoint number for each breakpoint. We use this to disable or delete breakpoints.
Disabling Breakpoints
We can enable or disable breakpoints based on their number. This is useful to temporarily disable a breakpoint without deleting it.
Breakpoints are enabled by default.
Deleting Breakpoints
We can use this menu to delete breakpoints by their number. We can use delete
(or d
for short).
Setting Watchpoints
A watchpoint is a breakpoint triggered when a specific memory address is accessed. This is useful for debugging when a specific memory address is accessed.
You can set a watchpoint to watch a register:
You can also watch an instruction or a hardcoded location:
The Watchpoint List
You can use info watchpoints
(info watch
for short) to see the current watchpoints set.
Running the Binary
You can run the binary using the run
command (r
for short). Without any breakpoints set, this doesn't do much:
However, if we set a breakpoint, we can see the binary pause at the breakpoint:
This output is displayed because we installed GEF. If you did not install GEF, this output does not preview at runtime. This is one of the major benefits of GEF and why I recommend it.
The binary is now running. GEF provides us with information on the essential registers, the stack frame, the instruction set, and the call stack.
Stepping
There are two kinds of steps when using a debugger: stepping in and stepping over.
Step In: This method steps into any called functions and pauses at the first instruction. This allows you to walk through called functions.
Step Over: This method steps over a function and immediately executes all its contents. This is useful for library functions where their instructions aren't important.
To step in, use the si
instruction. To step over, use the ni
instruction. You can use ni <num>
(or si <num>
) to step that many instructions. gdb
also remembers your most recent instruction, so pressing Enter will repeat your last instruction.
For the current binary, running the following will get us into read_in
:
Continuing
You can use the continue
command (c
for short) to continue execution until the next breakpoint or watchpoint is hit.
You can use the finish
command to continue running until the current function returns.
The until
(u
for short) command continues execution until the program counter is greater than the jump address. This is useful for stepping over loops.
The advance
function continues running until a specified location. This is the same as using a breakpoint without having to permanently set a breakpoint.
Stack Traces
A stack trace is a list of all the functions that have been called up to this point. This is useful for debugging and understanding the flow of the program.
To view the stack trace, use the backtrace
command (bt
for short).
I stepped into a random location to show many stack frames. This comes from an STL function calling other STL functions inside the ld
file.
GEF also previews this stack trace:
Moving Up and Down the Stack
You can use up
and down
to move up and down the stack trace. This is useful for viewing the contents of the stack frame. These take optional arguments of how many frames to move up or down.
Running Backwards
You can run programs in reverse order to better understand how to reach a certain location in a binary. Stepping backward works slightly differently than stepping forward.
Use reverse-stepi
(rsi
for short) to reverse-executive one machine instruction. This is the same as stepping backward (and in).
Use reverse-nexti
(rni
for short) to reverse-executive a single instruction in reverse. Called functions are un-executed automatically.
reverse-continue
(rc
for short) will continue execution in reverse until the next breakpoint or watchpoint is hit.
reverse-finish
takes you to where the current function was called (the top of the function).
Debugging Forks
gdb
must be instructed on how to follow forks. Since a fork creates a separate process, you must tell gdb
how to respond.
Use set follow-fork-mode
to set how you want to follow. You can choose to follow the parent
or the child
.
Use show follow-fork-mode
to show the active mode.
gdb
can also detach a process after a fork to retain debugger control over both. The mode can be on
(the follow-fork-mode
process will be detached and will run independently) or off
(both processes are held under gdb
, one suspended while debugging the other).
Again, show detach-on-fork
shows the current mode.
Finally, follow-exec-mode
sets the debugger's response to an exec
call. The mode can be new
(gdb
creates an inferior process and rebinds to it) or same
(gdb
continues to debug the same process).
show follow-exec-mode
shows the current mode.
Last updated