ROPgadget
2026/5/29工具工具ROPgadget大约 6 分钟
ROPgadget
链接
是什么
ROPgadget 是一款用于搜索二进制文件中 ROP(Return-Oriented Programming)gadget 的工具。ROP 是一种代码复用技术,攻击者通过组合程序中已有的代码片段(gadget)来构造恶意操作链,绕过 DEP/NX 等安全防护机制。
核心功能:
- 搜索 ELF/PE 文件中的 ROP gadget
- 支持多种架构(x86、x64、ARM、MIPS 等)
- 自动生成 ROP 链
- 过滤和排序 gadget
- 与 pwntools 集成
在 CTF Pwn 类题目中,ROPgadget 是寻找可用 gadget 的核心工具,配合 pwntools 构造 ROP 链进行漏洞利用。
安装与配置
安装方法
# 使用 pip 安装(推荐)
pip install ROPgadget
# 使用 pip3
pip3 install ROPgadget
# 从 GitHub 安装最新版
pip install git+https://github.com/JonathanSalwan/ROPgadget.git
# 验证安装
ROPgadget --version
# Kali Linux(可能已预装)
ROPgadget --help环境配置
# 基本配置
# ROPgadget 无需额外配置,直接使用即可
# 确保 capstone 库已安装
pip install capstone基本用法
搜索所有 gadget
# 搜索 ELF 文件中的所有 gadget
ROPgadget --binary ./vuln
# 搜索指定二进制文件
ROPgadget --binary /path/to/binary
# 只显示包含 ret 的 gadget
ROPgadget --binary ./vuln --only "ret"搜索特定 gadget
# 搜索包含特定指令的 gadget
ROPgadget --binary ./vuln --only "pop|ret"
# 搜索特定指令序列
ROPgadget --binary ./vuln --only "pop rdi; ret"
# 搜索包含 "pop rdi" 的 gadget
ROPgadget --binary ./vuln --only "pop rdi"
# 搜索包含 "pop rsi" 的 gadget
ROPgadget --binary ./vuln --only "pop rsi"
# 搜索包含 "pop rdx" 的 gadget
ROPgadget --binary ./vuln --only "pop rdx"
# 搜索 syscall 指令
ROPgadget --binary ./vuln --only "syscall"
# 搜索 int 0x80 指令
ROPgadget --binary ./vuln --only "int 0x80"过滤和排序
# 限制搜索深度(gadget 最大指令数)
ROPgadget --binary ./vuln --depth 5
# 搜索特定地址范围
ROPgadget --binary ./vuln --range 0x400000-0x500000
# 搜索特定段
ROPgadget --binary ./vuln --section .text
# 搜索特定字符串
ROPgadget --binary ./vuln --string "/bin/sh"
# 搜索特定字节序列
ROPgadget --binary ./vuln --bytes "4889e5"
# 只搜索可执行段
ROPgadget --binary ./vuln --only "pop" --nojop搜索特定架构
# x86-64
ROPgadget --binary ./vuln --binary64
# x86-32
ROPgadget --binary ./vuln --binary32
# ARM
ROPgadget --binary ./vuln --thumb
# MIPS
ROPgadget --binary ./vuln --mips搜索字符串和常量
# 搜索字符串
ROPgadget --binary ./vuln --string "flag"
ROPgadget --binary ./vuln --string "/bin/sh"
# 搜索十六进制值
ROPgadget --binary ./vuln --hex 0xdeadbeef输出格式
# 默认输出
ROPgadget --binary ./vuln
# JSON 格式输出
ROPgadget --binary ./vuln --json
# 只显示地址
ROPgadget --binary ./vuln --only "pop rdi; ret" --addrCTF常用技巧
常用 gadget 搜索命令
# 1. 搜索 pop rdi; ret(x86-64 最常用)
ROPgadget --binary ./vuln --only "pop rdi; ret"
# 2. 搜索 pop rsi; pop r15; ret(用于两个参数)
ROPgadget --binary ./vuln --only "pop rsi; pop r15; ret"
# 3. 搜索 pop rdx; ret(x86-64 第三个参数)
ROPgadget --binary ./vuln --only "pop rdx; ret"
# 4. 搜索 ret(用于栈对齐)
ROPgadget --binary ./vuln --only "ret"
# 5. 搜索 leave; ret(栈迁移)
ROPgadget --binary ./vuln --only "leave; ret"
# 6. 搜索 syscall
ROPgadget --binary ./vuln --only "syscall"
# 7. 搜索 int 0x80(x86-32 系统调用)
ROPgadget --binary ./vuln --only "int 0x80"自动生成 ROP 链
# 使用 ROPgadget 自动生成 ROP 链
ROPgadget --binary ./vuln --ropchain
# 注意:自动生成的 ROP 链可能不是最优的
# 建议手动搜索 gadget 并使用 pwntools 构造配合 pwntools 使用
from pwn import *
# 加载 ELF 文件
elf = ELF('./vuln')
# 使用 pwntools 的 ROP 类自动搜索 gadget
rop = ROP(elf)
# 查找常用 gadget
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
pop_rsi = rop.find_gadget(['pop rsi', 'pop r15', 'ret'])[0]
pop_rdx = rop.find_gadget(['pop rdx', 'ret'])[0]
ret = rop.find_gadget(['ret'])[0]
leave_ret = rop.find_gadget(['leave', 'ret'])[0]
syscall = rop.find_gadget(['syscall'])[0]
print(f"pop rdi; ret: {hex(pop_rdi)}")
print(f"pop rsi; pop r15; ret: {hex(pop_rsi)}")
print(f"pop rdx; ret: {hex(pop_rdx)}")
print(f"ret: {hex(ret)}")
print(f"leave; ret: {hex(leave_ret)}")
print(f"syscall: {hex(syscall)}")
# 使用 pwntools 自动搜索并打印
rop.dump()搜索 libc 中的 gadget
# 搜索 libc 文件中的 gadget
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only "pop rdi; ret"
# 搜索 ld.so 中的 gadget
ROPgadget --binary /lib64/ld-linux-x86-64.so.2 --only "pop rdi; ret"搜索特定地址范围
# 只在 .text 段搜索
ROPgadget --binary ./vuln --range 0x400000-0x401000
# 排除特定地址范围
ROPgadget --binary ./vuln --range 0x400000-0x500000 --norop搜索 JOP gadget
# 搜索 JOP(Jump-Oriented Programming)gadget
ROPgadget --binary ./vuln --only "jmp"
# 搜索间接调用
ROPgadget --binary ./vuln --only "call"搜索特定字节模式
# 搜索特定字节序列
ROPgadget --binary ./vuln --bytes "5f c3" # pop rdi; ret
# 搜索 shellcode 特征
ROPgadget --binary ./vuln --bytes "31c05068"搜索字符串地址
# 搜索 "/bin/sh" 字符串地址
ROPgadget --binary ./vuln --string "/bin/sh"
# 在 libc 中搜索
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --string "/bin/sh"
# 搜索其他常见字符串
ROPgadget --binary ./vuln --string "flag"
ROPgadget --binary ./vuln --string "cat"
ROPgadget --binary ./vuln --string "sh"搜索 syscall 序列
# x86-64: 搜索 syscall 指令
ROPgadget --binary ./vuln --only "syscall"
# x86-32: 搜索 int 0x80
ROPgadget --binary ./vuln --only "int 0x80"
# ARM: 搜索 svc 0
ROPgadget --binary ./vuln --only "svc"
# MIPS: 搜索 syscall
ROPgadget --binary ./vuln --only "syscall"使用 Python API
from ropgadget.rgutils import *
# 使用 ROPgadget 的 Python API
binary = './vuln'
gadgets = get_gadgets(binary)
# 过滤 gadget
for gadget in gadgets:
if 'pop rdi' in gadget['gadget']:
print(f"Address: {hex(gadget['vaddr'])}, Gadget: {gadget['gadget']}")完整 ROP 链构造示例
from pwn import *
from subprocess import check_output
# 使用 ROPgadget 搜索 gadget
def find_gadget(binary, pattern):
output = check_output(['ROPgadget', '--binary', binary, '--only', pattern])
for line in output.decode().split('\n'):
if ' : ' in line:
addr = int(line.split(' : ')[0], 16)
return addr
return None
elf = ELF('./vuln')
# 找 gadget
pop_rdi = find_gadget('./vuln', 'pop rdi; ret')
pop_rsi = find_gadget('./vuln', 'pop rsi; pop r15; ret')
ret = find_gadget('./vuln', 'ret')
# 构造 ROP 链
payload = b'A' * offset
payload += p64(ret) # 栈对齐
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.symbols['main'])搜索 __libc_csu_init gadget
# x86-64 程序中常见的 gadget
# 在 __libc_csu_init 函数中
ROPgadget --binary ./vuln --only "pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret"
ROPgadget --binary ./vuln --only "mov rdx; mov rsi; mov edi; call"搜索栈迁移 gadget
# 搜索 leave; ret
ROPgadget --binary ./vuln --only "leave; ret"
# 搜索 pop rbp; ret
ROPgadget --binary ./vuln --only "pop rbp; ret"
# 搜索 xchg rsp, rax; ret
ROPgadget --binary ./vuln --only "xchg"搜索加解密 gadget
# 搜索异或 gadget
ROPgadget --binary ./vuln --only "xor"
# 搜索加法 gadget
ROPgadget --binary ./vuln --only "add"
# 搜索移位 gadget
ROPgadget --binary ./vuln --only "shr|shl|sar|sal"常见问题
找不到 gadget
原因:gadget 搜索深度不够或指令被优化。
解决:
# 增加搜索深度
ROPgadget --binary ./vuln --depth 10
# 搜索 libc 中的 gadget
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6
# 使用 pwntools 自动搜索
from pwn import *
elf = ELF('./vuln')
rop = ROP(elf)gadget 地址包含坏字符
解决:
# 检查地址是否包含坏字符(如 \x00, \x0a)
def check_badchar(addr, badchars=b'\x00\x0a\x0d'):
for b in p64(addr):
if b in badchars:
return True
return False
# 寻找替代 gadget
# 或使用其他 libc 中的 gadget搜索速度慢
解决:
# 限制搜索范围
ROPgadget --binary ./vuln --section .text
# 减少搜索深度
ROPgadget --binary ./vuln --depth 3
# 只搜索特定指令
ROPgadget --binary ./vuln --only "pop rdi; ret"32 位和 64 位混淆
说明:
- 32 位程序使用
pop eax; ret、int 0x80 - 64 位程序使用
pop rax; ret、syscall - 使用
file ./vuln查看架构
ASLR 下的 gadget 使用
说明:
- 程序本身的 gadget 地址固定(非 PIE)
- libc 中的 gadget 需要先泄漏 libc 基址
- 使用 DynELF 或泄漏 GOT 表地址计算 libc 基址
PIE 程序的 gadget
说明:
- PIE 程序每次加载地址不同
- 需要先泄漏程序基址
- 或使用 libc 中的 gadget(先泄漏 libc 地址)