maikehong 发表于 2023-7-25 22:52:43

求助甲鱼大哥大神很棘手关于sp和bp的问题

题目是如下:
当入口程序是
        ebp=0019ff84
        esp=0019ff78

   :00401000 push ebp
   :00401001 mov ebp, esp
   :00401003 add esp, FFFFFFF8                                                        esp = 0019FF70
   :00401006 mov eax, dword ptr                                         mov eax, @loc1
   :00401009 mov ax, word ptr                                         mov ax, @loc2
   :0040100D mov al, byte ptr                                                 mov al, @loc3

我就是不明白ebp 跟 esp是什么关系?并且程序入口开始 ebp 也不等于 esp
程序入口时候 应该是
                mov ebp, esp
                push ebp
                这样才对。
我就这些不懂 困扰了我 几十年。

Mike_python小 发表于 2023-7-25 22:53:08

首先,ebp(Extended Base Pointer)和esp(Stack Pointer)是x86体系结构中的寄存器。它们在汇编语言中用于管理函数调用和栈操作。

ebp通常用作帧指针,它保存了当前函数的堆栈帧的基址。堆栈帧是函数在堆栈上分配的一块内存区域,用于存储局部变量、参数以及其他与函数执行相关的信息。ebp指向堆栈帧的底部,也就是栈顶的上一个位置。

esp是栈指针,它指向当前堆栈的栈顶。当函数被调用时,esp会指向分配给该函数的新堆栈帧的顶部。在函数执行期间,esp会随着栈上的数据的推入和弹出而移动。

在你提供的代码片段中,ebp和esp的初始值分别为:

ebp = 0019ff84
esp = 0019ff78


接下来的指令序列是:

:00401000 push ebp
:00401001 mov ebp, esp
:00401003 add esp, FFFFFFF8
:00401006 mov eax, dword ptr
:00401009 mov ax, word ptr
:0040100D mov al, byte ptr


解释这些指令的作用:

1. push ebp:将当前ebp的值压入栈中,目的是为了保存上一个函数的ebp值。

2. mov ebp, esp:将esp的值赋给ebp,这样ebp现在指向了当前函数的堆栈帧的底部。

3. add esp, FFFFFFF8:将esp的值减去8个字节,即将esp向下移动8个字节。这是为了给局部变量和其他数据预留空间。

4. mov eax, dword ptr :从ebp-4的内存位置复制一个双字(4字节)到eax寄存器中。

5. mov ax, word ptr :从ebp-6的内存位置复制一个字(2字节)到ax寄存器中。

6. mov al, byte ptr :从ebp-7的内存位置复制一个字节到al寄存器中。

至于你提到的程序入口时的顺序,请注意在函数调用过程中,一般情况下参数会被压入栈中,然后调整栈指针,最后将栈指针的值赋给ebp。因此,很可能是在代码片段之前已经执行了一些与函数调用有关的指令。

希望这样解释对你有帮助!如果还有其他问题,请随时提出。

球一个最佳答案谢谢啦!这对我非常重要!{:10_254:}{:10_254:}{:10_254:}

isdkz 发表于 2023-7-25 22:53:36

首先,让我们先解释一下EBP和ESP在x86体系结构中的作用。

EBP(Base Pointer)和ESP(Stack Pointer)都是x86体系结构中的寄存器,他们的主要作用是用于堆栈操作。在函数调用时,EBP用于保存上一个函数的堆栈基址,而ESP则是当前堆栈的栈顶。由于堆栈是向下增长的,ESP的值总是小于EBP。

一般来说,函数开始时会将EBP的值push到堆栈上以保存它,然后将当前的ESP值复制到EBP中。这就建立了一个新的堆栈帧,可以在函数内部使用形式的寻址来访问函数的参数和局部变量。

然后,你提到的代码是常见的函数调用的prologue(序言)部分,这部分代码负责设置新的堆栈帧。代码的解释如下:

:00401000 push ebp            ; 将原来的EBP值保存到堆栈
:00401001 mov ebp, esp      ; 将当前的ESP值复制到EBP,建立新的堆栈帧
:00401003 add esp, FFFFFFF8   ; 分配8个字节的局部变量空间(实际上是减去8,因为堆栈是向下增长)


而在你的代码中,ebp=0019ff84和esp=0019ff78,这就意味着EBP的值大于ESP的值,也就是说堆栈已经有了一些内容。

所以,在你的代码中,虽然开始时EBP和ESP的值不同,但是通过执行push ebp和mov ebp, esp两条指令后,就建立了新的堆栈帧,同时保留了上一个函数的堆栈基址。

关于你说的“程序入口时候 应该是 mov ebp, esp push ebp 这样才对”的部分,这其实是有误的。一般来说,函数调用的序言应该是push ebp和mov ebp, esp。你可以理解为先保存旧的堆栈基址,然后建立新的堆栈帧。如果你先执行mov ebp, esp,那么原来的EBP值就丢失了,当函数返回时就无法恢复到正确的堆栈帧,会造成错误。

maikehong 发表于 2023-7-25 23:00:39

从新来我把完整的代码搞一下

