http://natashenka.ca/wp-content/uploads/2014/01/strcpy8x11.png
strcpy 被认为是危险的,例如您正在演示的原因。您创建的两个缓冲区是存储在函数堆栈帧中的局部变量。堆栈框架大致如下:http:
//upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Call_stack_layout.svg/342px-Call_stack_layout.svg.png
仅供参考,事物被放在堆栈顶部,这意味着它在内存中向后增长(这并不意味着内存中的变量被向后读取,只是新的变量被放在旧变量的“后面”)。因此,这意味着如果您在函数堆栈帧的 locals 部分写入足够多的内容,您将在要复制到的变量之后向前写入所有其他堆栈变量并进入其他部分,并最终覆盖返回指针。结果是,如果你很聪明,你可以完全控制函数返回的位置。你可以让它真正做任何事情,但问题不是你。
正如您通过使第一个缓冲区为 5 个字符的字符串长度为 6 个字符而知道的那样,C 字符串以空字节 \x00 结尾。strcpy 函数复制字节直到源字节为 0,但它不检查目标是否有那么长,这就是它可以复制数组边界的原因。这也是为什么您的打印正在读取超过其大小的缓冲区,它读取到 \x00。有趣的是,strcpy 可能已写入 s 的数据,具体取决于编译器在堆栈中给出的顺序,所以一个有趣的练习可能是打印 a 并查看是否得到类似 'snsadsdas' 的内容,但我不能确保即使它污染了 s 也会是什么样子,因为有时由于各种原因在堆栈条目之间存在字节)。
如果这个缓冲区包含一个密码来检查带有散列函数的代码,然后你将它复制到堆栈中的缓冲区,从你得到它的任何地方(如果是服务器,则为网络数据包,或文本框等)你很好可能会从源复制更多的数据,而不是目标缓冲区可以容纳的数据,并将程序的控制权交给能够向您发送数据包或尝试密码的任何用户。他们只需要输入正确数量的字符,然后输入代表地址的正确字符即可跳转到 ram 中的某个位置。
如果您检查边界并可能修剪源字符串,则可以使用 strcpy ,但这被认为是不好的做法。还有更多现代函数需要最大长度,例如http://www.cplusplus.com/reference/cstring/strncpy/
哦,最后,这都称为缓冲区溢出。一些编译器在每个堆栈条目之前和之后添加由操作系统随机选择的一小块字节。每次复制后,操作系统都会根据其副本检查这些字节,如果它们不同,则终止程序。这解决了很多安全问题,但是仍然可以将字节复制到堆栈中足够远的位置以覆盖指向函数的指针,以处理当这些字节已更改时发生的情况,从而让您做同样的事情。做正确的事情变得更加困难。