我现在正在学习高级汇编语言,并且一直在讨论堆栈的概念。我认为我理解得很好,但在实践中我有一些问题。
堆栈增长,ESP寄存器始终指向堆栈的顶部...低内存中的地址。如果某些东西被推入堆栈,那么ESP应该减少。
EBP被用作帧指针,据我所知,应该总是超过ESP。
然而,通过以下计划:
stdout.put(esp, nl);
stdout.put(ebp, nl);
push(ike);
stdout.put(esp, nl);
stdout.put(ebp, nl);
push(ike);
stdout.put(esp, nl);
stdout.put(ebp, nl);
pop(eax);
pop(eax);
pop(eax);
pop(eax);
stdout.put(esp, nl);
stdout.put(ebp, nl);
似乎并非如此。看输出:
0018FF6C 0018FF70
0018FF68 0018FF70
0018FF64 0018FF70
0018FF74 0018FF70
EBP始终相同,第一次推送时ESP减少4个字节,第二次按下则减少4个字节。
在此之后我很困惑。在我的前两次弹出后,ESP应该回到它开始的地方。如果我没有把任何东西推到堆栈上,我怎么能再做两次弹出?我突然出现了什么?
EAX的进一步弹出和打印显示一些数字,然后是0,然后是更多的数字。所以,我肯定会弹出一些东西......但是什么呢?我的程序存储器属于哪个部分,为什么没有受到影响?
为什么EBP根本不受影响?
另外,为什么ESP减少了4个字节,而不是8?
如果有人能帮助我理解这一点,我将非常感激。
答案 0 :(得分:1)
EBP不会被推/弹指令修改,它是手动设置的,所以除非你自己更改它,否则它将保持不变。
IKE的推送导致4字节的更改,所以显然你在这里处于32位模式。
EAX(32位)的4个弹出将导致16字节(10h)的变化,就像它们一样。
不确定这里有什么问题。似乎按照我的预期工作?
答案 1 :(得分:0)
除了你推送和弹出的东西之外,堆栈还用于保存每个函数的堆栈帧(即局部变量),返回地址,旧ebp(特别是当前ebp所在的位置)和函数的参数。所以你弹出的是你的功能的堆栈框架 如果你反汇编一个程序,你会看到:
push param3 ; suppose func takes 3 parameters, they're
push param2 ; pushed in reversed order (C-style)
push param1
call func ; call pushes also the return address, i.e. the
; address of the instrucion after the call
...
func:
push ebp ; this is done for preserving the caller's stack frame
mov ebp, esp ; now we set up the beginning of func's stack frame
sub esp, smth. ; and its width, enough to fit all func's variables
此时堆栈将类似于:
00000058: arg3
00000054: arg2
00000050: arg1
0000004c: return address
ebp -> 00000048: caller's ebp
... ... -
00000034: random stuff | func's stack frame, random because they
00000030: random stuff | are uninitialized
esp -> 0000002c: random stuff -
最后,您只需按下4个字节,因为这是您机器的字大小,这样您就可以保存整个寄存器。一次性拍摄