当前位置: 首页 > news >正文

关于 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              // 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 工具链模拟也能练习

相关文章:

  • Jenkins与Kubernetes深度整合实践
  • GitLab 18.1 高级 SAST 已支持 PHP,可升级体验!
  • OSPF 路由协议详细笔记
  • Python-7-读取/写入文件数据
  • mb_bootloop_le.elf是使用microblaze默认的elf文件,这个文件包括哪些内容?
  • n8n智能体新境界:MCP服务器简化复杂自动化
  • Apache 支持 HTTPS
  • 系统性能优化-6 TCP 三次握手
  • easyExcel导入多sheet的Excel,存在合并单元格、列不固定的情况
  • 自动获取文件的内存大小怎么设置?批量获取文件名和内存大小到Excel中的方法
  • notepad++ 怎么快速给 python (nginx、shell) 文件加 # 注释
  • 远程玩3A大作要多少帧?ToDesk、向日葵、UU远程性能对决
  • 从零搭建高效本地代理池:设计与实现
  • Ubuntu中控制用户cpu资源分配控制步骤
  • Flutter 多平台项目开发指南
  • 【Go语言-Day 9】指针基础:深入理解内存地址与值传递
  • 量学云讲堂2025年天山至尊刘智辉第63期视频课程+第2段位课
  • Trae IDE 大师评测:驾驭 MCP Server - Figma AI Bridge 一键成就前端瑰宝
  • 原子级制造革命:双原子镧催化剂登顶Angew,焦耳超快加热技术深度解析
  • leetcode:50. Pow(x, n)(python3解法,数学相关算法题)