【发布时间】:2017-12-19 16:49:58
【问题描述】:
我正在使用 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:
func:
现在我们可以看到两个函数栈中都没有保存返回地址(至少这是我的看法)。
如果一个黑客想通过堆栈粉碎来破解这个程序,他会在函数堆栈中的哪个地址被他非法编辑?
【问题讨论】:
-
在 x86 架构上,
call指令将返回地址放入堆栈。在其他架构上,等效指令可能会将其放入寄存器中,然后被调用者可以在必要时将其保存在堆栈中。 -
@Jester "
call指令将返回地址放入堆栈" --- 是否可以在两张图片中看到这一点?我只是想看看像0x00001234这样的真实地址。 -
不,值在堆栈上。你可以看到它,例如通过在
callq之后执行x/a $rsp(即在func的入口处,地址0x400687)。 -
但是您的屏幕截图根本没有显示堆栈的内容。你到底想通过这些来证明什么?为什么要查看代码而不是查看堆栈?
-
Jester 写的内容加上检查
rsp在call之前/之后的值(我猜是在 gdb 中的“print $rsp”?),看看它是如何做到的rsp-=8(与push rax会改变它)。 “堆栈”内存就像任何其他常规内存一样,rsp几乎就像任何其他寄存器一样,所有“堆栈”魔法只是push/pop/call/ret/leave/enter/..指令使用rsp值作为指向内存的指针隐式。 IE。push rax几乎等于sub rsp,8mov [rsp],rax,它只是更原子,并且不影响标志。 (和“堆栈粉碎”= 更改rsp地址周围的内存内容)。