02.栈溢出实验-利用淹没返回值控制程序执行流程

栈溢出实验

利用淹没返回值控制程序执行流程


一.  实验环境:

操作系统:Windows XP SP3

开发环境:VC++ 6.0

调试器:Ollydbg


二.  实验代码:

#include <stdio.h>

#include<string.h>

#define PASSWORD "1234567"

int verify_password (char *password)

{

    int authenticated;

    char buffer[8];

    authenticated=strcmp(password,PASSWORD);

    strcpy(buffer,password);

    return authenticated;

}

main()

{

    int valid_flag=0;

    char password[1024];

    while(1)

    {

        printf("please input password:       ");

        scanf("%s",password);

        valid_flag = verify_password(password);

        if(valid_flag)

        {

            printf("incorrect password!nn");

        }

        else

        {

            printf("Congratulation! You have passed the verification!n");

            break;

        }

    }

}


三.  溢出原理


程序未对输入的密码进行长度检测,接收密码的缓冲区只有8,而输入的密码最长可以输入1024。判断密码是否正确的变量authenticated存储在栈中,当输入的密码长度大于8时,输入的字符串将冲破缓冲区,淹没authenticated所处的位置。当密码错误时authenticated的值是1,正确的时候authenticated的值是0.这就意味着我们可以构造一个合适的输入字符串来改变判断结果。


本次的程序与上一节的程序的区别为由控制台输入改为读取文件。这是因为很多字符无法由控制台直接输入。


四.  实战调试


本次的程序与上一节只有读取文件和控制台输入的区别,故此不再详细分析。着重分析如何通过覆盖返回值来控制程序执行流程。


1.   首先我们随便在password.txt中随便输入一个字符串保存,然后调试程序,进入main函数开始分析。


2.         因为这次的目的是淹没返回值控制程序流程,所以在进入密码比对函数之前,先记录下函数位置。


3.         单步进入密码比对函数,在刚刚进入函数的时候可以看到ESP的位置就是函数的返回地址。


4.         在经过strcpy函数之后,可以看到password.txt内的字符串成功的覆盖了函数比对结果变量,那么我们可以看到在附近还存储着函数返回地址,那么我们是否可以通过加长password.txt内的文本内容,覆盖掉返回地址。我们可以看到距离返回地址我们需要12个长度的文本,你那么这次我们将password.txt的内容修改为123456781234567812345678,再次调试程序。


5.         再次调试可以看到堆栈内的函数返回地址已经被成功覆盖了,但是执行后会报错,因为38373635的位置上并没有执行,是一片非法内存。


6.         那么我们可以看到在0012FB20的位置上的字符串是1234,根据上下文,我们可以看出是第三个1234,那么我们就把它覆盖成我们想要地址。比如输出比对成功字符串的位置。


7.         那么我们把password.txt中第三个1234改成输出字符串的地址,但是需要注意两点。第一,我们要按照小端序倒着输入返回地址,第二需要修改的是HEX而不是字符串。因此我们需要使用十六进制编辑器010editer进行如下编辑。


8.         然后我们再次调试程序。可以看到堆栈中返回地址已经被我们成功覆盖了。


9.         运行一下,可以看到成功的运行到了我们想要的位置,虽然随后就因为堆栈不平衡导致程序崩溃,但是这是后续需要解决的问题,这一节的目的我们已经成功达到了。