当发生call
时,我的理解是调用方的地址被推送到堆栈上,当执行ret
时,它会跳到堆栈中弹出的值。
如果我推送一个值却忘记弹出它,会发生什么?ret
不是简单地pop
它期望的来自堆栈的返回地址,并且被堆栈顶部的实际值严重错误吗?例如:
Function:
mov ax, "A"
push ax
ret
call Function
此外,我对pusha
和popa
也有同样的疑问。如果push
是pusha
之后的一个值,那么popa
现在会在弹出它使用的寄存器数量时使用该值吗?留下堆栈上最初的第一个寄存器,并将每个寄存器恢复为寄存器+1的值?
Stack只是一个内存,它不是"类型化的",无论如何,你总是可以推送一个寄存器,然后弹出另一个寄存器。
ret
只是ip
寄存器中的pop
,因此push/ret是jmp
的低效方式。没有什么魔法能与之前的call
相媲美。
您甚至可以在不使用推送的情况下更改堆栈上的数据,只需使用esp
或ebp
指针即可更改。并且确保您可以意外地覆盖返回地址。导致您的程序意外覆盖返回地址是恶意软件使用的一种众所周知的技术。