GDB Guides Series:

  1. GDB Guide Part 1 - Basics
  2. GDB Guide Part 2 - Breakpoints & Linux Calling Conventions
  3. GDB Guide Part 3 - Process Maps
  4. GDB Guide Part 4 - Examining Memory
  5. GDB Guide Part 5 - Stepping
  6. GDB Guide Part 6 - Automation
  7. GDB Guide Part 7 - Custom Commands
  8. GDB Guide Part 8 - Invoking Function Calls

1. Introduction

Part 1 of this series is meant as an introduction to GDB for beginners. As I progress through this series, I will be sharing more advanced tips and even provide a nice template in writing your own custom GDB commands using python!

2. Overview

Today’s guide will show you how to:

  1. Attach to a Process
  2. Set useful GDB Configuration
  3. Inspect Registers
  4. Disassemble Functions

I will be providing an example C file here for you to compile so that you may follow the walkthrough and deepen your understanding.

To compile test.c:

$ gcc test.c -o test
Compiling Test Program

3. Attaching to a Process

There are two ways to go about doing this. The first which is the simplest, is to run GDB with the binary path as its parameter. The second way to do this is to attach to an already running process via its PID.

3.1 Via Binary

If you have chosen this method, it means that you know the binary full path and also the parameters to run the binary.

$ gdb $BINARY_PATH
Attaching before Process Begins

Afterwards, run the binary with its parameters.

(gdb) r $param1 $param2 ....
Running with Parameters

3.2 Via PID

Alternatively, you can also attach to a running process via its PID in several ways as well.

  1. You can start either GDB first, then do attach $PID.

    $ gdb
    (gdb) attach $pid
    
    Attaching via PID - Method 1

  2. OR, you can start GDB with the -p parameter.

    $ gdb -p `pidof $PROC_NAME`
    
    Attaching via PID - Method 2

3.3 Test Example

I have created a video for those who are more visual learners, using a compiled test.c as an example for GDB to attach to.

Attaching to Test Program - Short Demo

4. Useful GDB Configurations

I would recommend the following configurations right from the get-go when doing dynamic analysis.

  1. set pagination off

    • By default, this is set to “on” and it is really annoying when you are dealing with large outputs, prompting you to press Enter in order to continue.
  2. set logging enabled on

    • By default, this is set to “off”. This is very useful especially when you want to look back at the commands you ran and the outputs you had. If the file path is not stated, it will be written to gdb.txt. See here for more information on GDB logging.
  3. set confirm off

    • By default, this is set to “on” as GDB is very cautious. When automating dynamic analysis with a GDB script, this will break your automation. I will recommend turning this off only when you need to automate things.
  4. set print pretty on

    • By default, this is set to “off”. This is more of a personal preference for me. This is more for you to decide whether you want your structure to be printed in one line or in a more readable form.
      # set print pretty on
      $1 = {
      next = 0x0,
      flags = {
          sweet = 1,
          sour = 1
      },
      meat = 0x54 "Pork"
      }
      
      # set print pretty off
      $1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"}
      
      Pretty Print On/Off

  5. set output-radix 16

    • By default, output-radix is set to 10. This would print out numbers in GDB as hexadecimal. I’m hard-wired to read hexadecimals when debugging stuff, so this is a must for me.
  6. set disassembly-flavor intel

    • By default disassembly-flavor is set to “att”. However, I prefer the “intel” style disassembly despite specializing in Linux exploitation 🤡.
  7. set print elements 0

    • By default, the limit is set to 200 characters. I will usually set this to 0 which means that printing is unlimited. This is especially useful whenever you are trying to print a very large string from memory.

5. Inspecting Registers

There are actually several ways to print the values of the registers.

5.1 Via info registers

# Prints all registers' values
(gdb) info registers

# Print only rax value
(gdb) info registers rax

# Print an array of registers
(gdb) info registers rdi rsi rdx rcx 
GDB command - info registers

5.2 Via print / printf

# Print rax value
(gdb) print $rax

# Printf rax value
(gdb) printf "rax = %d\n", $rax

# Printf rdi, rsi, rdx, rcx
(gdb) printf "rdi = %d\nrsi = %d\nrdx = %d\nrcx = %d\n", $rdi, $rsi, $rdx, $rcx
GDB command - print / printf

5.3 Test Example

Showing Registers’ Values - Short Demo

6. Looking at Disassembly

Looking at disassembly provides us more information such as offsets and instruction sets. Of course, you need not use GDB’s disassembly feature for static analysis and can always use Ghidra or IDA (Free Version or Pro) which is a lot more user-friendly.

Assuming we can only use GDB, then the way to go about doing this is to make use of the disassemble command.

# If no address/function is stated, disassemble function at current instruction.
(gdb) disassemble

# Disassemble the entire function
(gdb) disassemble [Function]

# Disassemble the entire function that contain the given address
(gdb) disassemble [Address]

# Disassemble instruction between start and end address. 
(gdb) disassemble [Start],[End]

# Disassemble from start of function to address of function + offset.
(gdb) disassemble [Function],+[Length]

# Disassmble from address to address + offset
(gdb) disassemble [Address],+[Length]

# Show the current instruction (rip)
(gdb) disassemble /m [...]

# Show the byte values of all instructions
(gdb) disassemble /r [...]
GDB command - disassemble

6.1 Test Example

Viewing Disassembly - Short Demo

7. Conclusion

I figured this is a good stopping point and of course, we are not yet done! I barely scratched the surface just for the basics! Stay tuned for part 2, where I will teach you more about Linux calling conventions, setting breakpoints etc.

8. References

  1. GDB Manual: https://sourceware.org/gdb/current/onlinedocs/gdb.html/

9. Resources

  1. test.c

GDB Guides Series:

  1. GDB Guide Part 1 - Basics
  2. GDB Guide Part 2 - Breakpoints & Linux Calling Conventions
  3. GDB Guide Part 3 - Process Maps
  4. GDB Guide Part 4 - Examining Memory
  5. GDB Guide Part 5 - Stepping
  6. GDB Guide Part 6 - Automation
  7. GDB Guide Part 7 - Custom Commands
  8. GDB Guide Part 8 - Invoking Function Calls