GDB is a powerful and extensively used tool to analyze C programs. A growing list of tips that are most commonly used and useful:

Objects (Memory, Frame, and Variables) Monitoring

print command

  • print <var> or p <var> to monitor the value of <var>
  • p &<var> to monitor the address of <var>
  • p *<addr> to print the value at the address addr

x command

x can be used to examine byte-addressable memory details

  • x /4bc ($esp-4) Display 4 bytes as chars starting at memory address $esp-4
  • x /16bx $ebp Display 16 bytes in hex starting from EBP
  • x /2wx buffer Display 2 word (8 bytes) in hex starting at address of buffer, one could tell the endianess from the display — little-endian architectures (e.g., x86) store the most-significant byte of a multi-byte object at the highest memory address, vice versa
  • x /s <addr> Display in string format

For the example function below, after b func and r, use x /3wx $ebp to observe (from lower memory address to higher) the saved previous FP (SFP, the FP of the frame calling the function), the return address (RET) saving the instruction pointer (IP, pointing to the next instruction after the function return), and the arguments. One could also use x /6wx $esp to observe the contents starting from the top of the stack (lowest memory address), i.e., buffer2, buffer1 contents and so on. One could potentially leverage buffer2 overflow to overwrite RET, buffer1 (disable ASLR for deterministic addresses with sudo sysctl -w kernel.randomize_va_space=0).

void func(char* str) {
   char buffer1[3] = "12";
   char buffer2[8];
   strcpy(buffer2, str);
}

info command

  • info locals to view the local variables of the current frame
  • info args to view the arguments of the current frame
  • info frame [addr] to view the metadata of the current/specified frame
  • info reg [reg name] to examine the values (in hex and decimal, exceptions for esp, ebp since for addressing, decimal values are not that useful) of all/specified CPU registers

Source Code Display

  • list shows the next 10 lines of source code
  • list - shows the previous 10 lines of source code
  • list <start> <end> shows lines from line start to end
  • ctrl-x a to turn on/off current source code instruction diaplay

Execution Environment

  • show env to print all environment variables
  • unset env var to remove env var from the environment
  • set env var string to set environment variable var
  • info signals to show all signals and its GDB action
  • handle <signal> <GDB action> to change the default GDB action for the target signal

Execution Flow

  • break [<file name>:]<line number> or b [<file name>:]<func name>
  • info break to show the current breakpoint(s)
  • clear to remove all breakpoints
  • disable [break point index] to disable all/specified breakpoint(s) and enable [break point index] is the reverse
  • step [count] or s [count] to execute next line(s)
  • stepi [count] or si [count] to step by machine instructions rather than source code lines

Assembly

  • disassemble <func name> or disas <func name> to display the assembly instructions (x86 Assembly Guide for a brief tutorial on 32-bit x86 assembly with AT&T syntax, a more comprehensive version is x86 Assembly Language Reference Manual) and memory address, one could combine it with stepi to check the effects (values of registers and variables) for each instruction. One could also observe the function prologue and epilogue in assembly.
  • disassemble [/r] [Address],+[Length] to disassemble instructions from address to address+length, /r is to display optinally the raw bytes of the instructions. For example, one could verify the presence of the inserted shellcode (e.g., as mentioned in Smashing The Stack For Fun And Profit) to spawn a root shell and redirect the execution flow to the address with shellcode instructions.

The code block belows shows an example of x86-32 instructions to make a setuid syscall via software interrupt (int $0x80). One could then compile it via as <file> and objdump -d <file> to obtain the opcodes used as the shellcode payload. Registers involved to specify the a syscall in general: EAX stores the syscall number (platform dependent system call table) and returned value after a syscall, EBX the first argument (here 0 for root privillege), ECX for 2nd arg, EDX for 3rd, then ESI, EDI, and EBP, if more args, EBX stores the memory address for the list of arguments).

.section .text
	_start:
	xor %eax, %eax
	xor %ebx, %ebx
	mov $0x17,%al
	int $0x80

GDB Confiugration

  • set listsize <number> to set number of lines shown by list command and show listsize to view the current value
  • source <script path> to execute GDB commands in the script file