Skip to content

GDB / GEF


¿Por qué GDB en offensive security?

  • Análisis de shellcode: ver byte a byte cómo se ejecuta
  • Buffer overflow: encontrar offset exacto al return address
  • Anti-debug bypass: entender qué checa el binario
  • Reverse engineering: entender binarios sin código fuente
  • Exploit development: verificar que tu payload funciona

Iniciar GDB

bash
gdb -q ./binario            # -q = quiet (sin banner)
gdb -q ./binario core       # analizar core dump
gdb --args ./binario arg1   # con argumentos
gef ./binario               # GEF directamente (si está configurado)

# Desde pwntools (scripting de exploits):
from pwn import *
p = gdb.debug('./binario', gdbscript='b main\nc')

Breakpoints — controlar dónde pausar

bash
# Por nombre de función o label
b _start                    # breakpoint al inicio del programa
b main                      # en función main
b loopFib                   # en label loopFib

# Por offset desde un label
b *_start+17                # 17 bytes después de _start
b *loopFib+9                # en la instrucción js de loopFib

# Por dirección exacta
b *0x401011                 # dirección absoluta

# Condicional — solo pausa si se cumple la condición
b loopFib if $rbx > 10      # pausa cuando rbx supere 10
b *0x401012 if $rcx == 0    # pausa cuando el contador llegue a 0

# Administrar breakpoints
info break                  # listar todos los breakpoints
del 1                       # eliminar breakpoint #1
del                         # eliminar TODOS
disable 2                   # desactivar sin eliminar
enable 2                    # reactivar

En contexto ofensivo

bash
# Breakpoint antes de strcpy para ver el buffer antes del overflow
b strcpy
# Breakpoint en la return address del frame vulnerable
b *vuln_func+45             # justo antes del ret

# Breakpoint condicional para cuando el canary cambie
b *función+offset if $rax != canary_value

# Watchpoint — pausa cuando se ESCRIBE en una dirección
watch *0x602060             # pausa cuando se modifica 0x602060 (ej: GOT entry)
rwatch *0x602060            # pausa cuando se LEE

Ejecución — controlar el flujo

bash
r                           # run (ejecutar desde el inicio)
r arg1 arg2                 # run con argumentos
r < input.txt               # run con stdin desde archivo

c                           # continue (hasta siguiente breakpoint)
si                          # step instruction (UNA instrucción ASM) ← usar siempre en ASM
ni                          # next instruction (como si pero no entra en calls)
s                           # step (por línea de fuente — evitar en ASM puro)
n                           # next (por línea, sin entrar en funciones)

finish                      # ejecutar hasta que la función actual retorne
until *0x401050             # ejecutar hasta llegar a esa dirección
jump *0x401050              # saltar directamente a esa dirección (sin ejecutar lo intermedio)

Diferencia crítica: si vs ni

bash
# si (step into) — entra DENTRO de los calls
# Antes: → 0x401020 call printf
si
# Después: → 0x7f... <printf+0>  ← estás dentro de printf

# ni (next over) — ejecuta el call pero NO entra
# Antes: → 0x401020 call printf
ni
# Después: → 0x401025 mov rax, 0  ← continuó después del call

En contexto ofensivo

bash
# Analizar shellcode byte a byte
b *0x401000     # start
r
si              # ejecutar 1 instrucción
si              # siguiente
si              # ...

# Llegar rápido al punto de crash
r               # dejar que crashee
# gdb muestra dónde fue el SIGSEGV
# ver qué había en rsp en ese momento → eso es lo que kontrolas

Ver registros

bash
info registers              # todos los registros
info reg rax rbx rip        # solo algunos

p $rax                      # imprimir rax (en decimal por defecto)
p/x $rax                    # en hexadecimal
p/d $rbx                    # en decimal
p/o $rcx                    # en octal
p/t $rax                    # en binario
p/s $rsi                    # como string (si apunta a string)
p/f $xmm0                   # como float

# Modificar registros (útil para testing)
set $rax = 0x1337           # cambiar rax mientras debuggeas
set $rip = 0x401050         # redirigir ejecución manualmente

En contexto ofensivo

bash
# Verificar valor de rsp al momento del ret (buffer overflow)
b *vuln+0x45    # breakpoint en el ret
r
p/x $rsp        # ver qué dirección va a saltar
x/1xg $rsp      # ver el contenido de rsp (la return address)

# Si la return address es 0x4141414141414141 ("AAAAAAAA")
# significa que controlamos el return address ✓
# ahora calcular el offset exacto con cyclic pattern:
# cyclic 200 | ./binario
# x/1xg $rsp   → ver el patrón que llegó ahí
# cyclic -l 0x6161616e → offset exacto

Ver memoria

bash
# x/[cantidad][formato][tamaño] dirección
x/16xg $rsp         # 16 valores de 8 bytes (qword) en hex desde rsp
x/32xb $rdi         # 32 bytes desde rdi
x/4xw 0x402000      # 4 dwords desde dirección 0x402000
x/s $rdi            # ver como string (hasta null byte)
x/i $rip            # ver instrucción en rip
x/10i $rip          # ver 10 instrucciones desde rip

# Formatos:
# x = hex, d = decimal, s = string, i = instrucción, c = char
# Tamaños:
# b = 1 byte, h = 2 bytes, w = 4 bytes, g = 8 bytes

En contexto ofensivo

bash
# Ver el stack completo durante un overflow
x/40xg $rsp         # ver 40 qwords del stack

