0%

基于数组越界的缓冲区溢出

上一篇文章说了函数调用时候的堆栈变化,这里就基于这个内容来验证一下基于数组越界的缓冲区溢出。

在c语言中,数组必须是静态的,也就是在定义的时候必须明确数组的大小,在根本上来说,这个是堆栈提升的原因,只有在数组的大小确定的时候,才能明确堆栈到底要提升多少,如果数组的大小是动态变化的,就极容易发生缓冲区溢出;而且c语言也不具备Java等语言中静态分析的功能,不会去检测数组是否有上溢或者下溢,其边界的检验是有程序员负责的,所以这就造成了一些问题,我们可以通过数组越界来改变一些内容。

首先来看一下这次的实验程序

正常来说,test1函数并没有被调用,所以是不会打印出12345的,而实际的情况却不是这样的

造成这样的情况,就是由于数组越界而造成的缓冲区溢出,这其中还有一个编译器的坑,在后面再解释。

我们直接在数组处下断点,前面的提升堆栈等操作就不细说了,前一篇文章已经走过一边流程了,这里直接给出到这一步的堆栈图。

然后我们看一下编译器是如何处理数组赋值的内容的

经过这段赋值操作,此时堆栈已经变成了下图,这里单独从中拎出来10行方便观看。

通过堆栈图我们可以很清晰的看到,明明只有8个数,它确是从ebp-24h开始排的,也就是说ebp-4的位置是没有使用的,这个也就是前面所说的坑,经过查询资料,发现从vs2010开始,ebp-4就都没有使用,具体是用来做什么的,暂时还不清楚,所以如果这里你使用的是vc6.0的话,这里就是从ebp-4开始排的。

好了说了上面那个坑,接着回来说堆栈图,在上一篇文章里我们已经很清楚函数在调用的时候会先把call语句的下一行地址压入栈中,所以图中b[10]的位置也就代表了ret返回地址的位置,在vc6.0中此处应该是b[9],如果我们将这个地址替换为我们想让程序到达的位置,也就可以控制程序的运行轨迹了。

在后面的操作就是将test1函数的地址赋给了b[10],也就代替了之前函数的返回地址,这个函数在执行完成后便会返回test1函数的位置081137Ah。

也就达到了缓冲区溢出的效果。