码迷,mamicode.com
首页 > Windows程序 > 详细

<<栈溢出>> Win32平台栈溢出攻击原理与实践(初级篇)

时间:2015-11-17 00:19:57      阅读:445      评论:0      收藏:0      [点我收藏+]

标签:

 Date-2015/11/16.Monday     

 

                      <缓冲区溢出攻击(Buffer Overflow)>

                     黑客最常用的攻击手段,经常关注网络安全领域的人士对“溢出”这个词都不陌生。说起漏洞的起源,以及为何频繁出现让人防不胜防,就必须要提到一个人:数字计算机之父——冯·诺依曼。  以及他的理论成果:著名的“冯·诺依曼体系结构” ,从ENIAC到至今的超级计算机无一例外都出自这种结构。该结构中 “指令”和“数据”并不加以区分的存储,导致“指令就是数据、数据也可以当作指令来执行!”。

                     专业的讲,内存中EIP指向哪儿,CPU就把哪儿当做指令来执行,如果指令格式不正确,就会出错!如下图1:

 技术分享

                                                   [非法指令! EIP已被覆盖为0x30303030]

          

用一个简短的C程序来说明这个问题:

图2

 技术分享

            可以简单地把缓冲区理解为“数组”,上面这段代码用了两个字符数组,但是没有进行越界检查。运行后直接崩溃! 把Big[]中的字符串“拷贝”至small[],结果就可想而知了。超长数据淹没了数组以外的内存区域,会发生不可预料的后果!缓冲区溢出攻击的艺术就在于“把不可预料的后果,变成我们想要的结果!

            关于“缓冲区溢出”更多更为详细的介绍 请参考网络资源;

            下面则通过调试器去发生溢出的程序内部去一探究竟。

为了更好地演示溢出的数据如何听候我们的指挥,我们将重新编写一个含有溢出漏洞的程序(由上篇博文中的password修改而来),代码如下

//buff_2.cpp

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#define PASSWORD "1234567"

int ypp(char *);

int main()

{

       char password[1024];

       int n=0;

       FILE *fp;

       if(!(fp=fopen("password.txt","rw+")))

       {

                printf("Cannot Open this File!\n");

                exit(0);

       }

       fscanf(fp,"%s",password);

                n=ypp(password);

                if(n)

                          printf("Error!\n");

                else

                          printf("Congratulation!\n");

    fclose(fp);

       return 0;

}

int ypp(char *password)

{

       int num;

       char  buff[8];   //人为构造栈溢出

       num=strcmp(password,PASSWORD);

       strcpy(buff,password);

       return num;

}

 

需要注意的是,“密码”已经不再是手动输入了,而是由文件读取。

也即事先我们要新建立个password.txt用以存储密码;

将文本文件放在当前目录下,用VC6.0编译(Debug版)

图3

 技术分享

Ctr+F5运行

图4

 技术分享

         在源程序中,由#define定义的“1234567”为真密码,可是“43214321”怎么能够验证成功呢???

         是时候说说“栈帧”的概念了。

(Stack),是一种“先进后出”的数据结构。EBP指向栈底(高地址),ESP指向栈顶(低地址),压栈(PUSH)、弹栈(POP)均在栈顶进行。调用某个函数就需要为这个函数新开辟一段“栈空间”,函数的返回地址、参数的传递均需要栈来配合(相当于中转站)。

在Ollydbg中栈帧的形式如下:

图5

 技术分享

 需要说的全部都在“图”里。

 当前EBP=0012FB20  那么你也就知道了在栈帧中到底哪块儿是EBP(高亮显示)。

在EBP下方比它高出4h字节的0012FB24中右边有串很醒目的提示“RETURN to……

也许你已经猜到了,没错,这个就是“返回地址”位于00401005处。

图6

 技术分享

图7 (ypp()就是在此处跳转的)

 技术分享

根据图5,总结一下,就是下面这个图了

图8

 技术分享

前面password.txt中保存的8位密码“43214321”占满了buff,别忘了还有个NULL(‘\0’)

判断程序的逻辑,strcmp()是用来比较真假密码的函数,密码相同、num置为0;密码不同、则是1。 

在图5中可以看到0012FB1C中为00000000,main中if(0)自然会输出“Congratulation!”

 

*****************************************************************************

在了解以上信息之后,溢出攻击才正式开始!

我们已经知道,溢出的数据会改变与之相邻的局部变量num的值;

缓冲区的长度为8个字节;

局部变量num要占4个字节;

前栈帧占用4字节;

返回地址同样也是4个字节。

这次,我们要淹没的就不仅是num了。而是num、前EBP和返回地址

 

所以我们要在password中放置20个字节的数据(8+4+4+4)

第一步:重置password.txt,输入5组“4321”保存;

图9

 技术分享

第二步:把PE载入OD进行调试

        Ctrl+G直接跟踪main,并在此处下断点。

图10

 技术分享

第三步:单步至<0040109F  CALL  buff_2.00401005 >  F7步入ypp()

        在ypp()中可继续单步,同时注意堆栈面板的变化。

        或者直接按F9执行!就会出现开篇的第一张图的情况。

图11

技术分享

先不要关闭OD,需要记录一些数据

在堆栈面板里面,查看EIP的地址

图12

 技术分享

EIP被改写为0x31323334,程序无法继续执行下去!

还记得我们之前说过的 “把不可预料的后果,变成我们想要的结果!”吗?

<这一步是关键!>

我们想要让EIP重新指向“正确的地址”! 这是完全可行的!

这个“正确的地址”可以是0x004010C5,图13

 技术分享

使EIP指向这条指令,调用printf直接输出“Congratulation!

我们需要对password.txt改写。注意:内存地址是十六进制数,无法用键盘输入的!

下面我们将使用十六进制编辑器UltraEdit对文本文件进行十六进制修改。

只修改“后4个字节”

图14

 技术分享

图15

 技术分享

将末尾四个字节数据修改为004010C5,逆序输入!!!(即C5  10  40  00

Ctrl+S保存

图16

 技术分享

重新打开password.txt

图17

技术分享

到这里已经接近尾声了.

我们编辑的这段“密码”文件严格来说并不算Shellcode

不过下一篇,将会讲解如何在缓冲区内布置“可执行代码”

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

双击exe文件,可以看到成功出现“Congratulation!”

由于堆栈不平衡,程序在输出字符串之后随即崩溃!

图18

 技术分享

 

 

 

初级部分完结!

中、高级部分,待续………………

 

END.

YPP-2015/11/16.  23:42

yppshell@outlook.com

<<栈溢出>> Win32平台栈溢出攻击原理与实践(初级篇)

标签:

原文地址:http://www.cnblogs.com/yppshell/p/4970459.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!