【发布时间】:2016-12-18 20:48:44
【问题描述】:
我希望分析调用函数的汇编代码,并为每个“调用”找出传递给函数的参数数量。我假设我无法访问目标函数,而只能访问调用代码。 我将自己限制为仅使用 GCC 编译的代码,以及 System V ABI 调用约定。 我尝试从每条“调用”指令中扫描回来,但我没有找到足够好的约定(例如,在哪里停止扫描?使用相同参数的两个后续调用会发生什么?)。非常感谢您的帮助。
【问题讨论】:
-
GCC 有两种不同的策略来调用这样的函数。一是它把参数压入堆栈,然后在函数调用后的某个时间清理它们,二是在函数开始时为所有函数调用的传出参数保留空间,并在结束时清理一次.无论哪种方式,堆栈上的函数参数在调用中都是可变的,但只有那些实际传递给该派系的参数。这意味着函数的参数可以在调用之前很久以及跨其他调用放入堆栈。
-
您无法在优化的代码中可靠地分辨出来。而且即使大部分时间做得好,也可能需要人类级别的人工智能。例如函数是否在 RSI 中留下了一个值,因为它是第二个参数,或者它只是在计算 RDI 的值(第一个参数)时将 RSI 用作临时寄存器?正如罗斯所说,gcc 为堆栈参数调用约定生成的代码具有更明显的模式,但仍然不容易检测到。
-
@PeterCordes 嗯...我假设一个基于堆栈的调用约定,但是是的,基于寄存器的调用约定将使它完全不可能。
-
使用相同参数的两个后续调用会发生什么? 编译器总是在进行另一个调用之前重写 args,因为它们假设函数会破坏它们的 args(即使在堆栈上也是如此) )。 ABI 说函数“拥有”它们的参数。我见过的编译器生成的代码从未真正修改过保存其参数的堆栈内存,即使这会启用尾调用:(
-
这会引发支持或不支持 GCC 编译优化的冲突。如果不支持优化,那么生成的代码可能会更有条理,但如果支持优化,我可能不会假设另一个 reg 用作所需 reg 的暂存器的情况,因为它通常需要额外的指令。
标签: function assembly call reverse-engineering