函数调用

x29: fp (栈底,高地址) sp: (栈顶,低地址) x30: lr(返回地址) 调用函数: bl 地址,x30 = pc + 4 函数开头:

SUB             SP, SP, stack_size
STP             X29, X30, [SP,stack_size-0x10+var_s0] // store pair
ADD             X29, SP, stack_size-0x10

结尾:

LDP             X29, X30, [SP,stack_size-0x10+var_s0] // load pair
ADD             SP, SP, stack_size
RET

调用约定: 参数、返回值寄存器传递,x0~x7, f0~f7 x8 (xr) 是一个间接结果寄存器。系统调用编号。 x9 到 x15 是调用者保存的临时寄存器。 x16 (ip0) and x17 (ip1) are inter-procedural scratch registers. Meaning they are used as scratch registers only during a Prologue. Other than that, they basically are never used. x18 (pr) is known as the platform register. Basically just another scratch register. Rarely used. x19 至 x28 是被调用者保存的寄存器。 f8 至 f15 是被调用者保存的寄存器(类似于 x19 至 x28) f16 至 f31 是临时寄存器,处理方式类似于 x9 至 x15。

保存现场

将代码插入程序(例如漏洞或某些视频游戏作弊码)时,有一个简单的方法可以安全地使用非易失性寄存器,同时还包括修改“永不安全”的 sp 寄存器。

Place this at the start of your exploit/cheat…

stp fp, lr, [sp, #-0x60]!
stp x19, x20, [sp, #0x10]
stp x21, x22, [sp, #0x20]
stp x23, x24, [sp, #0x30]
stp x25, x26, [sp, #0x40]
stp x27, x28, [sp, #0x50]
mov fp, sp

And place this at the end of your exploit/cheat…

ldp x27, x28, [sp, #0x50]
ldp x25, x26, [sp, #0x40]
ldp x23, x24, [sp, #0x30]
ldp x21, x22, [sp, #0x20]
ldp x19, x20, [sp, #0x10]
ldp fp, lr, [sp], #0x60

This will allow you to use any of the non-volatile registers you desire.

内联汇编示例

#include <cstdio>
 
int main() {
    int a=10, b;
    // ...
    asm volatile (
        R"(
        mov w0, %w0
        mov w1, #0x20
        add %w0, w0, w1
)"
        // ... some other operations
        : "=r"(b)/* no outputs */
        : "r"(a)/* no inputs */
        : "x0", "x1", "memory" // 告知编译器x0和x1寄存器的值已被修改
    );
    printf("%d\n", b);
    // ...
}