缓冲区溢出入门

    技术2026-05-07  4

                                  缓冲区溢出入门

    从接触C语言的数组开始,就提到过缓冲区溢出问题。一般是往数组中写入数据长度超过了数组的大小,C语言常用的库函数strcpysprintfstrcat等都非常容易导致缓冲区溢出。还记得教科书告诉我们当程序溢出后会发生不可预料的后果,但是一些有意的缓冲区溢出利用可以让“不可预料的结果”变成我们所期望的结果。

    先来个演示程序吧! buf.c

    #include <stdio.h>

     

    void fun()

    {

       printf("why here!!!/n");

       exit(0);

    }

    int main(int argc, char *argv[])

    {

       int a[1]={0};

       a[2]=(int)fun;

     

       return 0;

    }

    我用的是VC6的编译器在命令行下

    C/K>cl /FA buf.c

    运行程序;

    why  here!!!

     

    仔细分析程序发现我们并没有调用fun函数,但是函数确实被执行了,唯一可能的就是调用就是在a[2]=(int)fun;

    要搞清楚是怎么回事,需要一些C语言的底层和汇编的知识,主要是关于堆栈和汇编的call/ret.类似上面程序中的int a[1]等局部变量是在堆栈中分配的,地址从高到低;而想malloc或者new动态申请分配的内存在堆中分配,地址才低到高。在汇编中call指令用来调用函数,并将call下面的指令的地址压入堆栈中,

    也就是通常说的保存现场,在去执行调用的函数,当函数执行完以后,通过ret指令将压入堆栈的保存的地址返回给EIP寄存器,程序在根据EIP的内容继续执行。

             下面再来看看程序buf的汇编代码

    在命令行输入type buf.asm

    可见变量$SG336是我们定义的字符串”why here!!!”

    这个是函数fun的定义

    这是main()函数

    通过对比main()fun()不难发现在函数开头都会有这么两句

    通常称之为栈帧,EBP用来保存当前的栈基址,ESP为栈顶指针,在目前的系统中每当有数据入栈,ESP就减4,上面两句的意思就是保存当前BEP,并将BEP的内容传给ebp

    而当函数执行完以后

    则通过上面的方式回复EBP的值,ret将返回地址给EIP作为函数调用后的下一条指令的地址。

     

    对应的源文件的 int a[1]={0};

    此时堆栈中的结构大致如下:

    a[0]  】【   ebp   】【  eip  】地址从右往左递减;

    pop/push指令每次操作进出的是双字的大小,而整型数组每个元素的大小也为4个字节,刚好相等。

    因为只定义了变量int a[1],编译器在堆栈上只为其分配了4个字节的空间,即指令push ecx;而越界的a[2]

    的地址比a[0]8个字节,刚好位于堆栈中保存eip值的地址;

    a[2]=(int)fun;

    以上为源程序中覆盖eip值的语句及其对应的汇编代码,在c语言中函数名不是变量,a[2]=(int)fun

    a[2]=(int)&fun的作用是一样的,就是将函数的起始地址赋值给a[2],也就是说main()执行完后,ret

    返回的EIP中的值被替换成了fun()的起始地址,如前面所演示的fun函数就这样被执行了。

    这只是一个简单的堆栈缓冲区溢出的利用,当然缓冲区溢出也可能发生在其他内存段中,例如堆和bss,

    通过利用这些漏洞,一些不怀好意的人可以改变程序的控制流程,以完成其不可告人的目的!

     

     

     

     

                                                    

    最新回复(0)