strcpy漏洞分析
前段时间发现一个有趣的代码:
char str[1];
strcpy(str, "wwwwwwwwwwwwwwwwwwwww");
printf("%s", str);
getchar();
运行结果:
大家可以看到,我使用strcpy向一个长度为1的数组赋一个长度为21的字符,并且被赋值了,还被打印了出来!
那么下面来解释一下为什么会产生这样的原因!
在CPU看来内存是连续的,是一组线性的地址空间!
而C++编译器是不会检查越界问题的,这里我使用的是VSC++编译器(VS是根据后缀名调用不同的编译器)
strcpy属于标准C库代码,这个函数也不会去检查越界问题,这个函数会直接将内存中的值更改!
我们可以断点调试看一下内存:
首先先将数组的首地址打印出来:
printf("%p", str);
最后下断点
运行结果:
在选择VS自带的内存视窗
注意此选项只有在断点调试阶段才有效
将地址输入进去:
可以看到这个地址空间里有很多字节,有的字节是有数据的,这些数据可能是没有被初始化的随机数也可能是已经分配好了数据的字节,也有基于内存对齐而分配的地址空间,可以看到上面有很多数据为00的字节,这些都是基于内存对齐而分配的,刚刚的str数组并没有被初始化,但是它只占一个字节,所以可以确定第一个字节即使它的内存空间
,
注意此时断点在strcpy处,也就是说此时这个函数还没有被执行
我们按F10调试运行
走了一步此时在看地址空间:
可以看到strcpy把不属于str的地址空间给改掉了,上面说过内存中地址是连续的,strcpy把连在str后面的地址空间里的内存给改掉了!
由此可以知道strcpy函数的编写思路是将一个地址开始的空间修改,修改大小为数值大小,且不会去检查数值大小是否超出变量空间大小!
要知道,操作系统允许程序随意修改访问自己进程空间下的内存,那么有可能会造成,其他变量内存数据混乱的现象,如果你数值在给大一点儿超出进程下的空间大小,访问到其他进程里去了,那么操作系统会就给杀掉,因为在虚拟内存的保护模式下!
那么在来解释一下,为什么printf打印时会将其打印出来,printf也不会检查边界,它只看内存中\0,也就是说,它的编写思路是:从内存中的某个地址开始读取数据,直到遇到\0结尾!
我们数组默认也是有\0的就像上面的str[1],这个数组是无法赋值的,因为它只能存储一个\0!
我们来赋值试试
看,我们明明赋予了一个d的字节,却告诉我们,我们赋予了两个字节的数据,也就是说编译器默认的给我们增加了一个\0在后面!
所以我们定义字符数组变量时,即使给予了256个大小字节空间实际只能访问255个,因为最后一个用于结尾!
所以这也是为什么printf能打印出来的原因,我们来修改一下赋值,在中间一行加入\0试试:
运行结果:
可以看到printf在地址空间里不停的打印数据,直到遇到了\0
当我们在编程时很多IDE都会检查一些字符后面是否带有\0,一旦没有编译器可能会给你自动加上,也可能会报错,不然的话printf会把整个内存里的东西根据占位符的格式都给打印出来!
char *指针在分配时末尾的一个字节,也会隐式的被修改为\0
从上分析可以得出,strcpy和printf是不安全的所以微软公司推出了strcpy_s和printf_s函数,当然也有很多其他的函数,凡是微软测试出来认为不安全的函数都会被重写,其次微软默认是不允许我们使用这些不安全的函数的,需要增加一些宏开关才行!
这里我们将strcpy改成strcpy_s
在运行一下:
直接报中断,越界错误!
上一篇: 数据结构与算法作业 力扣155.最小栈
下一篇: 算法效率测试(以排序算法为例)