原因
C对数组引用不进行任何边界检查,而且局部变量和状态信息(寄存器值,返回地址)都放在栈里。
当对越界数组元素进行写操作,在进行ret时,容易出现严重错误;
造成后果
缓冲区溢出
栈分配字符数组保存一个字符串,但是其长度超出了为数组分配的空间。
- C语言常用的strcpy、sprintf、strcat 等函数都非常容易导致缓冲区溢出问题。
- 程序运行时,其内存里面一般都包含这些部分:
(1)程序参数和程序环境;
(2)程序堆栈(堆栈则比较特殊,主要是在调用函数时来保存现场,以便函数返回之后能继续运行),它通常在程序执行时增长,一般情况下,它向下朝堆增长。
(3)堆,它也在程序执行时增长,相反,它向上朝堆栈增长;
(4)BSS 段,它包含未初始化的全局可用的数据(例如,全局变量);
(5)数据段,它包含初始化的全局可用的数据(通常是全局变量);
(6)文本段,它包含只读程序代码。
BSS、数据和文本段组成静态内存:在程序运行之前这些段的大小已经固定。程序运行时虽然可以更改个别变量,但不能将数据分配到这些段中。
在栈中分配某个字节数组来保存一个字符串,但是字符串的长度超出了为数组分配的空间。C对于数组引用不进行任何边界检查,而且局部变量和状态信息,都存在栈中。这样,对越界的数组元素的写操作会破坏存储在栈中的状态信息。当程序使用这个被破坏的状态,试图重新加载寄存器或执行ret指令时,就会出现很严重的错误。
void echo() { char buf[8] ; gets(buf) ; puts(buf) ; }
输入的字符数量 被破坏的状态
0—7 无
8—11 保存的%ebx的值
12—15 保存的%ebp的值
16—19 返回地址
20+ caller中保存的状态
执行攻击代码exploit code
用一个指向攻击代码的指针覆盖返回地址达到跳转到攻击代码的效果
方式一:攻击代码会使用系统调用启动一个shell程序,给供给者提供一组操作系统函数;
方式二:执行一些未授权的任务,修复对栈的破坏,然后第二次执行ret指令,表面上正常返回到调用者;
蠕虫和病毒的区别
蠕虫:自我复制,可以自己运行,能将自己传播到其他机器
病毒:不能独立运行,把自己添加到包括OS在内的其他程序中。