滴水-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