在C语言中,数组的重要性和指针是一样的。所以更深入了解数组也就更加了解C语言。
1. 一维数组
首先,我创建了一个一维数组intArray。我们看一下编译器是怎么存放这些数值的:
我们可以看到,我们的数值1,2,3,4
连续存放在当前的Stack里面,就相当于连续存放的局部变量。
我们再看看intArray这个变量里面的值到底是什么?
很明显,我们的指针变量a里面存放了数组最顶部的值的地址[ebp - 18h]
(这个地址存放了intArray的第一个值:1
)。
我们再探究一下Array是怎样取出里面的数组的呢?
我们可以看到取出一维数组中的值大概分为三个步骤:
1 | //Step1: |
我们可以看到数组再取出数值的时候并没有越界判断。那么我们可以利用这一点强行修改ebp+4
的值达到return到指定function的功能。这种技术叫做缓冲区溢出。
首先通过计算,从Array取第几个值可以达到ebp+4
的位置: 我这里是取第7个值达到ebp+4:
所以我们写出下面代码模拟缓冲区溢出:
1 |
|
首先不运行这一段代码,正常情况main函数只调用了check()函数,那么并不会输出任何东西到console里面。
但是如果黑客通过逆向利用缓冲区溢出修改了原函数的返回地址(例如15行)。那么就会返回到黑客自己的函数hacker()。我们编译运行看看运行结果。
我们可以看到结果,虽然main函数没有调用hacker()函数,但是仍然跳转到我们的函数了。
我们想象一下,如果hacker()函数里面放的是某些病毒函数,那么你的电脑就躺枪了。当然外挂也可以这样Hook进到进程中。这是一个技术,就看你怎么运用了。
2. 多维数组
首先看一下底层的二维数组长什么样:
其实就是一维数组。
我们再看一下,底层是怎么取出二维数组里面的值的:
其实和一维数组一样,分成三个步骤
1 | Step1: 把数组中值的多少放到eax中 |
所以多维数组本质上就是一维数组,但是我们可以更方便的使用多维数组(感谢编译器的工作,多维数组的工作是基于编译器的支持。如果编译器不支持多维数组,就使用不了多维数组了)。
多维数组的实现其实是编译器帮我们计算一维数组的offset,再帮我们在内存中取出来。
同时,多维数组也存在缓冲区溢出,示例代码如下:
1 |
|
结果如下:
3. Conclusion
- 一维数组是连续存放的,取出值的过程其实是编译器帮我们计算offset取出值的过程。
- 多维数组本质上就是一维数组。一样的,取值的过程就是编译器帮我们计算offset再取出值的过程。
- 一维数组和多维数组都没有Boundary Detection。所以都有可能造成缓冲区溢出的危险。