0% found this document useful (0 votes)
13 views20 pages

GDB Debugging Techniques for C Programs

The document provides a comprehensive guide on using GCC and GDB for compiling and debugging C programs. It covers essential commands for compiling code, setting breakpoints, stepping through programs, inspecting variables, and displaying memory content. Additionally, it includes challenges that require understanding of GDB scripting to extract random values and navigate through program execution.

Uploaded by

amenomar408
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views20 pages

GDB Debugging Techniques for C Programs

The document provides a comprehensive guide on using GCC and GDB for compiling and debugging C programs. It covers essential commands for compiling code, setting breakpoints, stepping through programs, inspecting variables, and displaying memory content. Additionally, it includes challenges that require understanding of GDB scripting to extract random values and navigate through program execution.

Uploaded by

amenomar408
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Reverse Engineering

Debugging Refresher Challenges

1
GCC vs. GDB
// test.c
• GCC is the C and C++ compiler developed by GNU project. It is
#include <stdio.h>
widely adopted as the default compiler of UNIX-like systems.
#include <stdlib.h>
• Assume that we have a C source file “test.c" as shown. The basic float division (int x, int y){
way of compiling the code is: return x/y;
# gcc -o test test.c }
int main(int argc, char **argv) {
• Also, for debugging the code add the ‘-g’ option when compiling int a, b;
• If the program is compiled without errors, you can execute the float c;
program by typing a = atoi(argv[1]);
b = atoi(argv[2]);
# ./test c = division (a,b);
• If you are interested about how the assembly code look like, you printf("%d/%d=%f\n",a,b,c);
can also generate the assembly code by replacing the "-o" option return 0;
with "-S" as: }

# gcc -S test.c
2
GCC vs. GDB
• Gdb is a debugger by GNU project, It can step through your source
code line-by-line or even instruction by instruction.
• You may also watch the value of any variable at run-time.
• In addition, it also helps to identify the place and the reason making
the program crash.
# gdb executable
• Run will start the program running under gdb

3
Add Breakpoint
• Add a breakpoint is a spot in your program where you to try to find out
where the program is crashing, etc.
• To set a breakpoint you use the break command break *<address>
• You can break function sets the breakpoint at the beginning of function.
# (gdb) break *main
• Type run a gain it will stop in break point.
# (gdb) info break
• To get information about the breakpoints in your code
• To delete breakpoint, get its number first then use the following command:
#(gdb) delete break (number of breakpoint)
4
Stepping through program
• Once a running program is interrupted in gdb, we can step the
program to inspect how the program is executed.
• si: short of stepi <n> the debugger will step to the next instruction in
the compiled code.
• ni: short of nexti <n> the debugger will step to the next source line.
Each function call will be treated as a single source code line
• C: for continue, which will continue execution until the program hits a
• breakpoint.
• Finish: command to finish the currently executing function.

5
Inspect variables/register value
• If we are interested about all the register values, we can use:
# info registers
• Alternatively, you can also just print a particular register's value with
the `print` command, or `p` for short.
• For example,
• p $rdi will print the value of $rdi in decimal.
• p/x $rdi will also print it's value in hex with

6
Show Memory Content
• You can examine the contents of memory using the command: x/<n><u><f> <address>
• Where <u> is the unit size to display, <f> is the format to display it in, and <n> is the
number of elements to display.
• Unit sizes can be `b` (1 byte), `h` (2 bytes), `w` (4 bytes), and `g` (8 bytes).
• Valid formats are `d` (decimal), `x` (hexadecimal), `s` (string) and `i` (instruction).
• The address can be specified using a register name, symbol name, or absolute address.
Additionally, you can supply mathematical expressions when specifying the address.
• For example,
• x/8i $rip will print the next 8 instructions from the current instruction pointer.
• x/16i main will print the first 16 instructions of main.
• x/16gx $rsp will print the first 16 values on the stack
• x/gx $rbp-0x32 will print the local variable stored there on the stack.

7
Display values
• While stepping through a program, you may find it useful to have some
values always displayed to you.
• There are multiple ways to do this:
• The simplest way is to use the `display/<n><u><f>` parameterized
command
• For example,
• display/8i $rip will always show you the next 8 instructions.
• display/4gx $rsp will always show you the first 4 values on the stack.
• Another option is to use the `layout regs` command.
• This will put gdb into its TUI mode and show you the contents of all of the
registers, as well as nearby instructions.

