关于基址指针和堆栈指针的问题
我正在写一份报告来总结堆栈。如果您点击我的个人资料,您会看到我这样做已经有一段时间了。现在,我遇到了一些麻烦,因为在 GDB 上它向我展示了与 Visual Studio 上不同的东西。
因此,我不太确定我对基指针和堆栈指针的理解,如果我错了,我希望有人能引导我走向正确的方向。
对于 x86 计算机,堆栈通常是向下增长的(从较高的内存地址到较低的内存地址)。
所以当程序开始时,我们调用main函数。
一般来说,在每次函数调用的入口处,都会在当前esp位置创建一个栈,这就是我们所说的“栈顶”。这是正确的吗?
当旧的 ebp 被压入堆栈时,它是否被压入 esp 最初指向的位置?
之后,esp 将向下移动,指向一个空的内存位置,对吗?
最后,esp 总是在变化,向下移动指向下一个可用的内存空间。这是正确的吗?
esp 是按字节移动,还是按 4 个字节向下移动?
我知道有很多问题。但感谢您抽出时间!
谢谢您的回复,先生!
@iSciurus
我很困惑每个人如何定义 esp 指向被推入堆栈的最新条目。
对于x86,由于堆栈向下增长,根据您的解释,esp将首先指向堆栈的最低地址。当我查看汇编代码时,我们发现
0x080483f4 <+0>: push %ebp
0x080483f5 <+1>: mov %esp,%ebp
0x080483f7 <+3>: sub $0x10,%esp
esp 减少了 16 个字节。所以这是该函数调用的堆栈的大小。局部变量紧接在返回地址之后(ebp-4、ebp-8 等)。那么 esp 这里的总体目的是什么?据我了解,当我们尝试访问小于该地址的地址时,就会发生堆栈溢出。
最后一件事是:当我们说栈顶时,我们指的是最低地址(对于x86)。
这就是我脑海中的画面(向下增长)
[Parameter n ]
...
[Parameter 2 ]
[Parameter 1 ]
[Return Address ] 0x002CF744
[Previous EBP ] 0x002CF740 (current ebp)
[Local Variables ]
-- ESP
抱歉这些长问题。但我真的很感谢你的帮助。
I am writing a report to summarize stack. If you click on my profile you will see that I have been doing this for a while. Right now, I have some troubles because on GDB it shows me a different thing than on visual studio.
As a result, I am not too sure about my understanding of base pointer and stack pointer, and I am hoping that someone can lead me in the right direction if I am wrong.
For x86 computer, stack is typical growing downward (from higher memory address to lower).
So when a program begins, we called the main function.
In general, at the entry of each function call, a stack is created at the current esp location, and this is what we called "the top of the stack". Is this correct?
When the old ebp gets pushed onto the stack, is it pushed onto where the esp was first pointed to?
Afterward, the esp will move down to point to an empty memory location, is that correct?
Finally, esp is always changing, moving down pointing at the next available memory space. Is that correct?
Does esp move per byte, or per 4 bytes down?
I know there's a lot of questions. But thanks for your time!
Thank you for the response, sir!
@iSciurus
I am confused how everyone define esp pointing at the most recent entry that was pushed onto the stack.
For x86, since the stack grows downward, from your explanation, the esp will first point at the lowest address of the stack. When I look at the the assembly code, we have
0x080483f4 <+0>: push %ebp
0x080483f5 <+1>: mov %esp,%ebp
0x080483f7 <+3>: sub $0x10,%esp
So esp is decremented 16 bytes. So this is the size of the stack of this function call. Local variables come right after return address (ebp-4, ebp-8, etc). So what is the overall purpose of esp here? From what I understand, stack overflow occurs when we try to access an address smaller than that.
The last thing is: when we say the top of the stack, are we referring to the lowest address (for x86).
This is the picture I have in mind (growing downward)
[Parameter n ]
...
[Parameter 2 ]
[Parameter 1 ]
[Return Address ] 0x002CF744
[Previous EBP ] 0x002CF740 (current ebp)
[Local Variables ]
-- ESP
Sorry for these long questions. But I really appreciate your help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正确地说,堆栈帧是在当前 esp 位置创建的,而不是堆栈本身。堆栈在线程启动时创建一次,每个线程都有自己的堆栈,它只是进程内存空间中的一块区域。堆栈帧是在每个函数入口实际创建的,它是线程堆栈内的一个区域。
不,它被推送到地址[old_esp - 4](或x64中的[old_rsp - 8]),因为esp是堆栈的顶部并且指向最低使用的地址。堆栈中的下一个 DWORD(或 QWORD)是空闲的,并且 ebp 被推送到那里。
是的,这通常是通过 sub esp, value 完成的
否。首先,esp 向下移动,指向堆栈中使用的最低地址,而不是下一个可用空间。其次,请记住,esp 可能指向任何地方,而不仅仅是堆栈:直到您使用与堆栈相关的指令(如push/pop)之前都可以。
Esp 按机器字大小移动:在 x86 中,每 4 个字节移动,而在 x64 中,每 8 个字节移动。
esp 的总体目的几乎总是相同的:存储堆栈顶部。所有与堆栈相关的指令(例如 pop/push)都使用 esp 作为参数。在 x86 中,ebp 和 esp 都用于存储有关堆栈帧(相应的底部和顶部)的信息。也许,您对这种冗余感到困惑。然而,在 x64 中,只有 rsp 用于基于堆栈的参数,rbp 是通用寄存器。
堆栈中的缓冲区溢出怎么样,当代码尝试写入高于数组(或结构或其他)的最后一个元素时,通常会发生缓冲区溢出。堆栈向下增长,而数组向上增长。当我们写得更高时,我们可以访问返回地址、SEH 处理程序以及调用者的内部变量。
是的,当我们说顶部时,我们指的是最低地址。因此,大多数调试器以相反的顺序显示堆栈:
这里,ESP 指向所有数据“上方”的值,看起来更像“顶部”。尽管它仍然是使用率最低的地址。
To be correct, a stack frame is created at the current esp location, not the stack itself. The stack is created once at a thread startup, and each thread has its own stack, which is just an area in the process memory space. The stack frame is what actually created at each function entry, and it is a region inside the thread stack.
No, it is pushed onto the address [old_esp - 4] (or [old_rsp - 8] in x64), because esp is the top of the stack and points to the lowest used address. The next DWORD (or QWORD) in the stack is free and ebp is pushed there.
Yes, this is typically done with sub esp, value
No. First, esp is moving down pointing at the lowest used address in the stack, not at the next available space. Second, keep in mind that esp may point to anywhere, not only to the stack: it is ok until you use stack-related instuctions like push/pop.
Esp moves per machine word size: in x86 it moves per 4 bytes, while in x64 it moves per 8 bytes.
The overall purpose of esp is almost always the same: to store the top of the stack. All stack-related instructions like pop/push use esp as their argument. In x86 both ebp and esp are used to store information about the stack frame (the bottom and the top correspondingly). Maybe, you got confused about this redundancy. However, in x64 only rsp is used for stack-based parameters, rbp is a general purpose register.
What about buffer overflows in stack, they often occur when the code tries to write higher than the last element of an array (or struct or whatever). Stack grows downwards, but arrays grow upwards. When we write higher, we can access the return address, the SEH handler as well as internal variables of our caller.
Yes, when we say top, we mean the lowest address. So, most debuggers show the stack in reverse order:
Here, ESP points to the value "above" all the data and looks more like the "top". Though it is still the lowest used address.