OC逆向02:函数本质(下)

张建 lol

前言

本文主要是讲解函数的 参数、返回值、局部变量 在汇编中是如何存储的,以及 CPSR 标志寄存器

函数的参数和返回值

  • arm64下,函数的 参数 是存放在 x0-x7(w0-w7) 这8个寄存器里面的,如果超过 8 个参数,就会入栈

    • 如果自定义函数时,参数最好不要超过6个(因为有两个隐藏参数 self,_cmd)

    • 如果函数需要多个参数,可以传入数组、结构体、指针等类型

  • 函数的 返回值 放在 x0寄存器 中

    • 如果返回值 大于8个 字节,就会 利用内存传递

查看系统的参数汇编

下面通过系统中对函数的汇编来查看系统对参数、返回值是如何操作的

1
2
3
4
5
6
7
int sum(int a, int b){
return a + b;
}
- (void)viewDidLoad{
[super viewDidLoad];
sum(10, 20);
}
  • 查看汇编,在跳转到sum函数之前,已经将参数存入 w0、w1

  • 在sum函数中,读取w0、w1,放入w8、w9。然后将相加后的结果放入w0(即返回值在w0寄存器)

自己优化实现sum

运行发现,其结果与sum函数是一致的,结果都是30

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--asm.s-->
.text
.global _suma

_suma:
add x0, x0, x1
ret

<!--调用-->
int suma();
- (void)viewDidLoad{
[super viewDidLoad];
printf("%d",suma(10, 20));
}

<!--查看打印结果-->
30

编译器优化

来看一下代码的汇编

1
2
3
4
5
6
7
8
int test(int a, int b, int c, int d, int e, int f, int g, int h, int i){
return a + b + c + d + e + f + g + h + i;
}

- (void)viewDidLoad {
[super viewDidLoad];
test(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
  • test函数断住,查看汇编

以下是 viewDidLoad 栈空间的存入分析过程

  • 以下是 test 函数的汇编分析

编译器优化

  • 将debug模式改成release模式,此时再来查看汇编代码,发现没有test函数,被优化掉了

  • 如果非要执行 test,可以这样写
1
2
3
4
- (void)viewDidLoad{
[super viewDidLoad];
printf("%d", test(1, 2, 3, 4, 5, 6, 7, 8, 9));
}

汇编代码如下,发现优化后的test函数在汇编中,其本质是一个 ,也就是 test函数的返回值(相当于将 printf("%d", test(1, 2, 3, 4, 5, 6, 7, 8, 9)); 直接优化成了 printf("%d", 45);)

通过汇编实现函数

  • 定义函数声明及调用
1
2
3
4
5
6
7
int funcA(int a, int b);

- (void)viewDidLoad {
[super viewDidLoad];
int a = funcA(10, 20);
printf("%d", a);
}
  • 汇编实现 funcA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--sam中-->
.text
.global _funcA, _sum

_funcA:
sub sp, sp, #0x10
stp x29, x30, [sp] // 保护lr
bl _sum
ldp x29, x30, [sp]
add sp, sp, #0x10
ret

_sum:
add x0, x0, x1
ret

<!--vc中-->
int funcA();
- (void)viewDidLoad{
[super viewDidLoad];
printf("%d",funcA(10,20));
}

运行结果如下

1
30

【说明】:

  • 关于b指令:只是跳转,不改变lr寄存器

  • 拉伸空间和参数个数有没有关系?

    • 有关系,参数越多时,如果寄存器放不下,就需要用到内存。就会将栈空间放大,影响栈空间的不仅仅是参数个数,还有局部变量

函数的返回值

如果返回值是 一个结构体,一个寄存器放不下,这是什么情况?

有以下代码,运行查看其汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct str {
int a;
int b;
int c;
int d;
int f;
int g;
};
struct Str getStr(int a, int b, int c, int d, int f, int g){
struct Str str1;
str1.a = a;
str1.b = b;
str1.c = c;
str1.d = d;
str1.f = f;
str1.g = g;
return str1;
}

- (void)viewDidLoad {
[super viewDidLoad];

struct Str str2 = getStr(1, 2, 3, 4, 5, 6);
}
  • 断点运行,以下是 viewDidLoad 函数的汇编
  • Post title:OC逆向02:函数本质(下)
  • Post author:张建
  • Create time:2022-04-20 15:51:43
  • Post link:https://redefine.ohevan.com/2022/04/20/OC逆向/OC逆向02:函数本质(下)/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.