0%

C++反汇编与逆向分析 - 数组

数组和指针都是对内存地址操作,但是它们有诸多不同之处,数组是相同类型的数据集合,以线性方式连续存储在内存中;而指针只是保存一个地址值的4字节变量。在使用中,数组是一个地址常量值,保存数组首元素地址,不可修改;而指针是一个变量,只要修改指针所保存的地址数据,就可以随意访问;

运行环境:

  • 操作系统: Windows 7家庭版
  • 编译器:VC6 VS2013

数组

数组在函数内

当在函数内定义数组时,如果没有其他声明,该数组即为局部变量,拥有局部变量的所有特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
10:       int Array[5] = {1, 2, 3, 4, 5};
0040D7A8 mov dword ptr [ebp-14h],1
0040D7AF mov dword ptr [ebp-10h],2
0040D7B6 mov dword ptr [ebp-0Ch],3
0040D7BD mov dword ptr [ebp-8],4
0040D7C4 mov dword ptr [ebp-4],5
11: int nOne = 1;
0040D7CB mov dword ptr [ebp-18h],1
12: int nTwo = 2;
0040D7D2 mov dword ptr [ebp-1Ch],2
13: int nThree = 3;
0040D7D9 mov dword ptr [ebp-20h],3
14: int nFour = 4;
0040D7E0 mov dword ptr [ebp-24h],4
15: int nFive = 5;
0040D7E7 mov dword ptr [ebp-28h],5

** //数组有变量或变量表达式作为下标访问 **
** //数组有循环结构去处理数组内各个元素的流程 **

** //当局部变量连续但类型不同时,将更容易区分与数组间的不同 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
10:       int Array[5] = {1, 2, 3, 4, 5};
0040102B mov dword ptr [ebp-14h],1 ;数组首地址
00401032 mov dword ptr [ebp-10h],2
00401039 mov dword ptr [ebp-0Ch],3
00401040 mov dword ptr [ebp-8],4
00401047 mov dword ptr [ebp-4],5
11: int nOne = 1;
0040104E mov dword ptr [ebp-18h],1
12: int nTwo = 2;
00401055 mov dword ptr [ebp-1Ch],2
13: int nThree = 3;
0040105C mov dword ptr [ebp-20h],3
14: char cChar = 'A';
00401063 mov byte ptr [ebp-24h],41h ;连续DWORD中存在一个byte
15: float fLoat = 1.0f;
00401067 mov dword ptr [ebp-28h],3F800000h
16:
17: int nInt = 2;
0040106E mov dword ptr [ebp-2Ch],2
18: short sShort = 1;
00401075 mov word ptr [ebp-30h],offset main+69h (00401079)
19: double dDouble = 20.f;
0040107B mov dword ptr [ebp-38h],0
00401082 mov dword ptr [ebp-34h],40340000h
20: int nFour = 4;
00401089 mov dword ptr [ebp-3Ch],4
21: int nFive = 5;
00401090 mov dword ptr [ebp-40h],5

当我们使用表达式寻址的时候;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0040104B   mov         eax,dword ptr [ebp-8]				;数组下标访问,ary + sizeof(type) * n 
;这里存在常量折叠
0040104E push eax
0040104F push offset string "%d\n" (0042601c)
00401054 call printf (00401130)
00401059 add esp,8
27: printf("%d\n",Array[argc]);
0040105C mov ecx,dword ptr [ebp+8]
0040105F mov edx,dword ptr [ebp+ecx*4-14h]
00401063 push edx
00401064 push offset string "%d\n" (0042601c)
00401069 call printf (00401130)
0040106E add esp,8
28: printf("%d\n",Array[argc % 8]);
00401071 mov eax,dword ptr [ebp+8]
00401074 and eax,80000007h
00401079 jns main+70h (00401080)
0040107B dec eax
0040107C or eax,0F8h
0040107F inc eax
00401080 mov ecx,dword ptr [ebp+eax*4-14h]
00401084 push ecx
00401085 push offset string "%d\n" (0042601c)
0040108A call printf (00401130)

