关于 ARM64 汇编:调用流程与栈帧结构解析
一、ARM64 函数调用分析(汇编级)
寄存器规则(AArch64 ABI)
用途 | 寄存器 |
---|---|
参数传递 | x0 ~ x7 |
返回值 | x0(最多两个:x0、x1) |
栈指针 | sp |
链接寄存器 | x30(lr) |
帧指针 | x29(fp) |
临时/通用寄存器 | x9~x15等 |
函数调用指令流程
bl target_func ; 跳转并将返回地址保存到 x30(link register)
ret ; 返回上一级,等价于 br x30
举例
mov x0, #2
mov x1, #3
bl add_func ; 调用 add_func(2, 3)
二、栈帧结构详解
每个函数调用会建立自己的“栈帧”,用于:
-
保存 caller 的寄存器(尤其是 x29, x30)
-
为局部变量腾出空间
-
管理多层调用时的上下文切换
标准函数前后结构
函数入口
stp x29, x30, [sp, #-16]! ; 保存 fp 和 lr,栈顶向下扩展 16 字节
mov x29, sp ; 新帧指针 = 当前栈顶
函数返回
ldp x29, x30, [sp], #16 ; 恢复帧指针和返回地址
ret ; 返回上一层
示例完整函数
func:stp x29, x30, [sp, #-16]! ; 建栈帧mov x29, sp// 函数体ldp x29, x30, [sp], #16 ; 恢复ret
三、栈帧结构图解(栈向下增长)
高地址 ↑
───────────────上一帧 fp ← x29返回地址(lr) ← x30
───────────────局部变量区参数传递区(x8+)
───────────────当前 sp(栈底)
低地址 ↓
分析技巧:
-
IDA 里找
stp x29, x30, [sp,#-xxx]!
是函数入口特征 -
栈帧大小往往是 16 的倍数(对齐)
四、系统调用实现(Linux syscall in ARM64)
在裸汇编中实现 syscall 需要:
-
把 syscall 参数放入 x0~x5
-
把 syscall 编号放入 x8
-
执行
svc #0
发起系统调用
系统调用号对照
功能 | syscall 号 |
---|---|
write | 64 |
read | 63 |
openat | 56 |
mmap | 222 |
exit | 93 |
示例:调用 write 输出字符串
汇编代码(写到 stdout)
.global _start.section .data
msg: .ascii "Hello ARM64!\n"
len = . - msg.section .text
_start:mov x0, #1 // stdoutldr x1, =msg // buf addressmov x2, #12 // lengthmov x8, #64 // syscall number: writesvc #0 // trigger syscall// exit(0)mov x0, #0mov x8, #93 // syscall number: exitsvc #0
编译运行方式(Linux ARM64 模拟器或真机)
aarch64-linux-gnu-as hello.s -o hello.o
aarch64-linux-gnu-ld hello.o -o hello
qemu-aarch64 ./hello
五、逆向视角:系统调用识别
在 IDA 中看到以下指令,基本可以确认是系统调用:
mov x8, #64 ; syscall 编号
svc #0 ; 系统调用
结合调用前的 mov x0, #1
, ldr x1, =addr
, mov x2, #len
可以还原 syscall 是写 stdout。
汇总图(函数调用 → 栈帧 → syscall)
main:stp x29, x30, [sp, #-16]! // 保存mov x29, spmov x0, #1 // arg0: stdoutldr x1, =msg // arg1: buffer addrmov x2, #13 // arg2: lengthmov x8, #64 // syscall writesvc #0 // 发起调用ldp x29, x30, [sp], #16 // 恢复ret
六、总结
内容 | 关键点 |
---|---|
调用规则 | x0~x7 传参,x30 返回地址 |
栈帧结构 | 使用 x29 做帧指针,函数内局部变量/保存寄存器都存在栈 |
系统调用 | x0~x5 参数, x8 syscall 号, svc #0 触发 |
IDA/Ghidra逆向 | 找 stp/ldp , bl , svc 判断函数入口、调用、syscall |
实战环境 | 用 qemu + aarch64 工具链模拟也能练习 |