1. 程式人生 > >C++逆向第十課-----陣列與指標的定址

C++逆向第十課-----陣列與指標的定址

好久沒寫部落格了,做人最難的事情就是堅持,不管怎麼樣,從今天開始重新撿起來,做人還是應該有始有終

0x00 逆向時候判斷陣列的依據

  • 證明在記憶體上是連續的,中間不留空
  • 資料型別上要有一致性
    如何證明:
  • 比例因子定址,例如:mov reg,[ebp + esi * 4 - 8]
  • 迴圈迭代

    陣列的優勢就是迴圈批量的操作

0x01 陣列與區域性變數賦值的對比

原始碼:

8:        int n1 = 1;
00401028   mov         dword ptr [ebp-4],1
9:        int n2 = 2;
0040102F   mov         dword ptr [ebp-8]
,2 10: int n3 = 3; 00401036 mov dword ptr [ebp-0Ch],3 11: int n4 = 4; 0040103D mov dword ptr [ebp-10h],4 12: int n5 = 5; 00401044 mov dword ptr [ebp-14h],5 13: 14: int nArr[5] = {1,2,3,4,5}; 0040104B mov dword ptr [ebp-28h],1 00401052 mov dword ptr [ebp-24h]
,2 00401059 mov dword ptr [ebp-20h],3 00401060 mov dword ptr [ebp-1Ch],4 00401067 mov dword ptr [ebp-18h],5

由上面的程式碼可以看出連續定義相同型別的區域性變數和陣列賦值順序是相反的

0x02 陣列與字串

在C++中,字串本身就是陣列

字串初始化程式碼Debug與Release版對比

C++原始碼

    char szHello[] = "Hello World!!!!";
    printf("%s\r\n",szHello);

Debug版反彙編

mov     eax, dword ptr ds:aHelloWorld ; "Hello World!!!!"
mov     [ebp+var_38], eax
mov     ecx, dword ptr ds:aHelloWorld+4 ; "o World!!!!"
mov     [ebp+var_34], ecx
mov     edx, dword ptr ds:aHelloWorld+8 ; "rld!!!!"
mov     [ebp+var_30], edx
mov     eax, dword ptr ds:aHelloWorld+0Ch ; "!!!"
mov     [ebp+var_2C], eax
lea     ecx, [ebp+var_38]
push    ecx
push    offset aS_0     ; "%s\r\n"
call    _printf
add     esp, 8

Release版反彙編

mov     ecx, dword_40603C
mov     eax, dword_406038
mov     edx, dword_406040
mov     [esp+10h+var_C], ecx
lea     ecx, [esp+10h+var_10]
mov     [esp+10h+var_10], eax
mov     eax, dword_406044
push    ecx
push    offset aS       ; "%s\r\n"
mov     [esp+18h+var_8], edx
mov     [esp+18h+var_4], eax
call    _printf

對比以上原始碼可知道,字串賦值操作都是,將.data資料區的地址給連續的棧空間,相當於給陣列賦初值

0x03陣列字與字串處理

在Debug版下,將直接呼叫庫函式處理字串,但是在Release版本下,會採用內聯彙編進行優化。但是在高版本的編譯器中這些優化又消失瞭如VS2013,因為這些優化都整合到了硬體當中去了

C++原始碼

char szHello[] = "Hello World!!!!";
printf("%d\r\n",strlen(szHello));

Debug版反彙編

mov     eax, dword ptr ds:aHelloWorld ; "Hello World!!!!"
mov     dword ptr [ebp+Str], eax
mov     ecx, dword ptr ds:aHelloWorld+4 ; "o World!!!!"
mov     [ebp+var_C], ecx
mov     edx, dword ptr ds:aHelloWorld+8 ; "rld!!!!"
mov     [ebp+var_8], edx
mov     eax, dword ptr ds:aHelloWorld+0Ch ; "!!!"
mov     [ebp+var_4], eax
lea     ecx, [ebp+Str]
push    ecx             ; Str
call    _strlen

Release版反彙編

mov     eax, dword_406038
mov     ecx, dword_40603C
mov     edx, dword_406040
mov     [esp+10h+var_10], eax
mov     eax, dword_406044
push    edi
mov     [esp+14h+var_C], ecx
mov     [esp+14h+var_4], eax
//以下為無分支求字串長度的彙編程式碼
lea     edi, [esp+14h+var_10]
or      ecx, 0FFFFFFFFh
xor     eax, eax
mov     [esp+14h+var_8], edx
repne scasb
not     ecx
dec     ecx
push    ecx
push    offset aD       ; "%d\r\n"
call    _printf
add     esp, 8

0x04下標定址和指標定址

指標定址方式不但沒有下標定址方式便利,而且效率也比下標定址低。這一點我們可以用反彙編證明。

C++原始碼

 char szHello[] = "Hello!!";
 char * pChar = "Hello!!";
 printf("%c\r\n",szHello[0]);
 printf("%c\r\n",* pChar);

Debug版反彙編

11:       char szHello[] = "Hello!!";
0040B4B8   mov         eax,[string "Hello!!" (0041fe58)]
0040B4BD   mov         dword ptr [ebp-8],eax
0040B4C0   mov         ecx,dword ptr [string "Hello World!!!!"+4 (0041fe5c)]
0040B4C6   mov         dword ptr [ebp-4],ecx
12:       char * pChar = "Hello!!";
0040B4C9   mov         dword ptr [ebp-0Ch],offset string "Hello!!" (0041fe58)
13:       printf("%c\r\n",szHello[0]);
0040B4D0   movsx       edx,byte ptr [ebp-8]
0040B4D4   push        edx
0040B4D5   push        offset string "%d\r\n" (0041fe50)
0040B4DA   call        printf (0040b7b0)
0040B4DF   add         esp,8
14:       printf("%c\r\n",* pChar);
0040B4E2   mov         eax,dword ptr [ebp-0Ch]
0040B4E5   movsx       ecx,byte ptr [eax]
0040B4E8   push        ecx
0040B4E9   push        offset string "%d\r\n" (0041fe50)
0040B4EE   call        printf (0040b7b0)
0040B4F3   add         esp,8

由以上反彙編程式碼可以看出指標多了一次間接訪問

0x04多維陣列下標定址

以二位下標為例子
C++原始碼

int ary[3][5] = {
    {1,2,3,4,5},
    {1,2,3,4,5},
    {1,2,3,4,5}
    };
 printf("%d\r\n",ary[argc + 1][0]);

Debug版反彙編

17:       printf("%d\r\n",ary[argc + 1][0]);
0040B521   mov         eax,dword ptr [ebp+8]
0040B524   add         eax,1
0040B527   imul        eax,eax,14h
0040B52A   mov         ecx,dword ptr [ebp+eax-3Ch]
0040B52E   push        ecx
0040B52F   push        offset string "%d\r\n" (0041fe50)
0040B534   call        printf (0040b7b0)
0040B539   add         esp,8

可以看出陣列下標定址公式為
二維陣列type nArray[M][N],使用i,j作為下標定址

nArray + i * N * sizeof(type) + j * sizeof(type)
= nArray + i * N * sizeof(type) + j*sizeof(type)

Release版反彙編

mov     ecx, [esp+eax*4+3Ch+var_28]
push    ecx
push    offset aD       ; "%d\r\n"
call    _printf

由以上反彙編可以看出有些許優化,具體優化公式為

nArray + i * N * sizeof(type) + j * sizeof(type)
=nArray + sizeof(type)* (i * N + j