在Release版本下,使用esi寻址,也就是指针寻址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 var_14 = dword ptr -14h
.text:00401000 var_10 = dword ptr -10h
.text:00401000 var_C = dword ptr -0Ch
.text:00401000 var_8 = dword ptr -8
.text:00401000 var_4 = dword ptr -4
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 sub esp, 14h
.text:00401003 push esi
.text:00401004 push edi
.text:00401005 mov edi, 5
.text:0040100A mov [esp+1Ch+var_14], 1
.text:00401012 mov [esp+1Ch+var_10], 2
.text:0040101A mov [esp+1Ch+var_C], 3
.text:00401022 mov [esp+1Ch+var_8], 4
.text:0040102A mov [esp+1Ch+var_4], edi
.text:0040102E lea esi, [esp+1Ch+var_14]
.text:00401032
.text:00401032 loc_401032: ; CODE XREF: _main+46↓j
.text:00401032 mov eax, [esi] ;将5放入eax寄存器
.text:00401034 push eax
.text:00401035 push offset Format ; "%d\n"
.text:0040103A call _printf
.text:0040103F add esp, 8
.text:00401042 add esi, 4
.text:00401045 dec edi ;减1
.text:00401046 jnz short loc_401032
.text:00401048 pop edi
.text:00401049 xor eax, eax
.text:0040104B pop esi
.text:0040104C add esp, 14h
.text:0040104F retn
.text:0040104F _main endp
.text:0040104F

在高版本编译器下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.text:00401040 sub_401040      proc near               ; CODE XREF: start-8D↓p
.text:00401040
.text:00401040 var_14 = xmmword ptr -14h
.text:00401040 var_4 = dword ptr -4
.text:00401040
.text:00401040 push ebp
.text:00401041 mov ebp, esp
.text:00401043 sub esp, 14h
.text:00401046 movaps xmm0, ds:xmmword_402110
.text:0040104D push esi
.text:0040104E movups [ebp+var_14], xmm0
.text:00401052 mov [ebp+var_4], 5
.text:00401059 xor esi, esi
.text:0040105B nop dword ptr [eax+eax+00h]
.text:00401060
.text:00401060 loc_401060: ; CODE XREF: sub_401040+35↓j
.text:00401060 push dword ptr [ebp+esi*4+var_14]
.text:00401064 push offset unk_402108
.text:00401069 call sub_401010
.text:0040106E inc esi
.text:0040106F add esp, 8
.text:00401072 cmp esi, 5
.text:00401075 jl short loc_401060
.text:00401077 xor eax, eax
.text:00401079 pop esi
.text:0040107A mov esp, ebp
.text:0040107C pop ebp
.text:0040107D retn
.text:0040107D sub_401040 endp

数组做参数

数组中数据元素连续存储,并且数组是同类型的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
debug版本:
17: char szHello[20] = {0};
00401098 mov byte ptr [ebp-14h],0
0040109C xor eax,eax
0040109E mov dword ptr [ebp-13h],eax
004010A1 mov dword ptr [ebp-0Fh],eax
004010A4 mov dword ptr [ebp-0Bh],eax
004010A7 mov dword ptr [ebp-7],eax
004010AA mov word ptr [ebp-3],ax
004010AE mov byte ptr [ebp-1],al
18: Show(szHello);
004010B1 lea ecx,[ebp-14h] ;取首地址存入ECX寄存器中
004010B4 push ecx ;将ECX寄存器参数压栈
004010B5 call @ILT+5(Show) (0040100a)
004010BA add esp,4 ;平衡栈


