算算时间,距离上一次做实验的时间已经有4天了,虽然我已经尽量抓紧时间看,无奈训练占据了相当一部分时间。Lab2主要介绍栈和栈溢出相关内容,总结如下。
这里我先给出一个通用栈模型。
存在栈溢出的代码为:
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];// add local buff
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
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!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}
0x01 覆盖变量
首先我们要实现的是对变量的覆盖,分析verify_password函数可以知道,溢出位置为strcpy函数,而返回值为局部变量authenticated。所以我们可以输入长字符串覆盖掉栈中authenticated变量的值。此时栈帧如图。
经过strcmp函数的比较,如果参数字典序大于密码则authenticated值为0x1,如果小于则为0xFFFFFFFF(补码)。所以如果我们输入包含8个字符并且大于密码的字符串,则字符串末位\0即0x00会将authenticated的值覆盖为0x00000000,这时会判定密码正确。
我们首先用IDA将程序打开,找到溢出位置。注意,ida中整个程序的函数图形化调用在_main_0对应的窗口。在图中找到溢出位置,按空格键后需要跟随几次才能找到溢出位置。记录下内存地址。
再用olleydbg打开,ctrl+g输入地址0x401059,按F2设下断点,按F9运行,在程序窗口中输入8个字符q,查看堆栈情况。
可以看到,我们成功将authenticated的值覆盖为0x0。
0x02 覆盖返回值
既然我们可以覆盖变量,那么我们当然可以继续向下覆盖返回地址了,只要控制了返回地址我们就控制了EIP寄存器,而控制了EIP寄存器我们就控制了一切。
所以我们可以再向下覆盖12个字节,其中8~12字节将返回地址控制,这里我们将返回地址覆盖为密码正确后执行的操作。
我们通过olleydbg找到其内存地址为0x401122,对于不可见字符我们可以通过ue16进制编辑。
所以shellcode为:
4321432143214321"@
特别需要注意的是,在我们用16进制编辑器写入内存地址时,由于机器采用的是小端模式,所以需要将内存地址逆序,即22 11 40 00
0x03 植入代码
我们已经控制了eip,那我们可不可以控制机器执行在栈中写入的指令呢?这完全是可以的,虽然这种方式已经被淘汰掉了属于上古时代的东西,不过还是极具历史意义的。
这里我们控制程序进行弹框,shellcode我们可以直接使用书中配套的,不过需要修改两处数据。
一是调用messagebox函数所在内存地址,每台机器都不一样。书中是使用VC++ 6.0配套的工具depends进行查看,但是使用了depends取得的内存地址是错误的,所以可以写一个调用messagebox的C程序,通过olleydbg几步跟随,可以找到其在内存中的地址为0x767C7E60。
二是最后覆盖返回值时指向的地址,这里我们通过olleydbg查看堆栈取得。
最后shellcode如下
3跾hwesthfail嬆SPPS竊~|v袗悙悙悙悙悙悙悙悙悙悪悙悙惏?
效果如图: