返回

函数返参揭秘:揭开 iOS 逆向安防神秘面纱

IOS

我们已经深入探讨了函数的参数,现在让我们转而研究函数的返回值,看看是否能有更多令人惊喜的发现。

普通返回值

在大多数情况下,函数只返回一个值。我们来探究一下这个值是如何存储和获取的。

为了实现这个目的,我们需要使用 LLVM 反汇编器来查看函数的汇编代码。对于那些不熟悉 LLVM 的人来说,它是一种低级语言,用于表示代码。

在 LLVM 反汇编中,我们可以看到以下内容:

define i32 @foo(i32 %a) {
  %0 = add i32 %a, 1
  ret i32 %0
}

在这里,@foo 是函数名称,i32 %a 是函数参数,%0 是函数返回值。我们可以看到,返回值被存储在寄存器 %0 中。

要获取返回值,我们需要使用 ret 指令。ret i32 %0 指令表示将 %0 寄存器中的值作为返回值返回。

结构体返回值

我们已经讨论了普通返回值。接下来我们看看如果返回值是一个结构体时会发生什么。

struct Point {
  int x;
  int y;
};

struct Point foo() {
  struct Point p;
  p.x = 1;
  p.y = 2;
  return p;
}

在这里,foo 函数返回一个 Point 结构体。

在 LLVM 反汇编中,我们可以看到以下内容:

define %struct.Point @foo() {
  %0 = alloca %struct.Point
  %1 = getelementptr inbounds %struct.Point, %struct.Point* %0, i32 0, i32 0
  store i32 1, i32* %1
  %2 = getelementptr inbounds %struct.Point, %struct.Point* %0, i32 0, i32 1
  store i32 2, i32* %2
  %3 = load %struct.Point, %struct.Point* %0
  ret %struct.Point %3
}

在这里,我们可以看到 %0 寄存器用于存储 Point 结构体。

getelementptr 指令用于获取结构体的成员。%1 指令获取 x 成员,%2 指令获取 y 成员。

store 指令用于将值存储到结构体的成员中。%1 指令将值 1 存储到 x 成员中,%2 指令将值 2 存储到 y 成员中。

最后,load 指令用于加载结构体。%3 指令加载 Point 结构体,并将它作为返回值返回。

总结

函数的返回值可以是各种类型,包括普通值和结构体。了解返回值是如何存储和获取的,对于理解 iOS 逆向安防至关重要。