00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
10: strcpy(szBuffer,"HelloWorld");
00401038 push offset string "HelloWorld" (0042201c) ;获取常量首地址,并将此地址压入栈中
0040103D mov eax,dword ptr [ebp+8] ;取参数szBuffer地址存入EAX中
00401040 push eax ;压栈作为strcpy的参数
00401041 call strcpy (00401180)
00401046 add esp,8
11: printf(szBuffer);
00401049 mov ecx,dword ptr [ebp+8]
0040104C push ecx
0040104D call printf (00401100)
00401052 add esp,4
12: printf("\r\n");
00401055 push offset string "\r\n" (004224bc)
0040105A call printf (00401100)
0040105F add esp,4
13: }
00401062 pop edi
00401063 pop esi
00401064 pop ebx
00401065 add esp,40h
00401068 cmp ebp,esp
0040106A call __chkesp (00401270)
0040106F mov esp,ebp
00401071 pop ebp
00401072 ret

Release版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main proc near ; CODE XREF: start+AF↓p
.text:00401040
.text:00401040 var_14 = byte ptr -14h
.text:00401040 var_13 = dword ptr -13h
.text:00401040 var_F = dword ptr -0Fh
.text:00401040 var_B = dword ptr -0Bh
.text:00401040 var_7 = dword ptr -7
.text:00401040 var_3 = word ptr -3
.text:00401040 var_1 = byte ptr -1
.text:00401040 argc = dword ptr 4
.text:00401040 argv = dword ptr 8
.text:00401040 envp = dword ptr 0Ch
.text:00401040
.text:00401040 sub esp, 14h
.text:00401043 xor eax, eax
.text:00401045 lea ecx, [esp+14h+var_14]
.text:00401049 mov [esp+14h+var_13], eax
.text:0040104D push ecx
.text:0040104E mov [esp+18h+var_F], eax
.text:00401052 mov [esp+18h+var_14], 0
.text:00401057 mov [esp+18h+var_B], eax
.text:0040105B mov [esp+18h+var_7], eax
.text:0040105F mov [esp+18h+var_3], ax
.text:00401064 mov [esp+18h+var_1], al ;栈中清零
.text:00401068 call sub_401000
.text:0040106D xor eax, eax
.text:0040106F add esp, 18h
.text:00401072 retn
.text:00401072 _main endp



.text:00401000 push esi
.text:00401001 push edi
.text:00401002 mov edi, offset aHelloworld ; ;"HelloWorld"
.text:00401007 or ecx, 0FFFFFFFFh ;将ECX置位-1,是为了配合repne scasb指令
.text:0040100A xor eax, eax ;eax为0
.text:0040100C repne scasb ;repne/repnz与scas指令结合使用,表示串未结束
;
.text:0040100E mov eax, [esp+8+arg_0] ;获取参数内容,向EDI中赋值字符串首地址
.text:00401012 not ecx ;对ECX取反
.text:00401014 sub edi, ecx
.text:00401016 push eax
.text:00401017 mov edx, ecx
.text:00401019 mov esi, edi
.text:0040101B mov edi, eax
.text:0040101D shr ecx, 2
.text:00401020 rep movsd
.text:00401022 mov ecx, edx
.text:00401024 and ecx, 3
.text:00401027 rep movsb
.text:00401029 call sub_401080
.text:0040102E push offset unk_407030
.text:00401033 call sub_401080
.text:00401038 add esp, 8
.text:0040103B pop edi
.text:0040103C pop esi
.text:0040103D retn
.text:0040103D sub_401000 endp

数组作为返回值