maikehong 发表于 2023-7-25 23:16:46

本帖最后由 maikehong 于 2023-7-25 23:26 编辑

1 就一个子程序 程序如下

TestProc proc ;名为TestProc的子程序
local @loc1:dword, @loc2:word,@loc3:byte;一共占了 4+2+1 = 7个字节
mov eax,@loc1;对应类型进行存储,然后返回
mov ax, @loc2
mov al, @loc3
ret
TestProc endp
;====================1 以上是程序结束======2 以下是汇编情况如下============================================

   :00401000 push ebp                                                                                                                <------------3 一打开汇编停在这里入口程序中
   :00401001 mov ebp, esp
   :00401003 add esp, FFFFFFF8                                                esp = 0019FF70
   :00401006 mov eax, dword ptr                               mov eax, @loc1
   :00401009 mov ax, word ptr                                         mov ax, @loc2
   :0040100D mov al, byte ptr                                         mov al, @loc3

============================ 4 请查看如下esp和ebp 情况 =======================================

ebp=0019ff84
esp=0019ff78

我的问题在这里有以下几点:

1 ebp和esp 的值 一开始就不相等
2 既然不相等估计前面是不是被定义的7个字节 占了 但是呢 不管 + 还是 - 也是不相等
                  local      @loc1:dword, @loc2:word,@loc3:byte         ;一共占了 4+2+1 = 7个字节
3 在汇编0基础中 甲鱼哥说ebp 是为了协助 esp 他俩并不相等他只是 ep-->寄存器 指令中没有显性的给出段地址,段地址就默认在ss中。

4 我还是搞不懂ebp本身是干嘛的 也没有被定义比如 至少前面应该
mov ebp, esp
mov push ebp
这样我好理解呀? 可是编译器就不这么做。      

maikehong 发表于 2023-7-25 23:34:50

maikehong 发表于 2023-7-25 23:16
1 就一个子程序 程序如下

TestProc proc ;名为TestProc的子程序


ebp 算老几 凭什么 ebp 就先入栈

maikehong 发表于 2023-7-25 23:39:33

Mike_python小 发表于 2023-7-25 22:53
首先,ebp(Extended Base Pointer)和esp(Stack Pointer)是x86体系结构中的寄存器。它们在汇编语言中用 ...

废物一个

maikehong 发表于 2023-7-25 23:40:48

用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值,段地址默认为 SS 段寄存器中的值bp指的是 内存地址sp指的是指针。我百度了一下,上面你们俩个,都是菜鸟。瞎回答

maikehong 发表于 2023-7-25 23:42:34

maikehong 发表于 2023-7-25 23:40
用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值,段地址默认为 SS 段寄存器中的值bp指的是...

sp也是内存地址,我X ,到底是哪个地址。

maikehong 发表于 2023-7-25 23:44:27

maikehong 发表于 2023-7-25 23:42
sp也是内存地址,我X ,到底是哪个地址。

难道 ep指的是数据内存,sp指的是栈内存,但是ep就跟栈挂钩的的呀,也是指的是栈地址,难道有2个栈内存地址 1上1下。哎。ep 你把我害惨了。耽误了我几十年。

maikehong 发表于 2023-7-25 23:47:11

Mike_python小 发表于 2023-7-25 22:53
首先,ebp(Extended Base Pointer)和esp(Stack Pointer)是x86体系结构中的寄存器。它们在汇编语言中用 ...

下次给你一个重要的答案。我还没理解好ep 我自己先冷静冷静。做黑客不易啊。

maikehong 发表于 2023-7-25 23:52:40

isdkz 发表于 2023-7-25 22:53
首先,让我们先解释一下EBP和ESP在x86体系结构中的作用。

EBP(Base Pointer)和ESP(Stack Pointer)都 ...

如果 : EBP用于保存上一个函数的堆栈基址,而ESP则是当前堆栈的栈顶的
                之后干嘛还要mov ebp,esp那么如果这样,对于在下一个子函数被调用时候。怎么解释呢

人造人 发表于 2023-7-26 00:06:15

maikehong 发表于 2023-7-25 23:52
如果 : EBP用于保存上一个函数的堆栈基址,而ESP则是当前堆栈的栈顶的
                之后干嘛还要mov ebp,esp那么 ...

ebp中保存的不是上一个函数的堆栈基址
ebp中保存的是当前函数中局部变量的基址
就是说,用ebp来访问当前函数中的局部变量

人造人 发表于 2023-7-26 00:23:59

$ cat main.c
#include <stdio.h>

void func(int i) {
    if(!i) return;
    printf("%p\n", &i);
    func(i - 1);
}

int main(void) {
    func(3);
    return 0;
}
$ gcc -m32 -g -Wall -o main main.c
$ ./main
0xff84bcc0
0xff84bca0
0xff84bc80
$

0000118d <func>:
#include <stdio.h>

