算算时间,距离上一次做实验的时间已经有4天了,虽然我已经尽量抓紧时间看,无奈训练占据了相当一部分时间。Lab2主要介绍栈和栈溢出相关内容,总结如下。

这里我先给出一个通用栈模型。
1.png
存在栈溢出的代码为:

  1. #include <stdio.h>
  2. #define PASSWORD "1234567"
  3. int verify_password (char *password)
  4. {
  5. int authenticated;
  6. char buffer[8];// add local buff
  7. authenticated=strcmp(password,PASSWORD);
  8. strcpy(buffer,password);//over flowed here!
  9. return authenticated;
  10. }
  11. main()
  12. {
  13. int valid_flag=0;
  14. char password[1024];
  15. while(1)
  16. {
  17. printf("please input password: ");
  18. scanf("%s",password);
  19. valid_flag = verify_password(password);
  20. if(valid_flag)
  21. {
  22. printf("incorrect password!\n\n");
  23. }
  24. else
  25. {
  26. printf("Congratulation! You have passed the verification!\n");
  27. break;
  28. }
  29. }
  30. }

0x01 覆盖变量

首先我们要实现的是对变量的覆盖,分析verify_password函数可以知道,溢出位置为strcpy函数,而返回值为局部变量authenticated。所以我们可以输入长字符串覆盖掉栈中authenticated变量的值。此时栈帧如图。
2.png

经过strcmp函数的比较,如果参数字典序大于密码则authenticated值为0x1,如果小于则为0xFFFFFFFF(补码)。所以如果我们输入包含8个字符并且大于密码的字符串,则字符串末位\0即0x00会将authenticated的值覆盖为0x00000000,这时会判定密码正确。

我们首先用IDA将程序打开,找到溢出位置。注意,ida中整个程序的函数图形化调用在_main_0对应的窗口。在图中找到溢出位置,按空格键后需要跟随几次才能找到溢出位置。记录下内存地址。
4.png

再用olleydbg打开,ctrl+g输入地址0x401059,按F2设下断点,按F9运行,在程序窗口中输入8个字符q,查看堆栈情况。
5.png

可以看到,我们成功将authenticated的值覆盖为0x0。


0x02 覆盖返回值

既然我们可以覆盖变量,那么我们当然可以继续向下覆盖返回地址了,只要控制了返回地址我们就控制了EIP寄存器,而控制了EIP寄存器我们就控制了一切。

所以我们可以再向下覆盖12个字节,其中8~12字节将返回地址控制,这里我们将返回地址覆盖为密码正确后执行的操作。

我们通过olleydbg找到其内存地址为0x401122,对于不可见字符我们可以通过ue16进制编辑。

所以shellcode为:

  1. 4321432143214321"@

特别需要注意的是,在我们用16进制编辑器写入内存地址时,由于机器采用的是小端模式,所以需要将内存地址逆序,即22 11 40 00


0x03 植入代码

我们已经控制了eip,那我们可不可以控制机器执行在栈中写入的指令呢?这完全是可以的,虽然这种方式已经被淘汰掉了属于上古时代的东西,不过还是极具历史意义的。

这里我们控制程序进行弹框,shellcode我们可以直接使用书中配套的,不过需要修改两处数据。

一是调用messagebox函数所在内存地址,每台机器都不一样。书中是使用VC++ 6.0配套的工具depends进行查看,但是使用了depends取得的内存地址是错误的,所以可以写一个调用messagebox的C程序,通过olleydbg几步跟随,可以找到其在内存中的地址为0x767C7E60。
6.png

二是最后覆盖返回值时指向的地址,这里我们通过olleydbg查看堆栈取得。

最后shellcode如下

  1. 3hwesthfailSPPS竊~|v袗悙悙悙悙悙悙悙悙悙悪悙悙惏?

效果如图:
7.png