数组作为函数的返回值与作为函数的参数大同小异,都是将数组的首地址以指针的方式进行传递。当数组作为参数时,其定义所在的作用域必然在函数调用以外,在调用之前已经存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
char* RetArray()
{
012013D0 push ebp
012013D1 mov ebp,esp
012013D3 sub esp,0D8h
012013D9 push ebx
012013DA push esi
012013DB push edi
012013DC lea edi,[ebp-0D8h]
012013E2 mov ecx,36h
012013E7 mov eax,0CCCCCCCCh
012013EC rep stos dword ptr es:[edi]
012013EE mov eax,dword ptr ds:[01208000h]
012013F3 xor eax,ebp
012013F5 mov dword ptr [ebp-4],eax
char szBuffer[] = { "Hello World" };
012013F8 mov eax,dword ptr ds:[01205858h] ;字符串数组初始化为字符串
012013FD mov dword ptr [szBuffer],eax
01201400 mov ecx,dword ptr ds:[120585Ch]
01201406 mov dword ptr [ebp-10h],ecx
01201409 mov edx,dword ptr ds:[1205860h]
0120140F mov dword ptr [ebp-0Ch],edx
;使用EAX寄存器保存数组首地址,作为函数返回值。虽然EAX保存的地址存在,但是当函数调用结束后,此地址中数据不稳定,在进行其他操作会破坏数据
return szBuffer;
01201412 lea eax,[szBuffer]
}



printf("%s\n", RetArray());
0120149E call RetArray (0120117Ch) ;使用RetArray返回数组作为printf参数使用
012014A3 mov esi,esp
012014A5 push eax
012014A6 push 1205868h
012014AB call dword ptr ds:[1209118h]
012014B1 add esp,8 ;平衡堆栈

下标寻址和指针寻址

访问数组的方法有两种:

  • 下标访问
  • 指针访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
	char *pChar = NULL;						
013013E8 mov dword ptr [pChar],0 ;初始化指针为空
char szBuffer[] = "Hello";
013013EF mov eax,dword ptr ds:[01305858h] ;初始化数组
013013F4 mov dword ptr [szBuffer],eax
013013F7 mov cx,word ptr ds:[130585Ch]
013013FE mov word ptr [ebp-18h],cx
pChar = szBuffer;
01301402 lea eax,[szBuffer] ;获取数组首地址,使用EDX保存
01301405 mov dword ptr [pChar],eax
printf("%c\n", *pChar);
01301408 mov eax,dword ptr [pChar] ;通过指针访问数组
0130140B movsx ecx,byte ptr [eax] ;字符串指针的间接访问
0130140E mov esi,esp
01301410 push ecx ;间接访问传参
01301411 push 1305860h
01301416 call dword ptr ds:[1309110h]
0130141C add esp,8


printf("%c\n", szBuffer[1]);
01301426 mov eax,1
0130142B shl eax,0
0130142E movsx ecx,byte ptr szBuffer[eax] ;数组下标寻址
01301433 mov esi,esp
01301435 push ecx ;去除数据作为参数
01301436 push 1305860h
0130143B call dword ptr ds:[1309110h]
01301441 add esp,8

指针类型数据的数组

指针类型的数组就是数组中各元素都是相同类型的指针组成,我们称之为指针数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	char *pBuffer[3] = { "Hello", "World", "!\r\n" };
00303B28 mov dword ptr [pBuffer],305858h ;初始化字符串指针第一项
00303B2F mov dword ptr [ebp-10h],305860h
00303B36 mov dword ptr [ebp-0Ch],305868h
ShowString(pBuffer);
00303B3D lea eax,[pBuffer] ;首地址
00303B40 push eax
00303B41 call ShowString (03011E0h)
00303B46 add esp,4

void ShowString(char* pBuffer[])
{
003013C0 push ebp
003013C1 mov ebp,esp
003013C3 sub esp,0CCh
003013C9 push ebx
003013CA push esi
003013CB push edi
003013CC lea edi,[ebp-0CCh]
003013D2 mov ecx,33h
003013D7 mov eax,0CCCCCCCCh
003013DC rep stos dword ptr es:[edi]
for (size_t i = 0; i < 3; i++)
003013DE mov dword ptr [ebp-8],0
003013E5 jmp ShowString+30h (03013F0h)
003013E7 mov eax,dword ptr [ebp-8]
003013EA add eax,1
003013ED mov dword ptr [ebp-8],eax
003013F0 cmp dword ptr [ebp-8],3
003013F4 jae ShowString+54h (0301414h)
{
printf(pBuffer[i]);
003013F6 mov esi,esp
003013F8 mov eax,dword ptr [ebp-8]
003013FB mov ecx,dword ptr [pBuffer] ;取下标值
003013FE mov edx,dword ptr [ecx+eax*4] ;一维数组寻址
00301401 push edx ;将字符串首地址压入栈
00301402 call dword ptr ds:[309110h]
00301408 add esp,4
0030140B cmp esi,esp
0030140D call __RTC_CheckEsp (0301140h)
}
00301412 jmp ShowString+27h (03013E7h)
}

