栈溢出实验
shellcode初探
操作系统:Windows XP SP3
开发环境:VC++ 6.0
调试器:Ollydbg
#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[44];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
LoadLibrary("user32.dll");
if(!(fp=fopen("password.txt","rw+")))
{
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!n");
}
else
{
printf("Congratulation! You have passed the verification!n");
}
fclose(fp);
}
程序未对输入的密码进行长度检测,接收密码的缓冲区只有8,而输入的密码最长可以输入1024。判断密码是否正确的变量authenticated存储在栈中,当输入的密码长度大于8时,输入的字符串将冲破缓冲区,淹没authenticated所处的位置。当密码错误时authenticated的值是1,正确的时候authenticated的值是0.这就意味着我们可以构造一个合适的输入字符串来改变判断结果。
本次的程序与上一节的程序区别在引入了windows头文件,用于使用LoadLibrary函数。另外缓冲区的长度从8增加为44。
本次实战着重初步植入简单的shellcode代码。
1. 本次实验的最终目标是在成功溢出后弹出一个消息框。那么首先我们要找到消息框的API地址,如果考虑通用性,那么需要一套负责的过程,现在我们先不考虑这些。先使用Windows自带的工具获取API的地址。
2. 我们需要用到的API是MessageBox,熟悉Windows编程的朋友应该知道,实际上并没有MessgaeBox这个API,我们实际上需要调用的是MessageBoxA或者MessageBoxW,这取决于我们使用的环境是多字节还是宽字符。此次我们那使用的是MessageBoxA,这个API处于USER32.DLL中,我们获取一下USER.DLL基址是0x77D10000,然后获取MessageBoxA的偏移是0x407EA,两者相加就可以获取到MessageBoxA在内存中的入口地址0x77D507EA。
(此处获取到的地址只在我的计算机上生效。如果本地实验,需要本地重新获取。)
3. 此时我们需要确定一下我们需要覆盖的返回地址的位置和BUFFER起始位置。已知缓冲区长度为44,那么第45个字节的00将会覆盖掉密码比对结果。为了方便查看,我们使用abcd作为输入,长度为44的缓冲区则需要11组abcd刚好覆盖。
4. 运行起来,进入到密码比对函数。执行strcpy后观察堆栈的情况。可以看到缓冲区起点是0x0012FAF0,返回地址是0x0012FB24。
5. 那么我们是不是可以这样玩,把返回地址淹没成缓冲区起点,这样指令就会从缓冲区起点开始执行。这样我们就需要把返回地址覆盖成0x0012FAF0,然后再构造一个弹出消息框的机器码用于弹出消息框。
5.1 淹没返回值为0x0012FAF0
5.2这里我们先不讨论shellcode的编写。这会在接下来的大章节中详细描述。我们直接把弹出消息框的机器码填在password中。
5.3进入shellcode执行
5.4溢出成功