# Load binary gdb ./challenge gdb -q ./challenge # quiet (no banner) gdb -q -ex "run" ./challenge # run immediately # With arguments gdb --args ./challenge arg1 arg2 # Attach to running process gdb -p <pid> gdb ./challenge <pid> # Load core dump gdb ./challenge core # Inside GDB: load file (gdb) file ./challenge (gdb) core core.dump # Intel syntax (default is AT&T) (gdb) set disassembly-flavor intel # Put in ~/.gdbinit to make permanent
# ~/.gdbinit — sensible defaults set disassembly-flavor intel set pagination off set confirm off # Auto-load pwndbg source ~/pwndbg/gdbinit.py # Or peda source ~/peda/peda.py # Or GEF source ~/gef.py # Install pwndbg git clone https://github.com/pwndbg/pwndbg cd pwndbg && ./setup.sh # Install GEF (one-liner) bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
# By name b main b strcmp b win_function # By address b *0x401234 b *main+42 # offset from symbol # By file:line (if debug info) b challenge.c:42 # Conditional breakpoint b *0x401234 if $rdi == 0 b loop_func if i == 100 # Temporary (auto-delete after hit) tbreak main # Hardware breakpoint (for ROM/protected memory) hbreak *0x401234 # Manage info break # list all disable 2 # disable bp #2 enable 2 delete 2 # delete bp #2 delete # delete all clear main # clear by location
# Watchpoint: break when memory changes watch *0x404060 # break on write rwatch *0x404060 # break on read awatch *0x404060 # break on read or write watch variable # watch local variable # Catchpoints: catch events catch syscall # any syscall catch syscall read # specific syscall catch syscall 0 # by number catch exec # catch exec() catch fork # catch fork() catch throw # C++ exception # Commands on breakpoint hit commands 1 silent printf "rdi=%p\n", $rdi continue end
# Start / restart r # run r arg1 arg2 # run with args r < input.txt # run with stdin from file r <<< $(python3 -c "print('A'*100)") # Step / continue c # continue to next bp c 3 # continue, ignore next 3 hits n # next line (step over) s # step (step into functions) ni # next instruction (step over) si # step instruction (step into) finish # run until function returns until 0x401234 # run until address advance main+100 # advance to location jump *0x401234 # jump to address (no call) kill # kill the process q # quit gdb
# rr: record and replay execution rr record ./challenge rr replay # Inside rr replay session: reverse-continue # (rc) go backwards reverse-step # (rs) step backwards reverse-next # (rn) reverse-finish # back to caller # Set watchpoint then go backwards → find writer watch *0x404060 rc # find who wrote this address # Checkpoint: save execution state checkpoint # save info checkpoints # list restart 1 # restore checkpoint 1
# All registers info registers # (i r) info all-registers # include float/vector # Single register p $rax p/x $rax # hex p/d $rax # decimal p/t $rax # binary p/c $rax # char # x86-64 calling convention registers # rdi=arg1 rsi=arg2 rdx=arg3 rcx=arg4 # r8=arg5 r9=arg6 rax=return value # rsp=stack ptr rbp=frame ptr rip=instr ptr # Modify register set $rax = 0 set $rip = 0x401234 # redirect execution set $rsp = $rsp - 8 # move stack
# Disassemble disas # current function disas main disas 0x401000, 0x401050 disas/r main # with raw bytes # x/i — disassemble N instructions from addr x/10i $rip # next 10 instructions x/10i main x/10i 0x401234 # List functions / symbols info functions # all known functions info functions win # filter by name info variables # global variables info address main # address of symbol info symbol 0x401234 # symbol at address
# Syntax: x/[N][FORMAT][SIZE] ADDR # N = count # FORMAT = x hex | d dec | s string | i instr # c char | b binary | f float | u unsigned # SIZE = b byte | h halfword | w word | g giant(8) x/20x $rsp # 20 hex words at stack x/20xg $rsp # 20 hex 8-byte (64-bit) x/s 0x402010 # string at address x/s $rdi # string arg1 points to x/4xb 0x404060 # 4 raw bytes x/10i $rip # 10 instructions x/wx &variable # variable by name # Print memory map info proc mappings vmmap # pwndbg: colored map
# pwndbg search search -s "flag" # string search -b 0x41 # byte value search -4 0xdeadbeef # 4-byte value search -8 0x401234 # 8-byte (address) # GDB find command find 0x400000, 0x500000, "flag" find 0x400000, +0x10000, 0x41, 0x42 # Dump memory to file dump binary memory out.bin 0x400000 0x401000 dump hex memory out.hex 0x400000 0x401000 # Write to memory set {int}0x404060 = 0x41424344 set {char[5]}0x404060 = "AAAA"
# Backtrace (call stack) bt # backtrace bt full # with local variables bt 5 # only 5 frames # Frame navigation frame 0 # switch to frame 0 up # go up one frame down # go down one frame info frame # current frame details info locals # local variables info args # function arguments # Stack canary: usually at rbp-0x8 x/gx $rbp-8 # read canary value # pwndbg: visual stack stack # smart stack display telescope $rsp 20 # resolve pointers
# pwndbg cyclic (de Bruijn pattern) cyclic 200 # generate 200-char pattern r <<< $(cyclic 200) # run with pattern as input # → SIGSEGV at 0x6161616e cyclic -l 0x6161616e # → offset = 44 cyclic -l $rsp # if RSP contains pattern # Manual: what's at rsp on crash? x/gx $rsp # read return address # If it shows pattern bytes → leak offset # Check offset for 32-bit (eip) cyclic -l $eip cyclic -l 0x61616165
# Context view (auto on break) context # full: regs + stack + code context regs context stack context code context backtrace # Memory layout vmmap # all mappings with perms vmmap libc # filter by name piebase # PIE base address libcbase # libc base address aslr # ASLR status checksec # mitigations # Pointer resolution telescope $rsp 20 # smart pointer chain telescope 0x404060 dereference $rsp 10 # dereference chain
# Heap analysis heap # all chunks heap -v # verbose bins # all freelist bins fastbins # fastbin list smallbins tcache # tcache bins (glibc 2.26+) vis_heap_chunks # visual heap layout malloc_chunk addr # parse chunk struct arena # main_arena info # ROP gadgets rop # find ROP gadgets ropper # alias got # GOT table entries plt # PLT entries canary # stack canary value
# NOP an instruction (0x90 = NOP x86) set *(unsigned char*)0x401234 = 0x90 # NOP a sequence set {char[6]}0x401234 = "\x90\x90\x90\x90\x90\x90" # Change JNE (0x75) to JE (0x74) — invert branch set *(unsigned char*)0x401234 = 0x74 # Change JNE → JMP (0xeb) — always jump set *(unsigned char*)0x401234 = 0xeb # Force return value set $rax = 0 # return 0 (success) set $rax = 1 # Skip function: advance past it set $rip = 0x401250 # jump over check # pwndbg patch command patch 0x401234 "nop; nop" patch byte 0x401234 0x90
# Call a function directly call (void)win_function() call (int)strcmp("hello", "world") call (void*)malloc(256) # Signal handling handle SIGSEGV nostop # don't stop on SIGSEGV handle SIGALRM ignore # ignore alarm (anti-debug) handle all nostop noprint # ignore all signals info signals # Fork following set follow-fork-mode child # follow child set follow-fork-mode parent set detach-on-fork off # debug both
# Pattern (like cyclic) pattern create 200 pattern search $rsp pattern search 0x61616165 # Memory xinfo $rip # info about address memory watch 0x404060 4 # watch 4 bytes highlight add 0x404060 # Heap (GEF version) heap chunks heap bins heap arenas # Assemble / disassemble assemble # interactive assembler assemble -a x64 "xor rax,rax; ret" # libc resolver got # GOT with resolved names xfiles # loaded files/libraries
# p — print expressions p 0xff + 1 # → 256 p (int)$rax p (char*)$rdi # cast register to string p &global_var # address of symbol # Format specifiers for p p/x $rax # hex p/d $rax # decimal signed p/u $rax # decimal unsigned p/t $rax # binary p/o $rax # octal p/c $rax # character p/f $xmm0 # float p/a 0x401234 # address + symbol name
# Run commands from file gdb -x cmds.gdb ./challenge # cmds.gdb: b main r p $rdi x/s $rdi c quit # Inline with -ex gdb -q -ex "b main" -ex "r" -ex "p \$rax" ./challenge # Python scripting inside GDB (gdb) python import gdb frame = gdb.selected_frame() rax = frame.read_register('rax') print(f"rax = {int(rax):#x}") end # Python script file gdb -x script.py ./challenge
from pwn import * # Launch with GDB attached (stops at entry) p = gdb.debug('./challenge', ''' set follow-fork-mode child break main continue ''') # Attach GDB to running process p = process('./challenge') gdb.attach(p, ''' break strcmp continue ''') # Pause to let you set breakpoints p = process('./challenge') gdb.attach(p) pause() # wait for Enter before continuing
| Scenario | GDB Commands |
|---|---|
| Find strcmp args (flag comparison) | b strcmp → r → x/s $rdi / x/s $rsi |
| Find overflow offset | cyclic 200 → crash → cyclic -l $rsp |
| Bypass a check | b *0x401234 → set $rax = 0 → c |
| Patch a jne to jmp | set *(char*)0x401234 = 0xeb |
| Leak libc address | b puts → p $rip → compute base |
| Read stack canary | x/gx $rbp-8 |
| PIE: find base | piebase (pwndbg) or info proc mappings |
| Find /bin/sh in libc | search -s "/bin/sh" |
| Trace all calls | catch syscall + commands: bt; c |
| Anti-debug: SIGALRM | handle SIGALRM ignore |
b func breakpoint
· r run
· ni/si step
· x/s $rdi string arg
· x/20gx $rsp stack
· telescope $rsp smart stack
· cyclic / cyclic -l offset
· vmmap memory map
· checksec mitigations
· set $rax=0 patch register