需要一些关于 BOF 的高级解释

Need some advanced explanations about BOF

所以我正在关注 tutorial 关于缓冲区溢出的代码:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

 modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

然后我用 gcc 编译它,另外 运行 预先 sudo sysctl -w kernel.randomize_va_space=0 以防止随机内存并允许堆栈粉碎(缓冲区溢出)利用

gcc protostar.c -g -z execstack -fno-stack-protector -o protostar

-g 允许在 gdb 中调试 ('list main')
-z execstack -fno-stack-protector 就是去掉栈保护

然后执行:

python -c 'print "A"*76' | ./protostar

再试一次?

python -c 'print "A"*77' | ./protostar

您已更改 'modified' 变量

所以我不明白为什么77会发生缓冲区溢出,而它应该是65,所以它有12位的差异(3字节)。不知道是什么原因,有没有人解释清楚?

从77到87也是这样:

python -c 'print "A"*87' | ./protostar
you have changed the 'modified' variable

从 88 开始,它添加了一个段错误:

python -c 'print "A"*88' | ./protostar
you have changed the 'modified' variable
Segmentation fault (core dumped)

此致

要完全了解正在发生的事情,首先要注意您的程序是如何布局内存的。

根据您的评论,对于这个特定的 运行,buffer 的内存从 0x7fffffffdf10 开始,然后 modified0x7fffffffdf5c 开始(尽管 randomize_va_space 可能会在 运行 秒内保持一致,但我不太确定)。

所以你有这样的东西:

0x7fffffffdf10            0x7fffffffdf50      0x7fffffffdf5c
↓                         ↓                   ↓
(64 byte buffer)..........(some 12 bytes).....(modified)....

本质上,你有 64 个字符的缓冲区,然后当它结束时,有 12 个字节用于其他一些堆栈变量(可能 4 个字节 argc 和 8 个字节用于 argv),然后修改之后,正好在缓冲区开始后开始 64+12 = 76 字节。

因此,当您将 65 到 76 个字符写入 64 字节缓冲区时,它会过去并开始写入 in-between 缓冲区和 modified 的 12 个字节。当您开始写入第 77 个字符时,它会开始覆盖 modified 中的内容,这会导致您看到“you have changed the 'modified' variable”消息。

您还问过“如果我上升到 87 然后在 88 出现段错误,为什么它会起作用?答案是因为它是未定义的行为,一旦您开始写入无效内存并且内核会识别它,它会立即终止您的进程,因为您正在尝试 read/write 您无权访问的内存。

请注意,您几乎不应该在实践中使用 gets,这是一个重要原因,因为您不知道要读取多少字节,因此有机会覆盖。另请注意,当我 运行 它时,您看到的行为与我在我的机器上看到的行为不同。这是正常的,那是因为它是未定义的行为。当您 运行 它时,无法保证会发生什么。在我的机器上,modified 实际上在内存中出现在 buffer 之前,所以我从来没有看到 modified 变量被覆盖。我认为这是一个很好的学习示例,可以理解为什么像这样的未定义行为是如此不可预测。