函数堆栈是否保存了返回地址?

时间:2017-12-19 16:49:59

标签: c++ assembly x86-64

我正在使用系统Linux x86学习基于程序集的函数堆栈。

我已经阅读了一些文章,它告诉我函数堆栈(被调用者)将保存调用者调用它的返回地址,以便计算机可以知道函数返回时的继续点。

这就是为什么会有一种攻击:堆栈粉碎。 堆栈粉碎意味着如果我们可以溢出函数堆栈,特别是溢出带有设计地址的返回地址,程序将执行黑客代码。

但是,今天我尝试使用gdb来检查一个简单的c ++程序,但是我无法在任何函数堆栈中找到任何保存的返回地址。 这是代码:

void func(int x)
{
    int a = x;
    int b = 0;  // set a breakpoint
}

int main()
{
    func(10);  // set a breakpoint
    return 0;
}

然后我使用gdb来获取它的程序集:

main

enter image description here

func

enter image description here

现在我们可以看到在两个函数堆栈中没有保存返回地址(至少这是我的观点)。

如果黑客想用堆栈粉碎破解这个程序,那么函数堆栈中的哪个地址会被他非法编辑?

2 个答案:

答案 0 :(得分:5)

是的。在callq之前和之后立即检查堆栈。你会发现callq之后的指令地址现在出现在堆栈的顶部,而RSP已经减少了8。

callq指令使下一条指令的地址被压入堆栈。相反,函数末尾的retq指令会使堆栈上的地址弹出RIP

答案 1 :(得分:4)

  

现在我们可以看到在两个函数堆栈中没有保存返回地址(至少这是我的观点)。

您实际展示的是反汇编代码,而不是堆栈。

调用者通过callq指令将返回地址压入堆栈。在进入被调用者函数时,它位于堆栈的顶部,即:在此时,rsp包含返回地址所在的地址存储

使用GDB检查堆栈

  • p/x $rsp显示rsp寄存器的值,即:rsp 的堆栈顶部的地址指向堆栈顶部。

  • x/x $rsp显示位于堆栈顶部的内存内容(即:位于rsp指向的地址的内容。)

记住这些信息,您可以在进入被调用函数时(在将任何其他内容压入堆栈之前)运行命令x/x $rsp以获取返回地址。

您还可以使用命令info frame检查当前的堆栈帧。名称为saved rip的显示字段对应当前函数的返回地址。但是,您需要在创建当前函数的堆栈帧之后以及在它被销毁之前运行此命令(即:mov %rsp,%rbp之后但在被调用者之前pop %rbp之前)。