函数栈&EIP、EBP、ESP寄存器
0x01 前言
学习逆向和pwn的基本条件:看得懂汇编,深入了解C语言,熟练使用OD,IDA等调试软件。咱是菜鸡,也没人带,只能自己摸索,慢慢来。
这里学习一下EIP、EBP、ESP这三个寄存器,寄存器有许多,32位寄存器就有九个,还有16位和8位的寄存器,那为什么单独拿这三个来看呢,因为在很多情况下我们在调试的时候最注意的就是这三个寄存器,其实这几个寄存器都是为“栈”而生,下面将结合图片分别谈谈这几个寄存器。
0x02 栈的结构
概念就不说了,只需牢记它的几个属性:
1、先进后出。
2、在内存中表现为从高地址往低地址增长。
3、栈顶:栈的最上方(低地址区)。
4、栈底:栈的最下方(高地址区)。
栈就往弹夹压子弹,最先进弹舱的反而是最后被射出去的。
0x03 EIP、EBP、ESP寄存器
EIP存储着下一条指令的地址,每执行一条指令,该寄存器变化一次。
EBP存储着当前函数栈底的地址,栈低通常作为基址,我们可以通过栈底地址和偏移相加减来获取变量地址(很重要)。
ESP就是前面说的,始终指向栈顶,只要ESP指向变了,那么当前栈顶就变了。
每个函数栈基本都是这样的构造
理解一下,由于栈是由高地址向低地址生长的,栈在被使用完后必须恢复原来的状态,才能保证程序正常运行,然而恢复或者变化栈顶/栈底值需要依靠ESP和EBP两个寄存器。函数调用前基址指针必须先被压入栈,也就是必须先压EBP(基址指针),但是栈在用完是要还原的,所以在调用新函数时,需要先将原来的基址入栈,保存之前任务的信息,然后将栈顶指针的值赋给EBP(ESP一直处于栈顶),将之前的栈顶作为新的基址(栈底),然后在这个基址上开辟相应的空间用作被调用函数的堆栈。函数返回后,从EBP中可取出之前的ESP值,使栈顶恢复函数调用前的位置;再从恢复后的栈顶弹出之前的EBP值,因为这个值在函数调用前一步被压入栈。这样,EBP和ESP都恢复了调用前的位置,堆栈恢复函数被调用前的状态。
0x04 实例
我这里随便找了一个程序作为调试程序,就是逆向工程核心原理里的一个demo
LittleEndian.exe
载入od,我们的例子是00401000这个函数栈
我们在push ebp下断点( 00401000 ),然后F4运行到此处进行调试。
查看此时EBP和ESP的值
EBP:0019FF80
ESP:0019FF3C
初始情况下,这里EBP值大于EBP 可以看出来这辆有一定关系,基址指针地址较高。
我们F7执行前两句 push ebp mov ebp,esp
此时,前两句已执行,ESP和EBP的值也发生了变化。
EBP: 0019FF38
ESP: 0019FF38
为什么会变化呢,来分析一下,push ebp,push是压栈操作,相当于给栈压入了一段数据,所以要减去4字节,为什么要减呢,前面我们就说了,栈的生长方向是由高向低的,所以要想生长就得减,但是为什么是减四个字节呢,因为在32位的计算机中,一个寄存器占四个字节,所以push一个寄存器就要开辟四个字节的内存,所以减去四字节。
所以ESP地址变为了: 0019FF3C -0X4= 0019FF38
由于mov命令 所以ebp值等于esp 也是 0019FF38
继续向下,sub esp,0x10,因为下面的操作需要新的内存才能执行,所以还要开辟空间,所以esp-0X10(16)
ESP= 0019FF38 -16= 0019FF28
省略部分过程,到0040102B
此时ESP是没有变化的
继续向下执行
mov esp,ebp
此时ESP变回了 0019FF38
然后pop ebp,出栈,继续返还0x4的空间,所以ESP变为了 0019FF3C
ESP和EBP恢复了调用之前的位置。
EBP:0019FF80
ESP:0019FF3C
为什么恢复了原来的地址呢,我们继续分析一下,首先,EBP出栈了,这个时候栈是空的,因为栈基址都出栈了,所以栈顶会变为初始值 0019FF3C ,由于mov直接把ebp赋值给了esp,所以占用的内存不需要加回去,被自动释放了。
所以变为了 0019FF3C ,但是为什么EBP变成了 0019FF80 这个初始值呢, ebp不是一直保存着esp的初始地址么?
所以重点就在pop这个语句了。pop ebp究竟表达什么意思?ebp的值起初存在了栈中,出栈以后,它的值就恢复了原样。所以这个意思很重要。pop的意思也许就是把弹出的值赋给我们的变量,pop ebp,也就是把存在栈中的值弹出来赋给ebp。
总结一下:
1、两句的mov ebp,esp实际上是把ebp进栈后的栈顶地址给了ebp。
2、在ebp没有出栈前,它会一直保存ebp进栈以后的栈顶值,也就是1的值。
3、在ebp出栈前,需要把esp恢复到只有ebp在栈中时的值。
4、出栈后,esp自然恢复到ebp进栈以前的初始值,而pop ebp则恢复了ebp的初始值。
5、pop的语义很重要,pop ebp的意思是把当前栈顶的元素出栈,送入ebp中,而不是让ebp出栈,这点必须明确!