数组由三个指针变量组成,故长度为12字节;

同样存储三个字符串,但指针数组存储的是各个字符串的首地址,而二位字符数组中存储着每个字符串的字符数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char pBuffer1[3][10] = { { "Hello" }, { "World" }, { "!\r\n" } };
00E33CCD mov eax,dword ptr ds:[00E35858h] ;一维数组初始化过程
00E33CD2 mov dword ptr [pBuffer1],eax
00E33CD5 mov cx,word ptr ds:[0E3585Ch]
00E33CDC mov word ptr [ebp-38h],cx
00E33CE0 xor eax,eax
00E33CE2 mov dword ptr [ebp-36h],eax
00E33CE5 mov eax,dword ptr ds:[00E35860h]
00E33CEA mov dword ptr [ebp-32h],eax
00E33CED mov cx,word ptr ds:[0E35864h]
00E33CF4 mov word ptr [ebp-2Eh],cx
00E33CF8 xor eax,eax
00E33CFA mov dword ptr [ebp-2Ch],eax
00E33CFD mov eax,dword ptr ds:[00E35868h]
00E33D02 mov dword ptr [ebp-28h],eax
00E33D05 xor eax,eax
00E33D07 mov dword ptr [ebp-24h],eax
00E33D0A mov word ptr [ebp-20h],ax

二维字符数组初始化过程中,赋值的不是字符串地址,而是期中字符数据,据此可以明显地区分它与字符指针数组;

指向数组的指针变量

当指针变量保存的数据为数组的首地址,且将此地址结尾数组时,此指针变量本称为数组指针;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
	char cArray[3][10] = { "Hello", "World", "!\r\n" };
001E3D7E mov eax,dword ptr ds:[001E5858h]
001E3D83 mov dword ptr [cArray],eax
001E3D86 mov cx,word ptr ds:[1E585Ch]
001E3D8D mov word ptr [ebp-60h],cx
001E3D91 xor eax,eax
001E3D93 mov dword ptr [ebp-5Eh],eax
001E3D96 mov eax,dword ptr ds:[001E5860h]
001E3D9B mov dword ptr [ebp-5Ah],eax
001E3D9E mov cx,word ptr ds:[1E5864h]
001E3DA5 mov word ptr [ebp-56h],cx
001E3DA9 xor eax,eax
001E3DAB mov dword ptr [ebp-54h],eax
001E3DAE mov eax,dword ptr ds:[001E5868h]
001E3DB3 mov dword ptr [ebp-50h],eax
001E3DB6 xor eax,eax
001E3DB8 mov dword ptr [ebp-4Ch],eax
001E3DBB mov word ptr [ebp-48h],ax
char(*pArray)[10] = cArray;
001E3DBF lea eax,[cArray] ;取数组首地址存入ECX中
001E3DC2 mov dword ptr [pArray],eax
for (size_t i = 0; i < 3; i++)
001E3DC5 mov dword ptr [ebp-7Ch],0
001E3DCC jmp wmain+0D7h (01E3DD7h)
001E3DCE mov eax,dword ptr [ebp-7Ch]
001E3DD1 add eax,1
for (size_t i = 0; i < 3; i++)
001E3DD4 mov dword ptr [ebp-7Ch],eax
001E3DD7 cmp dword ptr [ebp-7Ch],3
001E3DDB jae wmain+103h (01E3E03h)
{
printf("%s\n", *pArray);
001E3DDD mov esi,esp
001E3DDF mov eax,dword ptr [pArray]
001E3DE2 push eax
001E3DE3 push 1E586Ch
001E3DE8 call dword ptr ds:[1E9110h]
001E3DEE add esp,8
001E3DF1 cmp esi,esp
001E3DF3 call __RTC_CheckEsp (01E1140h)
pArray++;
001E3DF8 mov eax,dword ptr [pArray]
001E3DFB add eax,0Ah
001E3DFE mov dword ptr [pArray],eax
}

