我目前正在尝试开发对IA32架构中的汇编的理解。我以为我理解了堆栈结构,直到我运行了一些缓冲区溢出漏洞利用代码,它将返回地址推送到堆栈顶部然后返回。令我惊讶的是,该程序返回到我已经推入堆栈的地址。我最初认为基本指针将用于返回,因为它位于返回地址旁边。但是,我没有触摸基指针。那么,汇编如何知道返回的位置?
感谢。
答案 0 :(得分:3)
ret
指令只是弹出堆栈顶部的任何内容,并将其用作返回地址。没有什么花哨的事情发生。如果你没有跟踪你的堆栈大小并碰巧在错误的时间执行ret
(在堆栈顶部没有正确的返回地址的情况下),你有一个错误。
答案 1 :(得分:1)
x86有几种方法可以返回。
最简单的是RET指令。这将从堆栈中获取当前地址,将其放入EIP并瞧。
因为x86架构是CISC,所以它也有扩展功能,所以你有一个带有size参数的RET。这个用于在ADD返回后将ADD运行到堆栈后替换基本的RET指令:
RET 16
-- becomes
RET
ADD 16, esp
这允许您在返回的同时编写单个指令来取消参数变量。在这种情况下,调用约定非常重要,因为调用者负责在堆栈上添加参数变量,并且被调用者将它们从堆栈中删除!
因为x86处理器带有所谓的段,所以有一条RET指令可以从堆栈中检索段和IP地址。您现在不会在常规程序中使用此指令,因为内存模型足够大以避免它,但它是可用的。这称为FAR RET。
英特尔处理器还支持4级保护,因此内核可以受到保护(用户程序无法访问),驱动程序也可以驻留在单独的保护级别,以避免内核对驱动程序的破坏。 RET指令也可用于在这些级别之间切换。再一次,信息可以在堆栈中找到。
最后,对于中断处理程序也有一个特殊情况。在标准程序中,您永远不会看到仅在内核中使用的程序。每当处理器生成中断时,都会调用一个特殊的处理程序。该处理程序必须使用特殊的IRET指令(中断返回)返回。那个也从堆栈中获取数据,但它有更多的信息保存在那里,例如寄存器和状态标志。
ESP -> +-------+
| ... | <-- local variables
EBP -> | ... | <-- previous frame pointer (EBP)
| ... | <-- registers that cannot be modified are saved here
| EIP | <-- IP address saved by CALL instruction
| ... |
| ... | <-- parameters to your function
+-------+
| ... | <-- local variables of caller
EBP -> | ... | <-- previous frame pointer (EBP)
.........