0

我正在阅读 Robert C Seaford 的 C 和 C++ 书中的安全编码。

int main(int argv, char* argv[]) {
  char a[16];
  char b[16];
  char c[32];

  strcpy(a, "0123456789abcdef");
  strcpy(b, "0123456789abcdef");
  strcpy(c, a);
  strcat(c, b);
  printf("a = %s\n", a);
  retrun 0;
}

三个字符数组的静态声明未能为空终止字符分配存储空间。

结果,第strcpy()一个在数组末尾写入了一个空字符。

根据编译器分配存储的方式,此空字节可能会被strcpy()下一行覆盖。如果发生这种情况,a现在指向一个 20 个字符的数组,而b指向一个 10 个字符的数组。

我的问题是作者平均数组如何a包含 20 个字符和数组b指向 10 个字符?

4

1 回答 1

4

我必须假设“10”是指十六进制的 0x10(16),“20”是指十六进制的 0x20(32)。

无论如何,关键是字符串操作代码通过空字符的位置确定字符串的长度,而不是数组中实际分配的字符数。如果内存是这样分配的:

<-------a------><-------b------><--------------c-------------->

然后在第一个之后strcpy,内存看起来像这样。(我将使用 ! 来表示空值)。

<-------a------><-------b------><--------------c-------------->
0123456789abcdef!

然后在第二秒之后strcpy,记忆看起来像这样。

<-------a------><-------b------><--------------c-------------->
0123456789abcdef0123456789abcdef!

每个strcpy操作都会溢出它应该写入并使用相邻内存的数组,这是一种非常糟糕的做法。任何a像字符串一样操作的函数都会看到 32 (0x20) 个字符(不包括 null),而b像字符串一样操作的任何函数都会看到 16 (0x10) 个字符(不包括 null)——尽管事实上你'在每个数组中只为 15 个字符分配了足够的内存(不包括空值)。

任何这一切都不会崩溃的事实是内存布局方式的意外。如果您向数组中写入的字符多于您的空间,并且正在写入的相邻内存不可写,那么您将崩溃。

当然,由于您正在阅读有关安全编码的内容,因此您正在覆盖其他内容的事实可能是一个严重的安全问题,具体取决于输入的可信度以及被覆盖的确切内容。

于 2013-04-10T11:35:52.420 回答