C缓冲区学习1 ---缓冲区的作用和副作用
这学期学了计算机学院王彦老师的计算机安全概论,算是一门入门级的课程,但是自己还是长了很多的见识的,学到缓冲区溢出攻击的时候,突然想到自己拖了很久的一件事情,就是好好的总结和学习一下C语言缓冲区的问题。时间过了这么久,真是有些惭愧了。这篇博文应该算是入门级的了,欢迎大家指教。
C语言可以用指针直接操作内存地址,这是他的一个优势,但是也是安全最容易出问题的地方。我们知道C语言是没有内存保护的,他假定C语言负责内存的安全。过去的经验告诉我,Objective-C语言也是这样的,相反java就提供了内存保护的机制。详细的就不多说了。说一下缓冲区的来历。
在linux上写程序,了解操作系统的话,我们都知道,会涉及到系统调用。有事我们看不到,直接使用C标准库已经实现好的函数,这时候往往我们调用C标准库中的函数,比如getc(),实际上在标准库实现getc的过程中就会进行系统的调用。
常见的会用到系统调用的函数是C库中的文件读写函数,比如一些以f开头的函数。
现在我们看一下,在没有缓冲区的情况下,利用gcc编译程序并运行,从文件读入一个字符的过程:
用户态程序调用 getc(FILE* stream) ---> GNU C库 getc ---->系统调用 -----> GNU C返回代码 ---->用户态getc返回 --->读入成功
其中红色文字部分表示比较耗时,代价比较高。试想如果我们读入10000个字符,那么这样一个耗时操作就要执行1000次。
所以,C中引入了缓冲区,缓冲区实际上是一块内存空间,这样当进行系统调用的时候,就不只读一个字符了,C标准库中stdio.h中定义了BUFSIZE就是缓冲区的大小,即每次读入数据的大小。这样结构就发生了变化:
用户态程序调用 getc(FILE* stream) ---> GNU C库 getc ---->系统调用 ----->读入缓冲区----> GNU C返回代码 ---->用户态getc返回 --->读入成功
遮样当时图读取第二个字符的时候,就免去了红色的部分,而是用缓冲区取代了原来的系统调用部分,大大减少了系统调用的次数。提高程序的效率。
但是这样就引发了一个很多同学在学习C的过程中经常遇到的问题
/////那就是getchar清空缓冲区的例子,后续会更新这个例子
------技术的分割线
缓冲区溢出带来的问题
下面就是我学到的内容了,首先先了解一下C语言函数进栈的操作。
图片来自哈工大计算机学院王彦老师课件
那么,怎样使缓冲区溢出呢,简单的说就是用大于BUFSIZE的数据去填充一个BUFSIZ的缓冲区,由于C没有内存保护,并不会禁止程序的编译和执行,所以“子程序变量”中溢出的字符就会覆盖返回地址,邪恶的同学应该想到攻击的办法了。
假如我们有一段攻击代码,只要我们将返回地址改写为攻击代码的位置就可以在函数返回时跳转到我们期待的代码了,那么linux下常见的攻击代码就是shell code ,想学习shell code的朋友可以参照 这篇文章,写的很棒http://blog.csdn.net/sepnic/article/details/6158424
但是实际中,由于linux本身的一些安全特性,shell code的地址预测的难度增加了,所以现在常用的方法是nop填充,即在shell code 的前面加入空语句,这样指针就会顺序的执行,自然的跳到shell code的位置,加大了命中shell code的范围。
缓冲区是一把双刃剑,极大的优化了程序的同时,也有各种各样的问题,接下来会给大家介绍一些常用的函数,就体现了这些,效率的同时也隐含了很多的问题。