弄清楚一个函数是否有返回值 not?

逆向工程 二元分析 静态分析 小精灵 职能 x64
2021-06-14 12:52:23

我正在对x86-64bitELF 二进制文件进行二进制分析所有的二进制文件都是从C语言编译的基本上,对于给定的函数,我想弄清楚这个函数是否有返回值。即在其对应的C代码中,是否return存在有意义的

由于我基本上面对的是汇编代码,因此通过某些类型信息来弄清楚是不可行的。但是,对于普通的x86-64bit汇编程序,调用约定只允许寄存器rax保存返回值,所以我想检查一下rax典型函数调用后的用法,并决定目标函数是否返回值。

下面是一个AT&T语法示例

foo:
   ...
   call bar
   mov 0, %rax  <--- bar should not have a return value

bar:
   ...

在上面的例子中,由于rax立即重置,函数不太可能bar返回值。

另一个例子:

foo:
   ...
   call bar
   jmp *%rax  <----- It is very likely that bar has a return value

对于上述情况,我想如果没有一些积极的过程间优化,我们可以肯定地说它bar返回一个值(一个指针)。

我认为这是另一个(临时)逆向工程任务,但我想可能有一种更“正式”的方法来解决它,对此有什么想法吗?

2个回答

我发现这种方法存在几个问题:

  • 不使用返回双精度值的函数 eax
  • 返回结构的函数不一定使用eax,请参见此处
  • 许多函数的返回值,如free, closeprintf通常被忽略,因此“调用者不读取eax”不会转化为“函数没有返回值”
  • 有“eax被使用”的边缘情况例如,xor [location], eax可能意味着eax也有一个值xor eax, [location],但xor eax, eax意味着可能没有。

在一般情况下,我认为没有万无一失的方法。例如,在一个以

for (i=0; i<somevar; i++)
    somearray[i]=0;
return i;

编译器可能只是决定使用eax循环计数器;这意味着没有理由mov在循环之后再做一次如果调用者忽略 的值eax,则您无法仅从程序集来确定该return语句是否存在。(当然,在这种特殊情况下,任何自尊的编译器都会生成rep stoswsse指令的变体,但您明白了)。

因此,当调用者确实read 时eax,您可以非常确定该函数具有返回值;但是忽略的调用者eax基本上没有任何意义。

并且即使调用者读取eax,您也可以构造用汇编器编写的函数的病态情况,保留eax,以及知道这一点并eax在函数调用中使用的调用者。但是您可能不会在没有故意混淆的软件中遇到这种情况

返回值由编译器使用的调用约定定义,通常仅表示“ eax/的值rax”。

在您的第一个示例中,rax没有重置,它只是设置为 value 0,因此函数很可能以 a 结尾return 0;

在第二个例子中,你应该检查什么是对上一次写raxbar功能。

您可以尝试检查rax函数之后的下一个操作是写入还是读取,基本上如果是读取,则使用返回值,如果是写入,则丢弃返回值。