关于 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 号
write64
read63
openat56
mmap222
exit93

示例:调用 write 输出字符串

汇编代码(写到 stdout)

.global _start

.section .data
msg:    .ascii "Hello ARM64!\n"
len = . - msg

.section .text
_start:
    mov x0, #1              // stdout
    ldr x1, =msg            // buf address
    mov x2, #12             // length
    mov x8, #64             // syscall number: write
    svc #0                  // trigger syscall

    // exit(0)
    mov x0, #0
    mov x8, #93             // syscall number: exit
    svc #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, sp
  mov x0, #1                     // arg0: stdout
  ldr x1, =msg                   // arg1: buffer addr
  mov x2, #13                    // arg2: length
  mov x8, #64                    // syscall write
  svc #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 工具链模拟也能练习
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值