使⽤C语⾔你必须知道的常见的字符串错误
参考⾃《C和C++安全编码》。
c语言指针实验总结实验环境:win10 & visual studio 2019
越界
举⼀个⾮常简单的例⼦。
void get_name()
{
char name[8];
puts("Your name?");
gets(name);
return;
}
因为gets()函数的原因,⽆法对⽤户的输⼊长度进⾏控制。
char*gets(char*dest)
{
int c =getchar();
char*p = dest;
while(c !=EOF&& c !='\n')
{
*p++= c;
c =getchar();
}
*p ='\0';
return dest;
}
因⽽程序员往往会设计⼀个⾜够长的数组保证输⼊,如果不加输⼊限制的话,就只能期望⽤户可以⽼⽼实实的配合。对于和善的⽤户⾃然是可以很好的⼯作,对于恶意⽤户⽽⾔,程序就有可能遭重。
同时要注意,字符串的结尾:’\0’也要占⼀位,所以8位的数组不可以输⼊8个字符,如下图所⽰。
在复制和连接字符串中,由于⼀些函数执⾏⽆界复制的操作,可能出现越界操作。
在c语⾔调⽤main()函数时,通常声明为:
int main(int argc,char*argv[])
{
;
}
命令⾏参数作为指向从argv[0]到argv[argc-1]的数组成员并以空字符结尾的字符串指针传⼊main()
在argc>0的情况下,argv[0]指向的字符串是程序名;
在argc>1的情况下,从argv[0]到argv[argc-1]引⽤的字符串是实际程序参数。
在任何情况下,argv[argc]的值都应该是NULL。
当分配空间不⾜以复制⼀个程序的输⼊,就会产⽣漏洞。在下⾯的程序中,提供⼀个超过128长度的字符串就会造成漏洞,攻击者可以把argv[0]设置为NULL来调⽤这个程序。
int main(int argc,char*argv[])
{
char prog_name[128];
strcpy(prog_name, argv[0]);
}
这个程序可以在较早版本的编译器下编译并执⾏。但现在较新的编译器检查了缓冲区溢出,对对象⼤⼩检查失败,直接不予以执⾏。
使⽤strlen()函数可以确定argv[0]到argv[argc-1]引⽤字符串的长度,以便可动态分配充⾜的内存。
int main(int argc,char*argv[])
{
const char*const name = argv[0]? argv[0]:"";
char*prog_name =(char*)malloc(strlen(name)+1);
if(prog_name !=NULL)
strcpy(prog_name, name);
else
;
}
在这⾥只是讲个原理,毕竟在vs的环境下只有替换为strcpy_s才能编译成功。
strcpy()也可替换为更安全的strdup(),接受⼀个指向字符串的指针,然后返回⼀个新分配的复制字符串的指针,最后将返回的指针传递给free()以回收内存。
空结尾
字符串需要以’\0’结尾,为了接下来的实验,添加下条语句:
#define _CRT_SECURE_NO_WARNINGS
#pragma warning( disable : 4996)
之后敲⼊实验代码:
int main()
{
char a[10];
char b[10];
char c[20];
strncpy(a,"0123456789",sizeof(a));
strncpy(b,"0123456789",sizeof(b));
strcpy(c, a);
}
调试如下图所⽰,由于c中没有结束字符,所以直接提⽰c中字符串⽆效。
通过上图我们发现,a和b地址之间相差20位, 接下来测试下⾯代码:
int main()
{
char a[10];
char b[10];
strncpy(a,"0123456789",sizeof(a));
strncpy(b,"0123456789",sizeof(b));
a[2]='\0';
printf("%c\n", b[21]);
printf("%s", b);
}
调试图如下:
可以发现a的地址是0x005cfdec,b的地址是0x005cfdd8,中间相差20位(⼗进制),其中有10位(⼗进制)缓冲区。因为b中没有声明字符’\0’,因此在打印b时会直接冲到a的区域。
差⼀
由于程序员判断边界失误会发⽣的错误。
例如声明了a[6],循环赋值时从0直接赋到6超了⼀位。
有⼀个strncat()函数,功能是在字符串结尾追加n个字符。strncat()会在指定的最⼤长度之后⼀字节的位置写⼊字符串结束符。在C的标准库中,对字符串操作的函数最⼤长度都不是统⼀的,所以容易出现差⼀错误。
int main()
{
char str[5]="0000";
char buff[10]="111111";
strncat(buff, str,sizeof(str)-1);
printf("%s", buff);
}
截断
当⼀个数组不⾜以容纳⼀个字符串时,程序员为了防⽌缓冲区溢出,通常会控制输⼊,这样会发⽣数据的丢失,有时也会产⽣漏洞。
数组写⼊越界
由于c语⾔中字符串由数组实现,所以⼀些不使⽤函数的操作有可能具有安全隐患。
int main(int argc,char* argv[]){
int i =0;
char buff[128];
char* arg1 = argv[1];
while(arg1[i]!='\0'){
buff[i]= arg1[i];
i++;
}
buff[i]='\0';
printf("buff = %s\n", buff);
}
转载请注明出处。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论