通常情況下在C語言中我們定義一個大小為5的數組是這樣的:
int arr[5] = {1,2,3,4,5};
1,2,3,4,5
這五個數字對應的下標是0,1,2,3,4
,當我們想訪問數組中第一個數字時,通常會使用arr[0]
的形式去訪問,但是,如果我們使用arr[5]=6
來對超過數組大小的地方進行賦值,會發生什么?
編譯之后,發現編譯器並沒有報錯,也沒有警告,但是我們知道,這里其實已經發生了數組越界問題。
那我們先來看一下不進行數組越界操作的時候反匯編是什么樣的:
再來看一下對數組進行越界操作的時候是什么樣的:
對比之后發現在添加了一句arr[5]=6
之后,反匯編代碼這里多出一句0040104B mov word ptr [ebp],6
,也就是6被存到內存地址為[ebp]
的地方了。
參照上一篇文章,可以知道[ebp]
是存放原ebp的地址,也就是函數執行第一步的push ebp
,一個函數在開始執行前,會將下一條指令的地址壓入棧中,位置是[ebp+4]
,這里我們注意到,arr[5]
的位置是[ebp]
,那么推測[ebp+4]
的位置就是arr[6]
,存放在這里的值在函數結束之后會被存放到EIP中當成下一條指令的地址,執行此處的指令。
那么我們要是在arr[6]
的位置換成另一個函數的地址,那么在這個函數結束后不就可以自動調用另一個函數了,於是將原來的代碼改成這樣:
#include <stdio.h>
void First();
void Hello();
int main(void)
{
First();
return 0;
}
void First()
{
int arr[5] = {1,2,3,4,5};
arr[6] = (int)Hello;
}
void Hello()
{
printf("Hello World!");
getchar();
}
這樣,根據我們的構想,在主函數里面調用了First函數,但是在First函數結束后,會調用Hello函數:
這里可以看到返回地址被改成了Hello函數的入口地址:
從上面就可以看出數組越界的危害性。