函数指针

函数调用过程中,通过call指令跳转到首地址处,执行函数内的指令代码,既然是指针,当然可以使用指针变量进行存储。用于保存函数首地址的指针变量被称为函数指针;

1
2
3
4
5
6
7
8
9
	void(*pShow)(void) = Show;
00BA144E mov dword ptr [pShow],0BA11BDh ;函数名即为函数首地址,常量地址
pShow();
00BA1455 mov esi,esp ;
00BA1457 call dword ptr [pShow] ;间接调用
00BA145A cmp esi,esp
00BA145C call __RTC_CheckEsp (0BA1140h) ;检查栈平衡
Show();
00BA1461 call Show (0BA11BDh) ;直接调用

与函数调用最大的区别是函数是直接调用,而函数指针需要取出指针变量中保存的地址数据,间接调用函数;

函数指针是比较特殊的指针类型,由于其保存的地址数据为代码段内的地址信息,而非数据区,因此不存在偏移;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int __stdcall Show(int nShow)
{
010613D0 push ebp
010613D1 mov ebp,esp
010613D3 sub esp,0C0h
010613D9 push ebx
010613DA push esi
010613DB push edi
010613DC lea edi,[ebp-0C0h]
010613E2 mov ecx,30h
010613E7 mov eax,0CCCCCCCCh
010613EC rep stos dword ptr es:[edi]
printf("%d\n", nShow);
010613EE mov esi,esp
010613F0 mov eax,dword ptr [nShow]
010613F3 push eax
010613F4 push 1065858h
010613F9 call dword ptr ds:[1069110h]
010613FF add esp,8
01061402 cmp esi,esp
01061404 call __RTC_CheckEsp (01061140h)
return nShow;
01061409 mov eax,dword ptr [nShow]
}


int(__stdcall *pShow)(int) = Show;
01061A8E mov dword ptr [pShow],10611E5h ;获取函数首地址并保存
int nRet = pShow(3);
01061A95 mov esi,esp ;保存进入函数前栈顶,用于栈顶检查
01061A97 push 3 ;压入参数3
01061A99 call dword ptr [pShow] ;间接调用
01061A9C cmp esi,esp
01061A9E call __RTC_CheckEsp (01061140h)
01061AA3 mov dword ptr [nRet],eax
printf("%d\n", nRet);
01061AA6 mov esi,esp
01061AA8 mov eax,dword ptr [nRet] ;eax用于接受函数返回值
01061AAB push eax
01061AAC push 1065858h
01061AB1 call dword ptr ds:[1069110h]
01061AB7 add esp,8

字符串优化

二维数组

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//跟变量初始化一样c语言源码:
10: int Array[3][5] = {{1, 2, 3, 4, 5},{10, 20, 30, 40, 50},{100, 200, 300, 400, 500}};
00401028 mov dword ptr [ebp-3Ch],1
0040102F mov dword ptr [ebp-38h],2
00401036 mov dword ptr [ebp-34h],3
0040103D mov dword ptr [ebp-30h],4
00401044 mov dword ptr [ebp-2Ch],5
0040104B mov dword ptr [ebp-28h],0Ah
00401052 mov dword ptr [ebp-24h],14h
00401059 mov dword ptr [ebp-20h],1Eh
00401060 mov dword ptr [ebp-1Ch],28h
00401067 mov dword ptr [ebp-18h],32h
0040106E mov dword ptr [ebp-14h],64h
00401075 mov dword ptr [ebp-10h],0C8h
0040107C mov dword ptr [ebp-0Ch],12Ch
00401083 mov dword ptr [ebp-8],190h
0040108A mov dword ptr [ebp-4],1F4h



