标签:
1.1节中我们说到可以利用栈溢出来破坏栈中原有的内容,这一节中,我们就来看看如何争夺到返回地址(EIP),使得我们可以随意控制它的值,这样我们就可以控制程序。来看一个经典的程序:
这个程序的get_print函数中定义了一个大小为11个字节的数组,正常情况下我们的输入应该最多为10个字符(还有一个\0结束符),而gets函数没有明确定义输入的大小,因此,我们可以输入超过10个字符,从而造成栈溢出。如下,输入10个‘A’,一切正常:
图8
当我输入11个‘A’时,虽然顺利打印出来11个‘A’,但是VS2008报了如下错:
图9
运行时错误检查检测到栈崩溃,这是Windows为抵抗栈溢出漏洞利用采用的措施。目前我们还不知道如何绕过,先去掉它,在工程属性中,“C/C++”——“代码生成”的“基本运行时检查”选择为默认值,然后重新编译。
图10
修改后,输入11个‘A’貌似是没什么问题了,但是输入12个‘A’的时候,又出来一个这样的对话框:
图11
缓冲区溢出被检查到了,这当然不是一个好消息。作为经典的漏洞,Windows自然有多种对付招式,这又是一种叫做栈Cookie的保护方式,可以检查到栈溢出。同样,先去掉它,在工程属性中,“C/C++”——“代码生成”的“缓冲区安全检查”选择为否(GS-, 见图10),然后重新编译。这一次,我们输入一大串‘A’:
图12
什么?VS2008又出现了弹窗?别紧张,这次是好消息。
图13
看到熟悉的0xC0000005,表明是访问了不该访问的地址。同时,0x41414141不就是“AAAA”吗?这说明我们输入的“AAAA”已经以某种方式被程序使用了,这果断是好消息。
下面,用Immunity Debugger来看看究竟发生了什么。先找到函数get_print()的代码,在 MOV EBP, ESP语句上下断点:
图14
然后运行到这里,查看栈内容:
图15
回想1.1中的内容,get_print没有参数,因此0012FF14(当前ESP)处为保存的EBP,0012FF18(EBP+4)为返回地址(重要)。
下面两句分配栈帧的代码对栈的影响较大:
/*********************************************************/
MOV EBP, ESP
SUB ESP, 4C
/*********************************************************/
分配了4C(76字节)大小的空间,因此,下面这段栈空间(get_print)是我们关注的内容:
图16
现在,定位到gets函数,我们关注的不是它的调用过程,而是参数,它位于栈上EBP-C的位置,因此,它紧邻保存的EBP。也就是说,这个缓冲区下面是保存的EBP,再下面是保存的返回地址。这很重要,它决定我们需要输入多少内容才能准确地改写保存的返回地址(EIP)。
我们在gets函数之后设断点,并输入以下内容(16个A和4个B):
图17
此时,查看栈内容:
图18
看到了吗?保存的EBP被‘AAAA’覆盖,保存的返回地址(EBP+4)被BBBB覆盖。因为,我们知道局部变量的准确位置,因此,可以准确的知道需要多少字节来覆盖返回地址的内容。
这样,我们就从程序手中争夺了EIP,get_print()返回时,它将跳转到0x42424242处执行,由于该地址不可访问,因此会出现0xC0000005错误。后面,我们将给EIP写入有意义的地址,从而执行我们自己的内容。
标签:
原文地址:http://blog.csdn.net/hustd10/article/details/51108333