【问题标题】:Understanding when a function in assembly is either void or int了解汇编中的函数何时为 void 或 int
【发布时间】:2022-12-14 11:47:52
【问题描述】:

所以我在汇编中有以下代码:

我知道函数的参数是两个(x 和 y 或任何字母)。问题是我不知道如何从汇编代码中看出函数是否返回任何东西,因为它只说ret。这个函数会被认为是 void 还是 int?任何理解的解释将不胜感激。

【问题讨论】:

  • edit你的问题,把源码图片改成文字。
  • 如果函数不是 void,那么返回的值似乎是可疑的。我的意思是它只是第一个参数的值。为什么函数需要返回它,特别是因为它没有被修改?更重要的是:为什么你需要知道它是 void/int?如果您从 C 调用该函数,您可以将其声明为其中之一并让它编译/运行。

标签: assembly x86-64 reverse-engineering att


【解决方案1】:

两者皆有可能。通常,您不能仅通过查看函数代码来判断是否要返回一个值。为了更加确定,查找调用此函数的函数并检查这些函数是否检查返回值。如果其中任何一个这样做,就会有一个返回值。如果不是,则可能没有返回值。

【讨论】:

    【解决方案2】:

    如果需要,返回值在寄存器 eax / rax 中传递。

    由于该寄存器被修改,首先从变量x读取,然后添加到另一个变量,这两种情况都是可能的。在void 的情况下,eax 寄存器可以包含任何值,不限于*x

    void f(int *x, int *y) {
        *y += *x;
    }
    
    int g(int *x, int *y) {
        int z = *x;
        *y += z;
        return z;
    }
    

    作为反例,

    void h(int x, int *y) { *y += x; }
    
    add %edi, (%rsi)
    ret
    

    显然会将 void 作为唯一可能的签名。

    int 返回值 OTOH 的一个明确指示是在 eax/rax 的最后修改之后没有副作用(例如存储)时 - 没有合理的优化编译器会产生这样的代码,但是合理的混淆编译器可能会。

    【讨论】:

    • 问题中的示例有一个 add,而不是像您的示例那样的 mov
    • 你有倒退的参数。 rdi 是第一个参数。
    • 但是返回值可以在使用自定义调用约定的函数中的不同寄存器中返回,例如手写的或优化编译器发出的优化内部函数
    【解决方案3】:

    在高级语言中通常有函数的形式化概念。函数具有以下特点:

    • 签名——描述参数和返回值类型

      许多语言还允许可选参数(如果调用者未指定,通常会提供默认值)

      许多语言允许可变参数,通常在固定参数之后

    • 要么是返回值类型,要么是 void——但不能两者兼而有之

    • 仅限于单个入口点

    • 多个可能的返回点——必须全部返回相同的类型

    上面的大部分相当于限制/限制,告诉我们我们不能做什么,这些限制允许语言实现在构建时捕获错误,同时也防止在运行时出现某些混乱(难以发现错误)。

    这些形式化部分是在多年使用汇编语言的经验之后发展起来的,汇编语言缺乏这些形式化,因此缺乏这些限制/限制。

    在汇编代码中,没有函数概念的形式化。因此,需要打破的规则更少,您可以:

    • 有多个入口点
    • 同一个函数有不同的签名
    • 从不同的返回点返回不同类型的值(包括void)
    • 在未正确调用函数的情况下从一个函数跳转到另一个函数
    • 等..

    这个函数会被认为是 void 还是 int?

    我们实际上并不确定,因为组装,尤其是拆卸不提供该信息。

    %eax 寄存器被允许用作临时寄存器,因此在那里保留一个值并不能告诉我们太多信息。 %eax中的值被下一条指令使用,所以我们无法从中推断出%eax是否是返回值。如果 mov 后面没有跟 add,那么 mov 对于 void 函数来说将是不必要/无用的浪费,这会使参数倾向于返回值而不是 void return。

    我们也可以推断出函数总是%eax 中放置一个“有用的”值——也就是说,它在每个返回的代码路径上将返回值放入%eax 中。但是,考虑到该功能的简单程度,这并没有多大意义。

    ret 并没有告诉我们任何关于返回类型的信息。

    我知道该函数的参数是两个(x 和 y 或任何字母)

    参数寄存器用作指向 4 字节项目的 8 字节指针,这些项目可能是 intunsigned int,这意味着参数是 int *unsigned int *(或两者的混合!)。

    正如 fuz 所说,可以通过查看调用者是否使用返回值来收集更多信息,如果使用(并且已知代码是正确的),则函数返回一个值。但是,如果他们不这样做,则不能保证该函数是 void,因为调用者可以选择忽略返回值。这就是为什么说如果没有调用者使用返回值,它是可能void

    【讨论】:

    • 意味着参数是- 我可能会说第一个参数是const int *
    猜你喜欢
    • 2014-09-30
    • 2019-10-03
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-11
    • 1970-01-01
    相关资源
    最近更新 更多