初看缓冲区(堆栈)溢出

先看一段简单的小程序

#include <stdio.h>
#include <string.h>

char name[]="NEUQ_CSA";

int main()
{
	char output[8];
	strcpy(output,name);
	
	for(int i=0;i<8&&output[i];i++)
		printf("\\0x%x",output[i]);
	
	return 0;
}

首先, #include<stdio.h> 和 #include<string.h> 是包含后面使用的strcpy函数和printf函数的头文件。
 

然后, char name[] = “NEUQ_CSA”; 是把‘name’这个数组赋值,往里面放入‘NEUQ_CSA’这几个字符。
 
然后是 int main(){ …… } ,这个是重点啦,这就是我们常说的主函数!程序进来就是先找到这个地方,执行里面的语句。
 
接下来的 char output[8]; strcpy(output, name);  就是让系统给output变量分配8个char的空间,然后把‘name’里面装的字符拷贝给它。
 
 strcpy(des,source) 这个拷贝函数是把第二个参数source的值拷给第一个参数des。它不检查拷贝的长度,它会一直拷贝,直到source到结尾。这就是它的弱点!
 
下面的 for(int i=0;i<8&&output[i];i++) printf("\\0x%x",output[i]);  只是让大家方便检查 output里面的值而已,我把它以16进制的形式打出来。
 

初看缓冲区(堆栈)溢出

 

打印出来的是:

\0x4e\0x45\0x55\0x51\0x5f\0x43\0x53\0x41

分别为‘N','E','U','Q','_','C','S','A'的十六进制

初看缓冲区(堆栈)溢出

 

现在开始用简单的代码演示:

#include <stdio.h>
#include <string.h>

char name[]="abcdefgh";

int main()
{
	char output[8];
	strcpy(output,name);
	
	for(int i=0;i<8&&output[i];i++)
		printf("\\0x%x",output[i]);
	
	return 0;
}

 

只是对刚才的代码做了简单的修改,此时堆栈数据是这样的:

                                                                                       初看缓冲区(堆栈)溢出         

       补充:1.计算机为了能回头继续处理原来的事情,就需要把原来指令的指针EIP保存在堆栈中;当要回去原来的地方时,就把保存在堆栈中的 EIP恢复即可。并且各个函数的局部变量的分配也是在堆栈中。

                  2.堆栈是一数据结构,遵循“先进后出,后进先出”的规则,就像我们平时叠盘子一样,先放在下面的最后才能取出来,最后放上去的最先取出来。而在操作系统中,存和取的动作就是PUSH和POP。PUSH放一个数据到堆栈中去,POP取一个堆栈中的数据出来。 

 

这样在执行完 main 函数后,只要把保存在堆栈中的 EBP、EIP 恢复回去,就可继续原来的执行过程而没有任何问题。

 

那第二次输入‘abcdefghijklmnopqrstuvwxyz’时,output分配的还是8个字节,但却拷了26个字母进来,会变成什么样呢?

#include <stdio.h>
#include <string.h>

char name[]="abcdefghijklmnopqrstuvwxyz";

int main()
{
	char output[8];
	strcpy(output,name);
	
	for(int i=0;i<8&&output[i];i++)
		printf("\\0x%x",output[i]);
	
	return 0;
}

结果如下:

 

                                                                                   初看缓冲区(堆栈)溢出

由于拷贝的字母过长,不仅把分配给 output 的 8 个字节占据完了,而且还继续往下,把保存的EBP和EIP给占据了。
 
当执行完main函数后,系统要恢复EBP、EIP,而EIP已经被我们覆盖成ponm了。
但系统不知道,就会去执行原内容保存地址的东东。而那个位置是不可读的,所以就会出错。

 

我们可通过覆盖 EIP 为任意值来让程序运行到一个错误的地方,那如果我们特意把EIP覆盖成我们想去的程序的地方,那会怎么样呢?

 

 

参考文章:Q版缓冲区溢出教程