VC++6.0编译器生成的数组越界
由于VC++6.0编译器,在栈中给变量和数组分配缓存区内存时,不预留保护区(0xCCCCCCCC),并且数组的寻址方式为
DWORD PTR DS:[EBP+EAX*4 - 28h]或DWORD PTR DS:[EBP-4]或DWORD PTR DS:[EBP+4]
[EBP+4] 在函数销毁执行ret命令时,会修改EIP,所以CPU会运行被修改后的函数 “返回地址”。
下面是代码测试:
//数组越界后,越界的值是ebp+4(即EIP返回地址)
void Helloword() {
printf("==========helloword");
getchar();
}
void arrYuJie() {
int arr[5] = { 1,2,3,4,5 };
arr[6] = (int)Helloword;
}
int main(int argc, char* argv[]){
arrYuJie();
printf("Hello World!\n");
return 0;
}
arrYuJie:
004010D0 push ebp
004010D1 mov ebp,esp
004010D3 sub esp,54h
004010D6 push ebx
004010D7 push esi
004010D8 push edi
004010D9 lea edi,[ebp-54h]
004010DC mov ecx,15h
004010E1 mov eax,0CCCCCCCCh
004010E6 rep stos dword ptr [edi]
004010E8 mov dword ptr [ebp-14h],1
004010EF mov dword ptr [ebp-10h],2
004010F6 mov dword ptr [ebp-0Ch],3
004010FD mov dword ptr [ebp-8],4
00401104 mov dword ptr [ebp-4],5
0040110B mov dword ptr [ebp+4],offset @ILT+0(Helloword) (00401005)
00401112 pop edi
00401113 pop esi
00401114 pop ebx
00401115 mov esp,ebp
00401117 pop ebp
00401119 ret ----------------------------------------->ret后会修改EIP的值,CPU会执行这个地址,就是Helloword这个函数的地址,如下:
@ILT+0(?Helloword@@YAXXZ):
00401005 jmp Helloword (00401030)
Helloword:
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,44h
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-44h]
0040103C mov ecx,11h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
00401048 push offset string "==========helloword" (0042301c)
0040104D call printf (004014b0)
00401052 add esp,4
00401055 mov eax,[__iob+4 (00425a34)]
0040105A sub eax,1
0040105D mov [__iob+4 (00425a34)],eax
00401062 cmp dword ptr [__iob+4 (00425a34)],0
00401069 jl Helloword+5Ch (0040108c)
0040106B mov ecx,dword ptr [__iob (00425a30)]
00401071 movsx edx,byte ptr [ecx]
00401074 and edx,0FFh
0040107A mov dword ptr [ebp-4],edx
0040107D mov eax,[__iob (00425a30)]
00401082 add eax,1
00401085 mov [__iob (00425a30)],eax
0040108A jmp Helloword+6Ch (0040109c)
0040108C push offset __iob (00425a30)
00401091 call _filbuf (00401180)
00401096 add esp,4
00401099 mov dword ptr [ebp-4],eax
pop edi
0040109D pop esi
0040109E pop ebx
0040109F add esp,44h
004010A2 cmp ebp,esp
004010A4 call __chkesp (00401530)
004010A9 mov esp,ebp
004010AB pop ebp
004010AC ret
//桶排序
visual studio 2019版本的编译器,在对变量或数组分配内存时,会预留int3(0xCCCCCCCC)缓存区,防止CPU执行越界后,导致程序飞
visual studio 2019版本的编译器,显然做了保护在分配栈缓冲内存时。并且寻址方式也做了变形保护。
例如:
//桶排序
void tongSort() {
int index = 0;
int arrS[8] = {2,4,6,5,3,8,2,1};
int arrK[9] = { 0 };
//把要排序的数组的数,放到空桶中,重复的数,桶里加1;
for (int i = 0; i < 8;i++) {
int t = arrS[i];
arrK[t]++;
}
//将空桶里每个数值所对应的个数,依次放回源桶中排序列;
for (int j = 0; j < 9;j++) {
int t = arrK[j];
if (t) {
for (int n = 0; n < t;n++) {
arrS[index] = j;
index++;
}
}
}
}
(三)多维数组
本质:多维数组就是一维数组,以二维数组举例(visual studio 2019版本的编译器);
1)编译器分内存空间:转为一位数组 int arr_one[ 3*4] = { --- --- };
2)//编译不过去,初始化太多。
int arr[3][4] = {
{1,2,3,4,5},
{5},
{9}
}
3)可编译就是乱,分配空间[3*4],正常分配,第二段不够补0
int arr[ 3 ] [4 ] = {1,2,3,4,5,6,7,8,9,11};
4)省略长度,编译可通过,4个一组。后面 [ 4 ] 的位置不可省略,前面的[ ]可以省略。总个数模上4来整数组分空间,还可补 0,能分几组就分几组。
int arr[ ][ 4 ] = {1,2,3,4,5,6,7,8,9,11};
5)寻址找到这个值 arr[ 1 ][ 2 ] = arr [ 1*4+2 ]
int arr[3][4] = {
{2,3,4,9},
{5,4,1,8},
{2,7,5,4}
}
6)三维数组:假设一共有5个班,每个班4个组,给个组3人。
int arr[ 5 ][ 4 ] [ 3 ]= {
{{4,1,4},{2,5,9},{5,4,3},{9,1,3}},
{{1,2,3},{12,52,1},{5,44,3},{9,12,31}},
{{4,1,8},{2,35,9},{5,4,6},{9,41,36}},
{{44,1,24},{2,51,3},{5,42,34},{9,1,3}},
{{4,16,47},{72,6,9},{7,64,5},{9,14,3}},
}
寻址:第二个班,第三组的第二人,今年多大
arr[ 1 ][ 2 ][ 1 ] = [ 1*4*3 + 2*3 + 1] = 44
7)五维数组 int arr[ m ][ n ][ g ][ f ][ k ]
寻址:arr[ 2 ][ 3 ][ 5 ][ 1 ][ 4 ] = arr[ 2*n*g*f*k + 3*g*f*k + 5*f*k + 1*k + 4e ]
8)练习,归并排序,前提是两个数组都是有序的。
j = 0 数组一:arr_1 [ 8 ] = { 3,5,7,11,13,15,21,25 };
k = 0 数组二:arr_2 [ 6 ] = { 4,8,9,17,22,35 };
arr_new [ 14 ]={ };