# Buscar patrón en memoria (encontrar offset)
# después de enviar cyclic pattern:
x/1xg $rsp          # ver qué valor cayó en rsp
# luego: cyclic -l VALOR → te da el offset

# Ver si el shellcode está bien en memoria
x/50xb $rsp+0x100   # ver bytes del shellcode inyectado
x/20i $rsp+0x100    # ver como instrucciones (verificar que se desensambla bien)

# Buscar string en memoria
find 0x400000, 0x500000, "/bin/sh"  # buscar "/bin/sh" en el binario
# encuentra la dirección → usar en ret2libc

# Ver GOT table (para info leaks)
x/10xg 0x602000     # ver entradas GOT

Desensamblar

bash
disas _start                # desensamblar función completa
disas loopFib               # con offsets exactos para breakpoints
disas 0x401000, 0x401050    # rango de direcciones
disas /m main               # con código fuente intercalado (si hay símbolos)

# Ver instrucción en rip actual
x/i $rip
x/5i $rip                   # próximas 5 instrucciones

En contexto ofensivo

bash
# Encontrar gadgets ROP manualmente
disas funcion
# buscar visualmente: pop rdi; ret / pop rsi; ret / syscall; ret

# Verificar PLT/GOT
disas puts@plt              # ver stub de plt
x/1xg 0x602020             # ver GOT entry de puts (dirección real en libc)

# Analizar función desconocida (reverse engineering)
disas 0x401234
# ir instrucción por instrucción con si para entender qué hace

GEF — indicators visuales

● en dirección    → breakpoint activo en esa instrucción
→ en instrucción  → próxima instrucción a ejecutar (rip)

En la sección code:x86:64:
  TAKEN [Reason: S]         → el salto condicional SÍ se tomará
  NOT taken [Reason: !(S)]  → el salto NO se tomará

En $eflags:
  MAYÚSCULAS = flag ACTIVO
  minúsculas = flag inactivo
  Ejemplo: [ZERO carry PARITY adjust sign INTERRUPT direction overflow]
  → ZF=1 (ZERO activo), CF=0, PF=1, SF=0...

En registros:
  $rsp → 0x7fff1000 → 0x0000000000000001
  ↑ dirección       ↑ valor al que apunta (GEF lo desreferencia)

En stack:
  0x7fff1000│+0x0000: 0x0000000000000001  ← $rsp
  El │ separa dirección del valor
  El offset +0x0000 es relativo a rsp

Comandos de análisis de binarios

bash
# Ver info del binario
checksec                    # protecciones: NX, PIE, RELRO, canary, etc.
info files                  # secciones del binario cargado
info functions              # listar funciones con símbolos
info variables              # variables globales

# Buscar strings útiles
find /bin/sh                # buscar string "/bin/sh" en memoria
grep -r "sh" ./             # antes de cargar

# Ver memoria mapeada
info proc mappings          # qué está cargado y con qué permisos
# Muestra: dirección inicio, fin, permisos (rwx), nombre
# Útil para verificar si hay segmento rwx (donde inyectar shellcode)

# Modificar memoria (para testing de exploits)
set {char}0x401000 = 0x90  # escribir NOP en esa dirección
set {long}$rsp = 0x401337  # cambiar return address en el stack

En contexto ofensivo

bash
# checksec output y qué significa:
# NX enabled   → stack no ejecutable (necesitas ROP o heap spray)
# PIE enabled  → ASLR de código (necesitas info leak para dirección base)
# RELRO Full   → GOT protegido (no puedes sobreescribir GOT entries)
# Stack canary → protección contra overflow básico
# RUNPATH      → ruta de búsqueda de librerías

# Buscar "/bin/sh" en libc (para ret2libc)
info proc mappings          # encontrar base de libc
# supón que libc está en 0x7ffff7a0d000
find 0x7ffff7a0d000, 0x7ffff7c00000, "/bin/sh"
# te da la dirección exacta → usarla en tu exploit

# Ver si lograste control del RIP
b *vuln+offset_ret
r
p/x $rip                    # ver si rip = tu valor
# si es 0x4141414141414141 → controlado ✓

Comandos de objdump complementarios

bash
# Desensamblar .text
objdump -M intel -d binario

# Desensamblar TODO
objdump -M intel -D binario

# Solo instrucciones (sin bytes ni addresses — legible)
objdump -M intel --no-show-raw-insn --no-addresses -d binario

# Ver .data como hex+ASCII (encontrar strings, flags, etc.)
objdump -sj .data binario

# Buscar gadgets ROP (alternativa a ROPgadget)
objdump -M intel -d binario | grep -A1 'pop rdi'
objdump -M intel -d binario | grep 'syscall'

Flujo típico para explotar un buffer overflow

bash
# 1. Ver protecciones
checksec

# 2. Encontrar el crash
python3 -c "print('A'*200)" | ./binario
# o con cyclic:
python3 -c "from pwn import *; print(cyclic(200))" | ./binario

# 3. En gdb, ver qué valor cayó en rsp al crashear
gdb -q ./binario
r < <(python3 -c "from pwn import *; sys.stdout.buffer.write(cyclic(200))")
# al crashear:
x/1xg $rsp          # ver patrón en rsp
# luego:
python3 -c "from pwn import *; print(cyclic_find(0x6161616e))"  # offset

# 4. Verificar control de rip
# enviar offset bytes de padding + dirección objetivo
r < <(python3 -c "print('A'*offset + 'B'*8)")
p/x $rip            # debería ser 0x4242424242424242

# 5. Construir exploit
# [padding][dirección_shellcode o gadget ROP]