void func(int i) {
    118d:       55                      pushl%ebp
    118e:       89 e5                   movl   %esp,%ebp
    1190:       53                      pushl%ebx
    1191:       83 ec 04                subl   $0x4,%esp
    1194:       e8 73 00 00 00          calll120c <__x86.get_pc_thunk.ax>
    1199:       05 5b 2e 00 00          addl   $0x2e5b,%eax
    if(!i) return;
    119e:       8b 55 08                movl   0x8(%ebp),%edx
    11a1:       85 d2                   testl%edx,%edx
    11a3:       74 2c                   je   11d1 <func+0x44>
    printf("%p\n", &i);
    11a5:       83 ec 08                subl   $0x8,%esp
    11a8:       8d 55 08                leal   0x8(%ebp),%edx
    11ab:       52                      pushl%edx
    11ac:       8d 90 14 e0 ff ff       leal   -0x1fec(%eax),%edx
    11b2:       52                      pushl%edx
    11b3:       89 c3                   movl   %eax,%ebx
    11b5:       e8 96 fe ff ff          calll1050 <printf@plt>
    11ba:       83 c4 10                addl   $0x10,%esp
    func(i - 1);
    11bd:       8b 45 08                movl   0x8(%ebp),%eax
    11c0:       83 e8 01                subl   $0x1,%eax
    11c3:       83 ec 0c                subl   $0xc,%esp
    11c6:       50                      pushl%eax
    11c7:       e8 c1 ff ff ff          calll118d <func>
    11cc:       83 c4 10                addl   $0x10,%esp
    11cf:       eb 01                   jmp    11d2 <func+0x45>
    if(!i) return;
    11d1:       90                      nop
}
    11d2:       8b 5d fc                movl   -0x4(%ebp),%ebx
    11d5:       c9                      leavel
    11d6:       c3                      retl

000011d7 <main>:

int main(void) {
    11d7:       8d 4c 24 04             leal   0x4(%esp),%ecx
    11db:       83 e4 f0                andl   $0xfffffff0,%esp
    11de:       ff 71 fc                pushl-0x4(%ecx)
    11e1:       55                      pushl%ebp
    11e2:       89 e5                   movl   %esp,%ebp
    11e4:       51                      pushl%ecx
    11e5:       83 ec 04                subl   $0x4,%esp
    11e8:       e8 1f 00 00 00          calll120c <__x86.get_pc_thunk.ax>
    11ed:       05 07 2e 00 00          addl   $0x2e07,%eax
    func(3);
    11f2:       83 ec 0c                subl   $0xc,%esp
    11f5:       6a 03                   pushl$0x3
    11f7:       e8 91 ff ff ff          calll118d <func>
    11fc:       83 c4 10                addl   $0x10,%esp
    return 0;
    11ff:       b8 00 00 00 00          movl   $0x0,%eax
}
    1204:       8b 4d fc                movl   -0x4(%ebp),%ecx
    1207:       c9                      leavel
    1208:       8d 61 fc                leal   -0x4(%ecx),%esp
    120b:       c3                      retl

在func函数中,ebp + 0x8就是局部变量 i 的地址
这个程序输出了3个局部变量 i 的地址,都不一样

    118d:       55                      pushl%ebp
    118e:       89 e5                   movl   %esp,%ebp
    // ...
    11d5:       c9                      leavel
    11d6:       c3                      retl

这4行指令保证了正常的函数调用和返回
如果不这么做,要如何才能实现这个程序的输出效果呢?
自己调试一下这个程序,看一看为什么输出的这3个局部变量 i 的地址不一样
看一看是如何通过ebp访问局部变量的

maikehong 发表于 2023-7-26 01:17:56

人造人 发表于 2023-7-26 00:23
在func函数中,ebp + 0x8就是局部变量 i 的地址
这个程序输出了3个局部变量 i 的地址,都不一样
...

我先分析分析你的代码

maikehong 发表于 2023-7-26 01:20:38

maikehong 发表于 2023-7-26 01:17
我先分析分析你的代码

你的汇编代码 入口地址不是main函数啊,

maikehong 发表于 2023-7-26 01:58:54

maikehong 发表于 2023-7-26 01:17
我先分析分析你的代码

你这个编译器 不准确

maikehong 发表于 2023-7-26 02:13:36

需要在各个角度来看ebp 比如
1 在main函数中 没有调用字程序的情况下 来看ebp 和 esp
2 在main函数中调用 子程序的情况下来看ebp和sp
3 在子程序中 调用 子程序的情况下来看ebp和sp
4 看了很多关于ebp和esp 各个将的云里雾里 都有道理,
5 是不是也要在本身电脑配置的情况下来看esp和ebp

maikehong 发表于 2023-7-26 02:21:28

都说1 esp 永远指向 栈顶(高位),ebp 指向 栈低(低位), 也没有说清楚栈低是啥
都说2ebp是用于保存上一个函数的堆栈基址,也没有说清楚,上个函数栈基地址是啥。难道是main函数的句柄吗
都说3ebp是ss的左膀右臂,神雕侠侣杨过不就是独臂吗?
都说4ebp你算老几呀,一开局就push 凭什么你先push。

Mike_python小 发表于 2023-7-26 07:35:32

maikehong 发表于 2023-7-25 20:39
废物一个

感谢你的自我介绍
页: [1] 2
查看完整版本: 求助甲鱼大哥大神很棘手关于sp和bp的问题