各种显示方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
1.常量下标显示:
printf("%d\r\n",ary[2][3]);

debug版本汇编:
00401091 mov eax,dword ptr [ebp-8]
00401094 push eax
00401095 push offset string "%d\n" (0042201c)
0040109A call printf (00401160)
0040109F add esp,8

分析:1.跟常量赋值一样,无法确定是数组
2.确定是数组了,公式公式:
ary+sizeof(int[5])*2+sizeof(int)*3(ebp‐3c)+(14*2)+(4*3)

2.前面是变量下标
debug汇编:
30: printf("%d\n",Array[argc][3]);
004010A2 mov ecx,dword ptr [ebp+8] ;出现乘法比例因子,没能搞这么大
004010A5 imul ecx,ecx,14h
004010A8 mov edx,dword ptr [ebp+ecx-30h] ;常量折叠,误算数组首地址,可以改
004010AC push edx
004010AD push offset string "%d\n" (0042201c)
004010B2 call printf (00401160)
004010B7 add esp,8
公式:
ary+sizeof(int[5])*argc+sizeof(int)*3

3.后面是变量下标
debug版本:
31: printf("%d\n",Array[2][argc]);
004010BA mov eax,dword ptr [ebp+8] ;常量折叠优化
004010BD mov ecx,dword ptr [ebp+eax*4-14h]
004010C1 push ecx
004010C2 push offset string "%d\n" (0042201c)
004010C7 call printf (00401160)
004010CC add esp,8
公式:
ary+sizeof(int[5])*2+sizeof(int)*argcebp‐3c+14*2+argc*4


4.两个都是表达式
debug版本:
32: printf("%d\n",Array[argc % -8][argc / 8]);
004010CF mov eax,dword ptr [ebp+8]
004010D2 cdq
004010D3 xor eax,edx
004010D5 sub eax,edx
004010D7 and eax,7
004010DA xor eax,edx
004010DC sub eax,edx
004010DE imul eax,eax,14h
004010E1 lea ecx,[ebp+eax-3Ch]
004010E5 mov eax,dword ptr [ebp+8]
004010E8 cdq
004010E9 and edx,7
004010EC add eax,edx
004010EE sar eax,3
004010F1 mov edx,dword ptr [ecx+eax*4]
004010F4 push edx
004010F5 push offset string "%d\n" (0042201c)
004010FA call printf (00401160)
004010FF add esp,8
公式:
ecx=(ebp‐3c)+(eax*14)ecx+(eax*4)


