summary
GDB debugger - info and some basics
a well-known debugger for Linux programs that should come pre-installed
use it to manipulate and inspect binaries and executables
if you do need to install:sudo apt-get install gdb
enhancements:
set intel as the default assmebly flavor:echo 'set disassembly-flavor intel' >> ~/.gdbinit
install gef plugin to enable view of registers and stack at each break, plus extra commands:
$ wget -O ~/.gdbinit-gef.py -q https://gef.blah.cat/py
$ echo source ~/.gdbinit-gef.py >> ~/.gdbinit
info functions - list functionsinfo variables - list variableinfo registers - list registersdisas main - disassemble a functionrun - run programstart - run program but break on entry
functions - list and callinfo function <name>- info on a specific functioncall function - may require return type and args:call (char) function(arg)
breakpoints - use breakpoints to stop execution and inspect or set valuesbreak *0x4551ad - asterisk before address to specify memory address, not a function or line numberbreak main+20break puts()break fcnName
c - continue executions - step to next line of code or functionsi - step inside current functionni - next instruction
examine:x/s 0x402a4 - examine string at that address
set values (such as a register):set $rip = 0x438457 - set instruction pointer to an address
Intro and Installation
GNU Debugger (GDB)
To debug our binaries, we will be using a well-known debugger for Linux programs called GNU Debugger (GDB). There are other similar debuggers for Linux, like Radare and Hopper, and for Windows, like Immunity Debugger and WinGDB. There are also powerful debuggers available for many platforms, like IDA Pro and EDB. In this module, we will be using GDB. It is the most reliable for Linux binaries since it is built and maintained directly by GNU, which gives it an excellent integration with the Linux system and its components.
Install gdb
GDB is installed in many Linux distributions, and it is also installed by default in Parrot OS and PwnBox. In case it is not installed in your VM, you can use apt to install it with the following commands:
$ sudo apt-get update
$ sudo apt-get install gdb
set intel disassembly flavor in gdb init$ echo 'set disassembly-flavor intel' >> ~/.gdbinit
install gef plugin
One of the great features of GDB is its support for third-party plugins. An excellent plugin that is well maintained and has good documentation is GEF. GEF is a free and open-source GDB plugin that is built precisely for reverse engineering and binary exploitation. T
To add GEF to GDB, use the following commands:
$ wget -O ~/.gdbinit-gef.py -q https://gef.blah.cat/py
$ echo source ~/.gdbinit-gef.py >> ~/.gdbinit
Getting Started
Now that we have both tools installed, we can run gdb to debug our HelloWorld binary using the following commands, and GEF will be loaded automatically:
$ gdb -q ./helloWorld
gef➤
As we can see from gef➤, GEF is loaded when GDB is run. If you ever run into any issues with GEF, you can consult with the GEF Documentation, and you will likely find a solution.
Going forward, we will frequently be assembling and linking our assembly code and then running it with gdb. To do so quickly, we can use the assembler.sh script we wrote in the previous section with the -g flag. It will assemble and link the code, and then run it with gdb, as follows:
GNU Debugger (GDB)
fluffyfluffyhead@htb[/htb]$ ./assembler.sh helloWorld.s -g
...SNIP...
gef➤
Info
Once GDB is started, we can use the info command to view general information about the program, like its functions or variables.
Tip: If we want to understand how any command runs within GDB, we can use the help CMD command to get its documentation. For example, we can try executing help info
Functions
To start, use the info command to check which functions are defined within the binary:
`(gdb) info functions
Variables
use the info variables command to view all available variables within the program:
` info variables
Disassemble
To view the instructions within a specific function, we can use the disassemble or disas shortcut along with the function name, as follows:
` gdb➤ disas main
info breakpointsinfo functions - list functions
functions
info function <name> - info on a specific functioncall function - may require return type and args:call (char) function(arg)
breakpoints
break *0x4551ad - asterisk before address to specify memory address, not a function or line numberdisable, enable, delete - breakpoint options
conditional breakpoints
examples:b loopFib if $rbx > 10b *loopFib+9 if $rbx . 10b *401012 if $rbx > 10
r, runset args <arg>
x = examinex/FMT ADDRESS = examine, format ( x, s, i ) , address
format can be : x hex, s string, i instruction
format can take 3 characters as arguments:
count: , starting at address
format: x,s,i x hex, s string, i instruction
size: b(byte), h(halfword), w(word), g(giant, 8 bytes)
Note: if we don't specify the Size or Format, it will default to the last one we used.
print the value of the IP ( Instruction Pointer ) register:
x $rip
print the value of the next 4 instructions:
x/4ig $rip
examine string at 0x40200:
x/s 0x40200
print...p/d $rbx - print rbx as a decimal
si - step into function calls, assembly instructins one by ones / step - until next line of code or functionni - step over, stay in current (main) functionhandle SIGILL nostop noprint pass
continue - run until next break pointinfo registers to see addresses and values
set the instruction pointer
set $rip=0xaddr - set the instruction pointer to a spot you want to run next
.rodata- read only data - get from objdump -x - all headers
stepping through errors:
handle SIGILL nostop noprint pass handle SIGSEGV nostop noprint pass
setting and modifying
set in gdbpatch in GEF
patch string 0x402000 "Patched!\\x0aset $rdx=0x9 = the space allocated in rdx for the original string was 18, but our patch is only 9, so we change rdx to the size of our string
address randomization
set disable-randomization on
your executable is a Position-Independent Executable (PIE) with ASLR (Address Space Layout Randomization) enabled. Here's why this happens:
## 1. PIE Behavior
- At disassembly time (before running), GDB shows relative offsets from the link-time base address (typically
0x0000000000001000for modern Linux PIEs)
So if:
Original disassembly offset: 0x115d
Actual runtime base address: 0x555555554000
Final runtime address: 0x555555554000 + 0x115d = 0x55555555515d
Why GDB Shows Different Addresses
- Pre-run disassembly: Displays link-time addresses (
0x115din your case) - Runtime addresses: Show actual loaded addresses (
0x55555555515d)
Check if your executable is PIE:
`file /home/kali/assemblyLearn/fileName
-- Output should include: "ELF 64-bit LSB pie executable"
- Disable ASLR for stable addresses during debugging:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space- OR
(gdb) set diasable-randomization on
to get addressing consistent with disassembly, run the program before disassembling
break main
run
disassemble main
info functions fg --> list functions, look for 'fg'
show registers - list the value of the registers
display/i $eip # Show current instruction
display $eax # Show EAX every step
display $ebx # You can do others like $ecx, $edx...
gef
registers - print out current value of all registers
Auto-import radare2’s function list into GDB (quick & dirty)
Have r2 analyze and emit a little GDB script that matches r2's functions analysis and names the addresses accordingly
# Generate a gdb script with "set $fcn_..." lines
r2 -e scr.color=0 -Aqc 'afl' ./stone \
| sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g' \
| awk '{
addr=$1; name=$NF;
sub(/^sym\./,"",name);
gsub(/[^A-Za-z0-9_]/,"_",name);
printf "set $%s = %s\n", name, addr
}' > r2syms.gdb
# Then in GDB:
source r2syms.gdb
set disable randomization on
b *$fcn_004006ee ( or functions per r2 list )
Note: We replace
.with_because GDB convenience vars can’t contain dots.