在x86-32上的C中的堆栈溢出和分段故障

时间:2012-10-13 17:11:01

标签: c segmentation-fault stack-overflow callstack

我对C中的堆栈溢出有疑问。 我写了一个小测试程序,所以当我用8开始它时,我得到我所期望的,我写第二个buf的边界,因此buf1是空的,因为尾随零现在是buf1中的第一个元素。 到目前为止一切都那么好,如果我尝试16,因为它也有效,17事件的事件。但我希望在这里遇到一个段错误...在24 As之后会出现段错误。这是为什么?我测试了x86-32 ubuntu,debian和suse。 24字节后总是段错误... 在具有相同代码的AMD64系统上,我在32 As之后得到段错误,就像我预期的那样...... 但为什么在x86-32之后的24 ????

包括

  #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;
  } 

1 个答案:

答案 0 :(得分:0)

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

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

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

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,caller,callee)。
  • 记录来自被叫方的返回,访问本地变量,再次记录并返回。
  • 如果在访问局部变量时发生了段错误,则很可能是基指针。

其他可能的原因:

  • 发生错误时使用的一些调试信息。
  • 从未经过测试的金丝雀(堆栈保护器),但无论如何都要分配。没有任何症状。
  • 参数的数量(syscall)破坏这会破坏调用者的堆栈指针,如果调用者不进行另一次调用,则会忽略该指针。症状:如果呼叫者再次拨打电话,则呼叫者会发生段错误。
  • this指针。普通功能不应该出现。

其他尝试:

在损坏之前和之后在被调用者中进行内存转储以查看布局。存储的基指针应指向堆栈空间(调用者的堆栈帧)。返回地址应指向代码空间(靠近当前IP)。 char *参数是一个指针。用另外两个参数(“&gt;&gt;&gt;”和“&lt;&lt;&lt;”作为char[4])展开它将有助于识别。