5.优化遍历二维数组
vc6下优化:
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 ary = word ptr -3Ch
.text:00401000 var_1C = dword ptr -1Ch
.text:00401000 var_18 = dword ptr -18h
.text:00401000 var_14 = dword ptr -14h
.text:00401000 var_10 = dword ptr -10h
.text:00401000 var_C = dword ptr -0Ch
.text:00401000 var_8 = dword ptr -8
.text:00401000 var_4 = dword ptr -4
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 sub esp, 3Ch
.text:00401003 push ebx
.text:00401004 mov ebx, 3
.text:00401009 push esi
.text:0040100A push edi
.text:0040100B mov dword ptr [esp+48h+ary], 1
.text:00401013 mov dword ptr [esp+48h+ary+4], 2
.text:0040101B mov dword ptr [esp+48h+ary+8], ebx
.text:0040101F mov dword ptr [esp+48h+ary+0Ch], 4
.text:00401027 mov dword ptr [esp+48h+ary+10h], 5
.text:0040102F mov dword ptr [esp+48h+ary+14h], 0Ah
.text:00401037 mov dword ptr [esp+48h+ary+18h], 14h
.text:0040103F mov dword ptr [esp+48h+ary+1Ch], 1Eh
.text:00401047 mov [esp+48h+var_1C], 28h
.text:0040104F mov [esp+48h+var_18], 32h
.text:00401057 mov [esp+48h+var_14], 64h
.text:0040105F mov [esp+48h+var_10], 0C8h
.text:00401067 mov [esp+48h+var_C], 12Ch
.text:0040106F mov [esp+48h+var_8], 190h
.text:00401077 mov [esp+48h+var_4], 1F4h
.text:0040107F lea esi, [esp+48h+ary]
.text:00401083
.text:00401083 loc_401083: ; CODE XREF: _main+9F↓j
.text:00401083 mov edi, 5
.text:00401088
.text:00401088 loc_401088: ; CODE XREF: _main+9C↓j
.text:00401088 mov eax, [esi]
.text:0040108A push eax
.text:0040108B push offset unk_407030
.text:00401090 call sub_4010B0
.text:00401095 add esp, 8
.text:00401098 add esi, 4
.text:0040109B dec edi
.text:0040109C jnz short loc_401088
.text:0040109E dec ebx
.text:0040109F jnz short loc_401083
.text:004010A1 pop edi
.text:004010A2 pop esi
.text:004010A3 xor eax, eax
.text:004010A5 pop ebx
.text:004010A6 add esp, 3Ch
.text:004010A9 retn
.text:004010A9 _main endp


2019下优化:
.text:00401040 _main proc near ; CODE XREF: __scrt_common_main_seh+F5↓p
.text:00401040
.text:00401040 Array = dword ptr -3Ch
.text:00401040
.text:00401040 push ebp
.text:00401041 mov ebp, esp
.text:00401043 sub esp, 3Ch
.text:00401046 movaps xmm0, ds:__xmm@00000004000000030000000200000001
.text:0040104D movups xmmword ptr [ebp+Array], xmm0
.text:00401051 push ebx
.text:00401052 movaps xmm0, ds:__xmm@0000001e000000140000000a00000005
.text:00401059 mov ebx, 3
.text:0040105E push esi
.text:0040105F movups xmmword ptr [ebp+Array+10h], xmm0
.text:00401063 lea esi, [ebp+Array]
.text:00401066 mov [ebp+Array+30h], 12Ch
.text:0040106D movaps xmm0, ds:__xmm@000000c8000000640000003200000028
.text:00401074 push edi
.text:00401075 movups xmmword ptr [ebp+Array+20h], xmm0
.text:00401079 mov [ebp+Array+34h], 190h
.text:00401080 mov [ebp+Array+38h], 1F4h
.text:00401087 nop word ptr [eax+eax+00000000h]
.text:00401090
.text:00401090 loc_401090: ; CODE XREF: _main+7A↓j
.text:00401090 mov edi, 5
.text:00401095 db 66h, 66h
.text:00401095 nop word ptr [eax+eax+00000000h]
.text:004010A0
.text:004010A0 loc_4010A0: ; CODE XREF: _main+75↓j
.text:004010A0 push dword ptr [esi]
.text:004010A2 push offset _Format ; "%d\n"
.text:004010A7 call _printf
.text:004010AC add esp, 8
.text:004010AF add esi, 4
.text:004010B2 sub edi, 1
.text:004010B5 jnz short loc_4010A0
.text:004010B7 sub ebx, 1
.text:004010BA jnz short loc_401090
.text:004010BC pop edi
.text:004010BD pop esi
.text:004010BE xor eax, eax
.text:004010C0 pop ebx
.text:004010C1 mov esp, ebp
.text:004010C3 pop ebp
.text:004010C4 retn
.text:004010C4 _main endp
.text:004010C4

二维数组访问优化