栈溢出实验
利用淹没返回值控制程序执行流程
操作系统: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. 运行一下,可以看到成功的运行到了我们想要的位置,虽然随后就因为堆栈不平衡导致程序崩溃,但是这是后续需要解决的问题,这一节的目的我们已经成功达到了。