8
Display code
• You can also use `disassemble main`, or `disas main` for short, to print
all of the instructions of main
• You can do that with the command `set disassembly-flavor intel` for
the CORRECT assembly syntax

9
Use the command continue, or c for short, to
Level 1 continue program execution.

• Before we do anything else we need to open the file in GDB.


• # gdb embryogdb_level1
• This challenge is fairly simple, we just have to run the file.
• (gdb) run
• The program will hit a breakpoint at the main function.
• We can continue the execution and get the flag.
• (gdb) continue

10
To solve this level, you must figure out the current
Level 2 random value of register r12 in hex.

• We can find the value of the r12 register using the p command which
is a short from of print.
• (gdb) p/x $r12
• Then hit c to continue and paste value in random value to get the flag

11
To solve this level, you must figure out the random
Level 3 value on the stack (the value read in from
/dev/urandom). Think about what the arguments to
the read system call are.
• Read syscall
• size_t read(int fd, void buf[.count],
size_t count);
• We can see that the second argument
is the location of the buffer in which
the data is to be read.
• This argument is loaded in the rsi
register. Let's look at how this loaded in
our assembly code.
• If we look at the address main+423, we
can see that the value of rsi is being
copied from rax.

12
Level 3
• This value in rax is set to rbp-0x18 as seen in the instruction at main+414 address.
• Now that we know the location of the buffer is rbp-0x18, we can now check the data
copied there using the x command.
• The format is set to hexadecimal using x and the unit size is set to giga word using g.
# /challenge/embryogdb_level3
# run
#c
# set disassembly-flavor intel
# disassemble main
# x/gx $rbp-0x18
#c
# paste code and get flag

13
Level 4
• To solve this level, you must figure out a series of random values
which will be placed on the stack.
• You are highly encouraged to try using combinations of stepi, nexti,
break, continue, and finish to make sure you have a good internal
understanding of these commands.
• The commands are all critical to navigating a program's execution.

14
Level 4 -solution
• The program takes user input and then
compares it with a value.
• In order to correctly provide user input, we
need to know what it is being compared with
before the comparison even happens.
• Let's begin by disassembling the main
function.
• We can see that the instruction at main+626
compares the value of the rax register with
the value of the rdx register.
• If the values are equal, it skips over the exit
syscall.

15
Level 4 -solution
• So rdx is the register that holds user input
and rax is the one that holds the value it is
to be compared to. We also know that the
value in rax is copied from rbp-0x18 by
looking at the instruction at main+622.
• We know that the read syscall reads data
into a buffer pointed to by it's second
argument. We also know that this second
argument is loaded into the rsi register.

16
Level 4 -solution
• In this case the buffer is located at rbp-0x18 as shown by the instruction at
main+498.
• All we must do now is to set a breakpoint at the instruction after the read
syscall is made.
• break *(main+517)
• Once our program has stopped at the breakpoint, we can check the data
that was copied into the buffer.
• (gdb) x/gx $rbp-0x18
• Next continue the program execution until we are asked for the user input.
• We can see that the check was successfully passed. This process will repeat
a couple of times but the method will be the same.
17
Gdb scripting
• To interact with gdb very quickly you can write your commands to
some file, for example `[Link]`
• To run gdb with this script use command start
• gdb -x <PATH_TO_SCRIPT> ./<path_to_executable_you_need_to break *main+42
test> commands
• This file will execute all the gdb commands after gdb launches. x/gx $rbp-0x32
• Another way is to load source command while gdb is running continue
• (gdb) source [-s] [-v] <command_file_name> end
• Consider the following gdb script: continue
• In this case, whenever we hit the instruction at `main+42`, we will
output a particular local variable and then continue execution.

18
Gdb scripting
• Another advanced script
start
• In this case, the silent indicates that we want gdb break *main+42
to not report that we have hit a breakpoint, to commands
silent
make the output a bit cleaner. set $local_variable = *(unsigned
• We use the set command to define a variable long long*)($rbp-0x32)
within our gdb session, whose value is our local printf "Current value: %llx\n",
$local_variable
variable. continue
• Finally, we output the current value using a end
continue
formatted string.

19
Level 5
• Use gdb scripting to help you //my_scripy.gdb2020
collect the random values start
like in level 4. break *main+709
commands
silent
set $currentValue = *(unsigned long
long*)($rbp-0x18)
printf "Current value: %llx\n", $currentValue
continue
end
continue

20

You might also like