天天看点

EIP&EBP&ESP 寄存器

接触过缓冲区溢出的朋友对这个绝对不陌生,EIP,EBP,ESP寄存器。这里先不解释,先看一段代码吧。

char a[8] = "zpf06188";
    for (int i=0;i<8;i++)
    {
        printf("%# x \n",&a[i]);
    }      

在VC6.0编译器里面,这样的代码是会报一个array bounds overflow的错,下标越界了。这是编译器直接对数组下标的检查,换一种方法,用拷贝函数呢?

#include<stdio.h>
#include<string.h>
char name[] = "abcdefghijklmnopqrstuvwxyz";
int main()
{
    char output[8];
    strcpy(output, name);
    for(int i=0;i<8;i++)
        printf("%# x  ",output[i]);
    return 0;
}      

输出结果就成了:

EIP&amp;EBP&amp;ESP 寄存器

调试结果指向:

EIP&amp;EBP&amp;ESP 寄存器

注意这里的值70 6F 6E 6D 分别是什么?对照码表就可以查出来是ponm,当然存储方式应该是mnop。

EIP&amp;EBP&amp;ESP 寄存器

堆栈的存储是低位向高位存储,整个操作完成,main函数执行完毕之后,堆栈中的EBP,EIP要回复回去,但是由于在strcpy的时候,拷贝的字符长度已经超过了数组限定的值,这就导致写入的值覆盖了EIP,EBP。最后弹出EIP的时候却发现,它原来的东西没了,被写成了6d6e6f70了。所以出错了。

EBP是"基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer). 在破解的时候,经常可以看见一个标准的函数起始代码:

ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。

EIP:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。

栈的基本模型:

参数N ↓高地址
参数… 函数参数入栈的顺序与具体的调用方式有关
参数 3
参数 2
参数 1
EIP 返回本次调用后,下一条指令的地址
EBP 保存调用者的EBP,然后EBP指向此时的栈顶。
临时变量1
临时变量2
临时变量3
临时变量…
临时变量5
void fun(void)
{
   printf("hello world");
}
void main(void)
{
  fun()
  printf("函数调用结束");
}