【问题标题】:How to find address of a function without using any pointer or &如何在不使用任何指针或 & 的情况下找到函数的地址
【发布时间】:2014-08-13 01:08:40
【问题描述】:

我在一次采访中被要求在不使用任何指针或 & 的情况下找出函数的地址。我无法回答他的问题,但是当我向他询问答案时,他给出了以下示例,其中函数 f1() 调用函数 f2()。因此,当函数 f2() 被调用时,堆栈会存储返回地址,该地址只不过是函数 f1() 的地址。在函数 f2() 中,我们可以读取堆栈,然后找出存储在堆栈中的地址,即函数 f1() 的地址。谁能详细解释一下我们如何从函数 f2() 中读取堆栈并找出 f1() 的地址。

int main()
{
    f1();
    return 0;
}
void f1()
{
    f2();
}

【问题讨论】:

  • 那是……完全不便携。
  • 没有可移植的纯 C 方式来做到这一点。参见例如stackoverflow.com/questions/16088040/…
  • @sirpsychosexy,这涉及指针,问题标题明确禁止...
  • 这是一个很做作的问题,在面试中用来测试OP对栈帧的了解。
  • 在 gcc 中,您可以使用register int rsp asm("rsp"); register int rbp asm("rbp"); 来获取 rsp 和 rbp 或任何其他寄存器的值,从而从堆栈中获取返回地址,或者直接使用 __builtin_return_address。在 MSVC 中,有办法获取堆栈帧 here

标签: c


【解决方案1】:

需要注意的是,这种方法完全不可移植,因为 T.C.提到,以及如果打开优化几乎肯定不会起作用的额外警告,您可以通过读取缓冲区的末尾来从堆栈中读取返回地址,如下例所示。

int main()
{
    f1();
    return 0;
}
void f1()
{
    f2();
}
void f2() {
    char buf[4];
    printf("%p\n", *(void**)(buf + 8));
}

请注意,上面的数字8 会因操作系统、体系结构和编译器填充而异,因此您很可能必须尝试各种不同的数字才能使其正常工作。 示例中8 的选择假定填充到 4 字节边界,并且在 32 位系统上使用 4 字节指针。

您还必须确保关闭优化。

之所以如此,是因为在函数调用之后的堆栈结构看起来有点像这样。

|Return Address|
|Saved Frame Pointer|
|Local Variables|

观察返回地址的地址高于局部变量。这就是读取缓冲区末尾之后可能会读取返回地址的原因。

这与优化中断的原因是编译器可能决定内联一个或两个函数,或者意识到其中一个根本不做任何事情并完全优化函数调用,这推翻了关于堆栈的假设。

【讨论】:

  • 假设这会起作用,它应该在结束时给你地址f2()跳转到,而不是f1()的地址。
  • 想一想,它实际上会在调用f2() 时为您提供寄存器EBP 的备份值。返回地址为buf+12
  • @Havenard,它非常脆弱,只是一个例子。我假设具有 4 字节指针的 32 位架构。
  • 我认为并不是那么脆弱,C 遵循严格的调用约定,您可以通过将 f2 声明为 void __cdecl f2() 来强制执行它,以防止出现优化问题。在这种情况下唯一剩下的问题是堆栈对齐。
  • @Havenard,虽然优化仍然可以杀死f1,但我相信在这个例子中我们不允许更改其定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
  • 2018-06-12
  • 1970-01-01
  • 2016-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多