文章

滴水-day12-缓冲区溢出-多维数组

基于缓冲区溢出的HelloWord

数组如果发生越界那么取到的值是堆栈中的未知值,基于这个逻辑可以造成函数缓冲区溢出。

函数名也相当于一个全局变量,把变量转为int类型输出即为这个函数的地址。

尝试强行让数组发生越界并写入数据:

int array[3] = { 1,2,3 };
array[4] = 0x12345678;

00051AAA  mov         eax,4
00051AAF  shl         eax,2
00051AB2  mov         dword ptr [ebp+eax-10h],12345678h

经过测试发现当强行向数组下标+1位置写入数据的时候发现其实是吧数据写入到了EBP中,而EBP是保存了上一个函数的栈底。

int array[3] = { 1,2,3 };
array[5] = 0x12345678;

000C1AAA  mov         eax,4
000C1AAF  imul        ecx,eax,5  //ecx=20=14H
000C1AB2  mov         dword ptr [ebp+ecx-10h],12345678h

当向数组下标+2处写入数据的话其实上是把数据写入到了EBP+4中,而EBP+4保存了ret的返回地址,也就是当函数执行结束后EIP保存的数据,是即将要执行的数据。

基于上面这两个结论,当我们获取到一个函数的地址后直接向一个数组下标+2中写入这个地址,那么EIP的值会被直接改变,从而直接执行我们所指定的函数。

// 缓冲区溢出HelloWorld
void helloWorld()
{
	printf("HelloWorld\\\\n");
}

void sub_100()
{
	int array[3] = { 1,2,3 };

	// 获取到sub_100函数地址
	array[5] = (int)helloWorld;

}
int main()
{

	sub_100();
	return 0;
}

通过这样的测试,在12行代码处我修改了EBP+4的值,当这个函数的ret执行后会直接把EBP+4的值保存到EIP中,再直接执行EIP所保存的地址,而这个地址是我通过类型转换获取到helloWorld函数的地址。

所以这个程序执行完后会输出helloWorld。

PS:程序输出了两次helloWorld是因为sub_100修改了EBP+4的值,导致sub_100函数ret返回后执行helloWorld函数,此时程序应该要返回到EBP+4的原始地址,但已被覆盖,再一次执行了helloWorld函数,此时程序会发生异常导致程序直接退出。

多维数组

二维数组

二维数组的定义:

int[][] ={

{},

{},

{}

}

PS:分配字节的宽度全部是以int类型4字节开始分配

数组在汇编中没有多维之分,都可看为一维数组。

数组的分配方式与局部变量差不多,唯一不同的就是分配的空间顺序,

数组是从最大字节开始倒序分配,

比如一维数组:int array[5] = {1,2,3,4,5};

那么分配数据的时候就是:

EBP-14H,1

EBP-10H,2

EBP-C,3

EBP-8,4

EBP-4,5

但是我测试后发现在VS2019中分配的时候没有把EBP-4给算上,所以他要从14H+4开始分配,咱也不知为何。

003F46D5  mov         dword ptr [ebp-18h],1
003F46DC  mov         dword ptr [ebp-14h],2
003F46E3  mov         dword ptr [ebp-10h],3
003F46EA  mov         dword ptr [ebp-0Ch],4
003F46F1  mov         dword ptr [ebp-8],5

同理,在二维数组中也是这样分配的:

007846D5  mov         dword ptr [ebp-34h],1
007846DC  mov         dword ptr [ebp-30h],2
007846E3  mov         dword ptr [ebp-2Ch],3
007846EA  mov         dword ptr [ebp-28h],4
007846F1  mov         dword ptr [ebp-24h],5
007846F8  mov         dword ptr [ebp-20h],6
007846FF  mov         dword ptr [ebp-1Ch],7
00784706  mov         dword ptr [ebp-18h],8
0078470D  mov         dword ptr [ebp-14h],9
00784714  mov         dword ptr [ebp-10h],0Ah
0078471B  mov         dword ptr [ebp-0Ch],0Bh
00784722  mov         dword ptr [ebp-8],0Ch

还是没有把EBP-4给算上,所以分配的空间是从字节总和+4字节处开始分配的。

比如说一个34的二维数组,分配的时候就是从34H开始(124=20=30H+4=34H);

通过上面代码可以看到,在汇编中不论是一维数组还是多维数组,分配数据的时候都是一样的。

当二维数组的数据没有写完整时,汇编会如何做?

int array2[3][4] = {
		{1,2,},
		{5,6,},
		{9,10,}
	};

我定义了一个3*4的数据,但每行数据我只给了2个数据,那么剩下的两个数据会是什么?

垃圾值吗。

不是的,在汇编中可以看到,当数据不足的时候汇编会自动给补0,缺少几个就补几个。

00F55443  mov         dword ptr [ebp-70h],1
00F5544A  mov         dword ptr [ebp-6Ch],2
00F55451  xor         eax,eax
00F55453  mov         dword ptr [ebp-68h],eax
00F55456  mov         dword ptr [ebp-64h],eax
00F55459  mov         dword ptr [ebp-60h],5
00F55460  mov         dword ptr [ebp-5Ch],6
00F55467  xor         eax,eax
00F55469  mov         dword ptr [ebp-58h],eax
00F5546C  mov         dword ptr [ebp-54h],eax
00F5546F  mov         dword ptr [ebp-50h],9
00F55476  mov         dword ptr [ebp-4Ch],0Ah
00F5547D  xor         eax,eax
00F5547F  mov         dword ptr [ebp-48h],eax
00F55482  mov         dword ptr [ebp-44h],eax

省略行数

int array3[][4] = {
		{1,2,3,4},
		{5,6,7,8}
	};

这个没什么好说的,虽然省略了行数,但列数还在,程序只管按着列数进行分组就可了。

三维数组

int arr[5][4][3] = {

	{{1,2,3},{4,5,6},{7,8,9},{11,12,13}},

	{{11,12,13},{14,15,16},{17,18,19},{111,112,113}},

	{{21,22,23},{24,25,26},{27,28,29},{211,212,213}},

	{{31,32,33},{34,35,36},{37,38,39},{311,312,313}},

	{{41,42,43},{44,45,46},{47,48,49},{411,412,413}}

};

三维数组寻址方式:

arr[2][3][2]

243 + 3*3 + 2

练习

day12.cpp

License:  CC BY 4.0