C 中大多数缓冲区溢出问题可以直接追溯到标准 C 库。最有害的罪魁祸首是不进行自变量检查的、有问题的字符串操作(strcpy、strcat、sprintf 和 gets)。
如果说之前所提到的一些问题可能只是影响部分功能的实现,那么缓冲区溢出将可能会造成程序运行终止,被不安全代码攻击等严重问题,因此我们不得不特别重视。
本专栏文章的主题是,通过防御性编程保护代码不受缓冲区溢出攻击。
永远不要使用 gets()
#include <stdio.h>
int main(int argc, char **argv){
char buf[128];
gets(buf);
}
编译:
编译时编译器会检查不建议使用"gets"。该函数从标准输入读入用户输入的一行文本,它在遇到 EOF 字符或换行字符之前,不会停止读入文本。也就是:gets() 根本不执行边界检查。因此,使用 gets() 总是有可能使任何缓冲区溢出。
可以将程序改为:
#include <stdio.h>
#define BUFSIZE 128
int main(int argc, char **argv){
char buf[BUFSIZE];
fgets(buf,BUFSIZE,stdin);
}
编译运行:
作为一个替代方法,可以使用方法 fgets()。它可以做与 gets() 所做的同样的事情,但它接受用来限制读入字符数目的大小参数,因此,提供了一种防止缓冲区溢出的方法。
C 编程中的主要陷阱
库函数中还有一些函数也可能造成缓冲区溢出,我们应该尽量避免使用它们。
strcpy()
strcat()
sprintf()
scanf()
sscanf()
fscanf()
vfscanf()
vsprintf
vscanf()
vsscanf()
如果有任何可能,避免使用这些函数。在大多数情况下,都有合理的替代方法。我们将仔细检查它们中的每一个,所以可以看到什么构成了它们的误用,以及如何避免它。
strcpy()函数将源字符串复制到缓冲区。没有指定要复制字符的具体数目。复制字符的数目直接取决于源字符串中的数目。如果源字符串碰巧来自用户输入,且没有专门限制其大小,则有可能会陷入大的麻烦中!
如果知道目的地缓冲区的大小,则可以添加明确的检查:
if(strlen(src) >= dst_size) {
...
}else {
strcpy(dst, src);
}
或者使用strncpy() 库函数:
strncpy(dst, src, dst_size-1);
dst[dst_size-1] = '\0'; /* 这样做是为了安全!*/
如果 src 比 dst 大,则该函数不会抛出一个错误;当达到最大尺寸时,它只是停止复制字符。注意上面调用 strncpy() 中的 dst_size-1。如果 src 比 dst 长,则那给我们留有空间,将一个空字符放在 dst 数组的末尾。
确保 strcpy() 不会溢出的另一种方式是,在需要它时就分配空间,确保通过在源字符串上调用 strlen() 来分配足够的空间:
dst = (char *)malloc(strlen(src));
strcpy(dst, src);
strcat()函数非常类似于 strcpy(),除了它可以将一个字符串合并到缓冲区末尾。它也有一个类似的、更安全的替代方法 strncat()。如果可能,使用 strncat() 而不要使用 strcat()。
使用 sprintf() 和 vsprintf() 与使用 strcpy() 一样,都很容易对程序造成缓冲区溢出。
继续, scanf系列的函数也设计得很差。在这种情况下,目的地缓冲区会发生溢出。考虑以下代码:
#include <stdio.h>
int main(int argc, char **argv){
char buf[256];
sscanf(argv[0], "%s", &buf);
//sscanf(argv[0], "%255s", &buf);
}
如果输入的字大于 buf 的大小,则有溢出的情况。幸运的是,有一种简便的方法可以解决这个问题。考虑以下代码,它没有安全性方面的薄弱环节。
就是在百分号和 s 之间的 255 指定了实际存储在变量 buf 中来自 argv[0] 的字符不会超过 255 个。其余匹配的字符将不会被复制。
总结
缓冲区溢出造成的危害非常大,可能导致程序运行终止或程序运行异常且难以定位问题。
"缓冲区溢出"漏洞是一个由来已久的漏洞类型,虽然现代操作系统的编译器,已经可以很大程度的阻止此类型漏洞的出现,但是作为一名C/C++程序员,还是有必要对此类漏洞的原理进行一定了解哈。
所以缓冲区溢出的原理非常简单,总结起来就是一句话:程序向缓冲区写入了超过缓冲区最大能保存的数据。
程序猿编码欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:
网友评论