1

我有一个关于 C 中的堆栈溢出的问题。我写了一个小测试程序,所以当我用 8 As 启动它时,我得到了我所期望的,我写在第二个 buf 的边界上,因此 buf1 是空的,因为现在,尾随零是 buf1 中的第一个元素。到目前为止一切顺利,如果我尝试使用 16 As 它也可以,使用 17 As 进行事件。但我预计这里会出现段错误……段错误在 24 As 之后出现。这是为什么?我在 x86-32 ubuntu、debian 和 suse 上进行了测试。总是在 24 字节后出现段错误……在具有相同代码的 AMD64 系统上,我在 32 后得到段错误,正如我所期望的那样……但是为什么在 24 后的 x86-32 上?

包括

  #include <string.h>

  /*
  * $ gcc -O0 -Wall -fno-stack-protector buffer.c -o buffer
  *
  * $ ./buffer AAAAAAAA
  * buf1: test
  * buf2: test
  * buf1:
  * buf2: AAAAAAAA
  *
  * $ ./buffer AAAAAAAAAAAAAAAAAAAAAAAA
  * buf1: test
  * buf2: test
  * buf1: AAAAAAAAAAAAAAAA
  * buf2: AAAAAAAAAAAAAAAAAAAAAAAA
  * Segmentation fault (core dumped)
  */

  static void exploit(const char *InputString)
  {
char buf1[8];
char buf2[8];

strcpy(buf1, "test");
strcpy(buf2, "test");

printf("buf1: %s\n", buf1);
printf("buf2: %s\n", buf2);

strcpy(buf2, InputString);

printf("buf1: %s\n", buf1);
printf("buf2: %s\n", buf2);
  }


  int main(int argc, const char *argv[])
  {
if (argc > 1)
  exploit(argv[1]);

return 0;
  } 
4

1 回答 1

0

您试图猜测编译器生成的内存布局。编译器可以自由地以任何看起来合适的方式布局代码。但是,堆栈帧必须包含:

  • 参数(由调用者推动)
  • 返回地址(由 CPU 推送)
  • 需要保留的寄存器。这取决于目标架构。
  • 局部变量(由被调用者分配)

在您测试的 x86 机器上,局部变量和返回地址之间有 8 个字节,而在 x64 机器上则有 16 个字节。如果寄存器被调用者保存,它们将被存储在这个空间中:

func:
PUSH BP
MOV BP, SP
SUB SP, privateSpace
...
MOV SP, BP
POP BP
RET argSize

http://en.wikipedia.org/wiki/Calling_convention#x86

因此,返回地址之后的空间包含存储的帧指针 BP。如果调用者不再使用基指针,则不会注意到此覆盖。一个函数使用它的基指针返回,但是如果函数通过系统调用退出(我记得 main 方法在这个意义上是“特殊的”,但我无法获取我的声明),损坏的基指针永远不会被使用并且损坏会被忽视)。

要测试它是否是基指针:

  • 创建第三级调用(main、caller、callee)。
  • 记录被调用者的返回,访问局部变量,再次记录,然后返回。
  • 如果在访问局部变量时发生段错误,则很可能是基指针。

其他可能的原因:

  • 发生错误时使用的一些调试信息。
  • 从未测试过但无论如何都会分配的金丝雀(堆栈保护器)。没有任何症状。
  • 参数数量 ( syscall ) 破坏 this 会破坏调用者的堆栈指针,如果调用者不进行另一个调用,它将被忽视。症状:如果调用者进行第二次调用,调用者中会出现段错误。
  • this指针。不应出现在普通功能中。

其他要尝试的事情:

在损坏之前和之后在被调用者中进行内存转储以查看布局。存储的基指针应指向堆栈空间(调用者的堆栈帧)。返回地址应指向代码空间(靠近当前 IP)。char* 参数是一个指针。将其与其他两个参数 (">>>" 和 "<<<" as char[4]) 放在一起将有助于识别。

于 2012-10-14T06:37:17.263 回答