Yve*_*ves 6 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;
}
Run Code Online (Sandbox Code Playgroud)
然后我使用gdb来获取它的程序集:
main:
func:
现在我们可以看到在两个函数堆栈中没有保存返回地址(至少这是我的观点).
如果一个黑客想用堆栈粉碎破解这个程序,那么函数堆栈中的哪个地址会被他非法编辑?
就在这里.callq在它之前和之后立即检查堆栈.您会发现callq现在后面的指令地址出现在堆栈顶部,RSP减少了8.
该callq指令使下一条指令的地址被压入堆栈.相反,retq函数末尾的指令会使堆栈上的地址弹出RIP.
现在我们可以看到两个函数栈中都没有保存返回地址(至少这是我的看法)。
您实际显示的是反汇编代码,而不是堆栈。
调用者通过callq指令将返回地址压入堆栈。在进入被调用函数的那一刻,它在堆栈的顶部,即:在那个时刻,rsp包含存储返回地址的地址。
p/x $rsp显示rsp寄存器的值,即:栈顶地址,因为rsp 指向栈顶。
x/x $rsp显示位于栈顶的内存内容(即:位于指向地址的内容rsp)。
考虑到这些信息,您可以x/x $rsp在进入被调用函数的那一刻(在将其他任何东西压入堆栈之前)运行命令以获取返回地址。
您还可以使用该命令info frame检查当前堆栈帧。显示的带有名称的字段saved rip对应于当前函数的返回地址。但是,您需要在创建当前函数的堆栈帧之后和销毁它之前(即:在被调用者内部之后mov %rsp,%rbp但之前pop %rbp)运行此命令。