This page documents some tips on how to use GDB for advanced debugging. It was created as all information I could find on the web lacked some useful information.
Assembly Debugging
Starting Out
When debugging assembly in gdb, it’s useful to learn about TUI mode. TUI mode gives gdb text based windows. This allows you to see both the disassembled coded as well as the ability to issue commands at the same time – a must have for debugging.
To enter into TUI mode use:
CTRL-X CTRL-1
This will create a single window. (To get rid of it use
CTRL-X a
Initially this window will not be in the right layout. (In gdb TUI mode, a layout is like a mode, ie assembler mode, etc).
You can tell gdb to change to assembly mode using:
layout asm
Now you should be able to see your program in a disassembled text window.
- Tip: If you want to see registers as well use:
layout regs
Common Tasks
In assembly debugging, theres a few common tasks that you need to these are listed below:
View registers
To view registers you can either use:
info all-registers | info reg | i r
to view the all/some of the registers. You can use:
layout reg
to bring up a TUI window with all the registers. This window also shows what changes between instructions when your executing them.
To examine the current value of a register you can use:
info reg es
You can examine the memory the register points to using the x instruction ie:
x/x $esp # View mem in hex x/b $esp # View mem in bin x/i $esp # View instruction at address
You can set a register using:
set $eip = 0x90 # Set current eip to address 0x90
Memory Addresses
If you recall the early lessons in computing there’s two main memory regions in a program – the code segment and the data segment.
The code segment normally consists of executable instructions and static things like constants and strings. You can view the instructions at a memory address using:
disassemble 0x123123
where 0x123123 is an address in memory. This will show you a number of instructions at that address and onwards till the end of the function ends.
You can examine a memory address using
x 0x123123
The x command takes a number of different parameters/options. It can output binary, hex, strings, instructions etc. For more help on how to use it try: help x
in gdb.
You can also print out the value of an address using:
p 0x123123
like x the p (or print) command the p command takes a number of options for displaying hex, binary etc.
Altering program execution
Say you’ve been debugging a program in assembly. You’ve setup break points, either to memory or functions. Now you’ve found the instruction you want to change. There’s a number of ways you can change it. Chances are the instruction is some type of compare, (cmp, cmpl, jge, jmp, etc). You could possible make the instruction a no instruction (nop) or modify the register the compare relies on to be something else.
In order to alter the line to be a nop you can do something like:
set *0xbxxxx = 0x90
In this case the address is 0xbxxxx and the instruction (under x86 arch) is 0x90 (nop). However this could cause an issue if the previous instruction was 2 bytes wide. ie:
1> 0x126c : incl 0xfffff3dc(%ebp) 2> 0x126d : testb %al,%al 3> 0x126f : jne 0x125c
In the above the instruction at line 1 is a 1 byte instruction. The instruction at line 2 is a two byte instruction. How? Looks at the difference between the addresses:
0x126d - 0x126c = 1 byte 0x126f - 0x126d = 2 byte
In x86 arch, 0x90 (nop) is only a 1 byte instruction.
If you wanted to modify line 2, you’ld have to set the al register so the test would succeed.
Managing Signals
You can enable/disable signals using the ‘handle‘ command. ie:
handle SIGALARM stop
The options to handle are:
nostop
- GDB should not stop your program when this signal happens. It may still print a message telling you that the signal has come in.
stop
- GDB should stop your program when this signal happens. This implies the
print
keyword as well. print
- GDB should print a message when this signal happens.
noprint
- GDB should not mention the occurrence of the signal at all. This implies the
nostop
keyword as well. pass
- GDB should allow your program to see this signal; your program can handle the signal, or else it may terminate if the signal is fatal and not handled.
nopass
- GDB should not allow your program to see this signal.
And to see what signals are currently set and their default setup use:
info signals