msn: [email protected]
来源:http://yfydz.cublog.cn
1. 前言 本文所描述的缓冲区可以是静态的,如通过数组方式定义;也可以是动态的,如通过malloc等函数分配的。 2. 非法操作 对缓冲区的非法操作分以下几种: 2.1 越界访问 最常见的错误操作就是数据越界,就是访问了缓冲区之外的数据,对于读操作,或许还能好点,但对写操作基本就出大问题了。对于数组方式,如果超过不多的话程序也许不会崩溃,但会发现其他数据被改写,如果过多覆盖了函数返回地址的话会造成程序崩溃,如果写入的数据是特殊的shellcode的话(缓冲溢出攻击),会使攻击者获取程序属主的权限;对于动态分配的缓冲区,越界后基本程序立即崩溃了。 数据越界问题是引起程序崩溃最常见的问题,但也是最隐蔽的,尤其在对一定大小的缓冲区进行内容分析处理时很容易出错,如果不随时与缓冲区结束地址进行比较的话,仅凭缓冲区内容是很容易出错的。 2.2 非法字符串操作 字符串的结束标志是“\0”字符,各种字符串操作函数,如strlen,strchr,strrchr,strstr等等都是以此为根据进行操作的,对于缓冲区,不要想当然的一定会在缓冲区中有“\0”字符,如内核网络编程中的一个数据包处理,即使你知道处理的协议是字符协议,如 ftp,telnet等,系统只保证在实际数据区包含了相关的数据内容,但实际数据区外的缓冲区(如果有的话)中的内容是不确定的,并不保证有“\0”字符,因此不要轻易在包的数据区中使用这些函数,除非你明确在缓冲区中某个地方设置了“\0”,否则带来的结果将就是越界访问而造成系统崩溃。 提示:使用字符串操作函数一定要确定缓冲区内有“\0”字符。 2.3 非初始化即使用 在函数中定义的静态缓冲区和用malloc分配的动态缓冲区内的初始数据内容都是随机的,不能直接使用,否则很容易出错。 举例,下面的小程序同时犯了上面所说的三种的错误: void a(void) { char buf[10]; if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0; } 提示: 在对缓冲区操作前,最好用memset()函数对其内容清零;动态分配内存可以用calloc函数代替malloc函数。 2.4 sizeof的误用 数组的大小是可以用sizeof()操作符计算出来的,但如果将数组地址作为参数传递到其他函数后,就不能再对该参数使用sizeof()操作了,这时候编译器就只将其作为一个普通指针,计算出来的值就始终是4(32位操作系统)或8(64位操作系统)。 举例: void a(int *p) { printf("sizeof(p)=%d\n", sizeof(p)); /* */ } void main(void) { int buf[10]; printf("sizeof(buf)=%d\n", sizeof(buf)); /* 10*sizeof(int) */ a(buf); } 在32位操作系统下,程序运行结果为: sizeof(buf)=40 sizeof(p)=4 而对于动态分配的缓冲区,则永远不能用sizeof()计算缓冲区大小,缓冲区大小必须自己记住。 3. 容易引发非法操作的函数 容易引发非法操作的函数主要是那些没有长度参数的缓冲区操作函数,如 gets,scanf,sprintf,strcpy,strcat等,对这些函数的使用要非常注意,最好能形成条件反射式地拒绝使用这些函数。一般需要使用其相应的带“n”的对应函数,如fgets,snprintf,strncat,strncpy等,这方面的安全编程讨论已经很多,任何buffer overflow相关文件都会涉及。 值得注意的是strncpy函数,是很容易出错的,其他字符串操作函数执行完后会自动在目的串结尾添加“\0”结束符,但strncpy不会,需要自己显式添加“\0”。 4. 结论 缓冲区是系统分配出了一块内存区,原则上可以在该内存区内进行任何操作,可以将其视为字符串,也可以视为数据,这都是无所谓,但一定要随时保证进行操作的所有数据地址一定是在缓冲区内才是安全的。