C++反汇编: 基础知识(7)

反汇编(Disassembly),即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编语言对软件调试、漏洞分析、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面,该笔记整理了C++反汇编的一些常识.

变量与常量

数值类型变量: 整数,浮点数,等类型其反汇编结果基本相同.

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 20;
    int z = 0;
    z = x + y;
    return 0;
}

反汇编结果如下,首先lea取地址,然后ECX=13,这里的13是因为我们有3个整数型变量,每个变量占用4个字节,所以4x3=12次,将EAX设置为CCCCCCC,并通过rep指令填充内存地址从[edi]指向的位置开始填充,填充14次,同样的也初始化了堆栈,接着通过mov赋值语句将内存地址分别初始化为指定的数值.

0040D709  |.  8D7D B4       lea     edi, dword ptr [ebp-4C]
0040D70C  |.  B9 13000000   mov     ecx, 13
0040D711  |.  B8 CCCCCCCC   mov     eax, CCCCCCCC
0040D716  |.  F3:AB         rep     stos dword ptr es:[edi]
0040D718  |.  C745 FC 0A000>mov     dword ptr [ebp-4], 0A
0040D71F  |.  C745 F8 14000>mov     dword ptr [ebp-8], 14
0040D726  |.  C745 F4 00000>mov     dword ptr [ebp-C], 0
0040D72D  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
0040D730  |.  0345 F8       add     eax, dword ptr [ebp-8]
0040D733  |.  8945 F4       mov     dword ptr [ebp-C], eax

字符类型:

int main(int argc, char* argv[])
{
    char x = 'a';
    char y = 'b';
    char z = 'c';
    z = x+y;
    printf("%s",z);
    return 0;
}

反汇编结果如下,观察发现字符型的表现形式与整数类型基本一致,只是在数据位大小方面有所区别,如上int类型使用dword作为存储单位,而字符类型则默认使用byte形式存储.

00410659  |.  8D7D B4       lea     edi, dword ptr [ebp-4C]
0041065C  |.  B9 13000000   mov     ecx, 13
00410661  |.  B8 CCCCCCCC   mov     eax, CCCCCCCC
00410666  |.  F3:AB         rep     stos dword ptr es:[edi]
00410668  |.  C645 FC 61    mov     byte ptr [ebp-4], 61
0041066C  |.  C645 F8 62    mov     byte ptr [ebp-8], 62
00410670  |.  C645 F4 63    mov     byte ptr [ebp-C], 63
00410674  |.  0FBE45 FC     movsx   eax, byte ptr [ebp-4]
00410678  |.  0FBE4D F8     movsx   ecx, byte ptr [ebp-8]
0041067C  |.  03C1          add     eax, ecx
0041067E  |.  8845 F4       mov     byte ptr [ebp-C], al

字符串:

int main(int argc, char* argv[])
{
    char x[] = "hello lyshark";
    printf("%s",x);
    return 0;
}
00401019  |.  8D7D B0            lea     edi, dword ptr [ebp-50]
0040101C  |.  B9 14000000        mov     ecx, 14
00401021  |.  B8 CCCCCCCC        mov     eax, CCCCCCCC
00401026  |.  F3:AB              rep     stos dword ptr es:[edi]
00401028  |.  A1 1C204200        mov     eax, dword ptr [42201C]
0040102D  |.  8945 F0            mov     dword ptr [ebp-10], eax
00401030  |.  8B0D 20204200      mov     ecx, dword ptr [422020]
00401036  |.  894D F4            mov     dword ptr [ebp-C], ecx
00401039  |.  8B15 24204200      mov     edx, dword ptr [422024]
0040103F  |.  8955 F8            mov     dword ptr [ebp-8], edx
00401042  |.  66:A1 28204200     mov     ax, word ptr [422028]
00401048  |.  66:8945 FC         mov     word ptr [ebp-4], ax
0040104C  |.  8D4D F0            lea     ecx, dword ptr [ebp-10]   ; 存储字符串首地址

长字符串:

int main(int argc, char* argv[])
{
    char temp[] = "welcome to lyshark blog";
    printf("%s",temp);
    return 0;
}
00401019  |.  8D7D A8            lea     edi, dword ptr [ebp-58]
0040101C  |.  B9 16000000        mov     ecx, 16
00401021  |.  B8 CCCCCCCC        mov     eax, CCCCCCCC
00401026  |.  F3:AB              rep     stos dword ptr es:[edi]
00401028  |.  B9 06000000        mov     ecx, 6
0040102D  |.  BE AC2F4200        mov     esi, 00422FAC                              ;  ASCII "welcome to lyshark blog"
00401032  |.  8D7D E8            lea     edi, dword ptr [ebp-18]
00401035  |.  F3:A5              rep     movs dword ptr es:[edi], dword ptr [esi]
00401037  |.  8D45 E8            lea     eax, dword ptr [ebp-18]
0040103A  |.  50                 push    eax                                        ; /<%s>
0040103B  |.  68 04244200        push    00422404                                   ; |format = "%s"
00401040  |.  E8 3BC60000        call    printf                                     ; \printf

字符串指针:

int main(int argc, char* argv[])
{
    char *x = "hello lyshark";
    printf("%s",x);
    return 0;
}
00401019  |.  8D7D BC            lea     edi, dword ptr [ebp-44]
0040101C  |.  B9 11000000        mov     ecx, 11
00401021  |.  B8 CCCCCCCC        mov     eax, CCCCCCCC
00401026  |.  F3:AB              rep     stos dword ptr es:[edi]
00401028  |.  C745 FC 1C204200   mov     dword ptr [ebp-4], 0042201C
0040102F  |.  8B45 FC            mov     eax, dword ptr [ebp-4]           ;  aaa.0042201C
00401032  |.  50                 push    eax                              ; /<%s>
00401033  |.  68 04244200        push    00422404                         ; |format = "%s"
00401038  |.  E8 43C60000        call    printf                           ; \printf

数组类型:

int main(int argc, char* argv[])
{
    int array[] = {1,2,3,4,5};
    printf("%d\n",array[0]);
    printf("%d\n",array[1]);
    return 0;
}
0040D718  |.  C745 EC 01000000   mov     dword ptr [ebp-14], 1
0040D71F  |.  C745 F0 02000000   mov     dword ptr [ebp-10], 2
0040D726  |.  C745 F4 03000000   mov     dword ptr [ebp-C], 3
0040D72D  |.  C745 F8 04000000   mov     dword ptr [ebp-8], 4
0040D734  |.  C745 FC 05000000   mov     dword ptr [ebp-4], 5
0040D73B  |.  8B45 EC            mov     eax, dword ptr [ebp-14]
0040D73E  |.  50                 push    eax                                        ; /<%d> = 1
0040D73F  |.  68 1C204200        push    0042201C                                   ; |format = "%d
"
0040D744  |.  E8 37FFFFFF        call    printf                                     ; \printf
0040D749  |.  83C4 08            add     esp, 8
0040D74C  |.  8B4D F0            mov     ecx, dword ptr [ebp-10]
0040D74F  |.  51                 push    ecx                                        ; /<%d>
0040D750  |.  68 1C204200        push    0042201C                                   ; |format = "%d
"
0040D755  |.  E8 26FFFFFF        call    printf                                     ; \printf

二维数组是一位的高阶抽象.

常量折叠: 在编译前遇到常量,都会进行计算,得出一个新的常量值,这样则可以直接Push常量节约资源.

int main(int argc, char* argv[])
{
    printf("%d\n",1+2);     // 常量+常量
    return 0;
}
0040D438  |.  C745 FC 00000000   mov     dword ptr [ebp-4], 1
0040D43F  |.  C745 F8 01000000   mov     dword ptr [ebp-8], 2
0040D446  |.  6A 03              push    3                                          ; 1+2直接Push3
0040D448  |.  68 1C204200        push    0042201C                                   ; |format = "%d"
0040D44D  |.  E8 9E020000        call    printf                                     ; \printf
0040D452  |.  83C4 08            add     esp, 8

常量传播: 如果两个变量从来都没被修改过,也没有传入过参数,在VS2013编译器中会被优化成常量.

int main(int argc, char* argv[])
{
    int x = 1;
    int y = 3;
    printf("%d\n",x+y);     // 变量+变量
    return 0;
}
0040D438  |.  C745 FC 01000>mov     dword ptr [ebp-4], 1
0040D43F  |.  C745 F8 03000>mov     dword ptr [ebp-8], 3
0040D44C  |.  50            push    4                                ; /<%d>
0040D44D  |.  68 1C204200   push    0042201C                         ; |format = "%d"
0040D452  |.  E8 99020000   call    printf                           ; \printf
0040D457  |.  83C4 08       add     esp, 8

窥孔优化:一种很局部的优化方式,编译器仅仅在一个基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,过一些认为可能带来性能提升的转换规则,或者通过整体的分析,通过指令转换,提升代码性能。这个窥孔,你可以认为是一个滑动窗口,编译器在实施窥孔优化时,就仅仅分析这个窗口内的指令。每次转换之后,可能还会暴露相邻窗口之间的某些优化机会,所以可以多次调用窥孔优化,尽可能提升性能

运算符相关

乘法:

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 300;
    int z = 0;
    z = x * y;
    printf("%d\n",z);
    return 0;
}
0040D438  |.  C745 FC 0A000>mov     dword ptr [ebp-4], 0A
0040D43F  |.  C745 F8 2C010>mov     dword ptr [ebp-8], 12C
0040D446  |.  C745 F4 00000>mov     dword ptr [ebp-C], 0
0040D44D  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
0040D450  |.  0FAF45 F8     imul    eax, dword ptr [ebp-8]
0040D454  |.  8945 F4       mov     dword ptr [ebp-C], eax
0040D457  |.  8B4D F4       mov     ecx, dword ptr [ebp-C]
0040D45A  |.  51            push    ecx                              ; /<%d>
0040D45B  |.  68 1C204200   push    0042201C                         ; |format = "%d"
0040D460  |.  E8 8B020000   call    printf                           ; \printf

除法: cdq指令扩展标志位 edx 高位 :eax 低位

int main(int argc, char* argv[])
{
    int x = 1000;
    int y = 20;
    int z = 0;
    z = x/y;
    printf("%d\n",z);
    return 0;
}
0040D438  |.  C745 FC E8030>mov     dword ptr [ebp-4], 3E8
0040D43F  |.  C745 F8 14000>mov     dword ptr [ebp-8], 14
0040D446  |.  C745 F4 00000>mov     dword ptr [ebp-C], 0
0040D44D  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
0040D450  |.  99            cdq
0040D451  |.  F77D F8       idiv    dword ptr [ebp-8]
0040D454  |.  8945 F4       mov     dword ptr [ebp-C], eax
0040D457  |.  8B45 F4       mov     eax, dword ptr [ebp-C]
0040D45A  |.  50            push    eax                              ; /<%d>
0040D45B  |.  68 1C204200   push    0042201C                         ; |format = "%d"
0040D460  |.  E8 8B020000   call    printf                           ; \printf

复合运算:

int main(int argc, char* argv[])
{
    int a = 10;
    int b = 20;
    int x = (a*a+b);
    int y = x*5;
    return 0;
}
00401028  |.  C745 FC 0A000>mov     dword ptr [ebp-4], 0A
0040102F  |.  C745 F8 14000>mov     dword ptr [ebp-8], 14
00401036  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
00401039  |.  0FAF45 FC     imul    eax, dword ptr [ebp-4]
0040103D  |.  0345 F8       add     eax, dword ptr [ebp-8]
00401040  |.  8945 F4       mov     dword ptr [ebp-C], eax
00401043  |.  8B4D F4       mov     ecx, dword ptr [ebp-C]
00401046  |.  6BC9 05       imul    ecx, ecx, 5
00401049  |.  894D F0       mov     dword ptr [ebp-10], ecx

多目运算符:

int main(int argc, char* argv[])
{
    unsigned int temp;
    scanf("%d",&temp);
    printf("%d\r\n",temp == 0 ? 0:-1);    // 针对有符号数
    printf("%d\r\n",temp == 0 ? 1:0);     // 针对无符号数
    printf("%d\r\n",temp >= 1 ? 35:98);   // 大于等于
    return 0;
}

针对有符号数

0040F979  |.  8B4D FC       mov     ecx, dword ptr [ebp-4]
0040F97C  |.  F7D9          neg     ecx
0040F97E  |.  1BC9          sbb     ecx, ecx
0040F980  |.  51            push    ecx                              ; /<%d>
0040F981  |.  68 802E4200   push    00422E80                         ; |format = "%d"
0040F986  |.  E8 45FFFFFF   call    printf                           ; \printf
0040F98B  |.  83C4 08       add     esp, 8

针对无符号数

0040F990  |.  837D FC 00    cmp     dword ptr [ebp-4], 0
0040F994  |.  0F94C2        sete    dl
0040F997  |.  52            push    edx                              ; /<%d>
0040F998  |.  68 802E4200   push    00422E80                         ; |format = "%d"
0040F99D  |.  E8 2EFFFFFF   call    printf                           ; \printf
0040F9A2  |.  83C4 08       add     esp, 8

大于等于符号

0040F9A5  |.  837D FC 01    cmp     dword ptr [ebp-4], 1
0040F9A9  |.  1BC0          sbb     eax, eax
0040F9AB  |.  83E0 3F       and     eax, 3F
0040F9AE  |.  83C0 23       add     eax, 23
0040F9B1  |.  50            push    eax                              ; /<%d>
0040F9B2  |.  68 802E4200   push    00422E80                         ; |format = "%d"
0040F9B7  |.  E8 14FFFFFF   call    printf                           ; \printf
0040F9BC  |.  83C4 08       add     esp, 8

流程控制

IF

条件分支:

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 20;
    int z = 30;
    if( x >= y){
        printf("x >= y");
    }else if(z >= x){
        printf("z >= x");
    }
    return 0;
}
00401028    C745 FC 0A00000>mov     dword ptr [ebp-4], 0A
0040102F    C745 F8 1400000>mov     dword ptr [ebp-8], 14
00401036    C745 F4 1E00000>mov     dword ptr [ebp-C], 1E
0040103D    8B45 FC         mov     eax, dword ptr [ebp-4]
00401040    3B45 F8         cmp     eax, dword ptr [ebp-8]
00401043    7C 0F           jl      short 00401054
00401045    68 24204200     push    00422024                         ; ASCII "x >= y"
0040104A    E8 51000000     call    printf
0040104F    83C4 04         add     esp, 4
00401052    EB 15           jmp     short 00401069
00401054    8B4D F4         mov     ecx, dword ptr [ebp-C]
00401057    3B4D FC         cmp     ecx, dword ptr [ebp-4]
0040105A    7C 0D           jl      short 00401069
0040105C    68 1C204200     push    0042201C                         ; ASCII "z >= x"
00401061    E8 3A000000     call    printf
00401066    83C4 04         add     esp, 4

嵌套条件分支:

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 20;
    int z = 30;
    if( z >= y){
        if(x < y){
            printf(" z>=y and x=y and x >y");  
        }
    }
    return 0;
}
00401028  |.  C745 FC 0A000000 mov     dword ptr [ebp-4], 0A
0040102F  |.  C745 F8 14000000 mov     dword ptr [ebp-8], 14
00401036  |.  C745 F4 1E000000 mov     dword ptr [ebp-C], 1E
0040103D  |.  8B45 F4          mov     eax, dword ptr [ebp-C]
00401040  |.  3B45 F8          cmp     eax, dword ptr [ebp-8]
00401043  |.  7C 24            jl      short 00401069
00401045  |.  8B4D FC          mov     ecx, dword ptr [ebp-4]
00401048  |.  3B4D F8          cmp     ecx, dword ptr [ebp-8]
0040104B  |.  7D 0F            jge     short 0040105C
0040104D  |.  68 1C204200      push    0042201C                         ; /format = " z>=y and x  68 AC2F4200      push    00422FAC                         ; /format = " z >=y and x >y"
00401061  |.  E8 3A000000      call    printf                           ; \printf
00401066  |.  83C4 04          add     esp, 4

and

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 20;
    int z = 30;
    if(xx)
    {
        printf("xx");
    }
    return 0;
}
00401028  |.  C745 FC 0A000000 mov     dword ptr [ebp-4], 0A
0040102F  |.  C745 F8 14000000 mov     dword ptr [ebp-8], 14
00401036  |.  C745 F4 1E000000 mov     dword ptr [ebp-C], 1E
0040103D  |.  8B45 FC          mov     eax, dword ptr [ebp-4]
00401040  |.  3B45 F8          cmp     eax, dword ptr [ebp-8]
00401043  |.  7D 15            jge     short 0040105A
00401045  |.  8B4D F4          mov     ecx, dword ptr [ebp-C]
00401048  |.  3B4D FC          cmp     ecx, dword ptr [ebp-4]
0040104B  |.  7E 0D            jle     short 0040105A
0040104D  |.  68 AC2F4200      push    00422FAC                         ; /format = "xx"
00401052  |.  E8 49000000      call    printf                           ; \printf
00401057  |.  83C4 04          add     esp, 4

OR:

int main(int argc, char* argv[])
{
    int x = 10;
    int y = 20;
    int z = 30;
    if(xx)
    {
        printf("xx");
    }
    return 0;
}
00401028  |.  C745 FC 0A000000 mov     dword ptr [ebp-4], 0A
0040102F  |.  C745 F8 14000000 mov     dword ptr [ebp-8], 14
00401036  |.  C745 F4 1E000000 mov     dword ptr [ebp-C], 1E
0040103D  |.  8B45 FC          mov     eax, dword ptr [ebp-4]
00401040  |.  3B45 F8          cmp     eax, dword ptr [ebp-8]
00401043  |.  7C 08            jl      short 0040104D
00401045  |.  8B4D F4          mov     ecx, dword ptr [ebp-C]
00401048  |.  3B4D FC          cmp     ecx, dword ptr [ebp-4]
0040104B  |.  7E 0D            jle     short 0040105A
0040104D  |.  68 AC2F4200      push    00422FAC                         ; /format = "xx"
00401052  |.  E8 49000000      call    printf                           ; \printf
00401057  |.  83C4 04          add     esp, 4

While

while 单循环:

int main(int argc, char* argv[])
{
    int x = 0;
    while(x <= 10){
        printf("%d",x);
        x++;
    }
    return 0;
}

x++ 是jge来操控,++x是jg

00401028  |.  C745 FC 00000 |mov     dword ptr [ebp-4], 0
0040102F  |>  837D FC 0A    |cmp     dword ptr [ebp-4], 0A
00401033  |.  7D 1C         |jge     short 00401051
00401035  |.  8B45 FC       |mov     eax, dword ptr [ebp-4]
00401038  |.  50            |push    eax                             ; /<%d>
00401039  |.  68 1C204200   |push    0042201C                        ; |format = "%d"
0040103E  |.  E8 5D000000   |call    printf                          ; \printf
00401043  |.  83C4 08       |add     esp, 8
00401046  |.  8B4D FC       |mov     ecx, dword ptr [ebp-4]
00401049  |.  83C1 01       |add     ecx, 1
0040104C  |.  894D FC       |mov     dword ptr [ebp-4], ecx
0040104F  |.^ EB DE         |jmp     short 0040102F

while 嵌套循环:

int main(int argc, char* argv[])
{
    int x = 0;
    int y = 0;
    while(x <= 10){
        while(y <=5){
            printf("%d,%d \n",x,y);
            y++;
        }
            x++;
            y=0;
    }
    return 0;
}
00401028  |.  C745 FC 00000000   |mov     dword ptr [ebp-4], 0
0040102F  |.  C745 F8 00000000   |mov     dword ptr [ebp-8], 0
00401036  |>  837D FC 0A         |cmp     dword ptr [ebp-4], 0A
0040103A  |.  7F 38              |jg      short 00401074
0040103C  |>  837D F8 05         |cmp     dword ptr [ebp-8], 5
00401040  |.  7F 20              |jg      short 00401062
00401042  |.  8B45 F8            |mov     eax, dword ptr [ebp-8]
00401045  |.  50                 |push    eax                            ; /<%d>
00401046  |.  8B4D FC            |mov     ecx, dword ptr [ebp-4]         ; |
00401049  |.  51                 |push    ecx                            ; |<%d>
0040104A  |.  68 1C204200        |push    0042201C                       ; |format = "%d,%d"
0040104F  |.  E8 4C000000        |call    printf                         ; \printf
00401054  |.  83C4 0C            |add     esp, 0C
00401057  |.  8B55 F8            |mov     edx, dword ptr [ebp-8]
0040105A  |.  83C2 01            |add     edx, 1
0040105D  |.  8955 F8            |mov     dword ptr [ebp-8], edx
00401060  |.^ EB DA              |jmp     short 0040103C
00401062  |>  8B45 FC            |mov     eax, dword ptr [ebp-4]
00401065  |.  83C0 01            |add     eax, 1
00401068  |.  8945 FC            |mov     dword ptr [ebp-4], eax
0040106B  |.  C745 F8 00000000   |mov     dword ptr [ebp-8], 0
00401072  |.^ EB C2              |jmp     short 00401036

do-while 循环:

int main(int argc, char* argv[])
{
    int x = 0;
    do
    {
        if(x/2 == 0){
            printf("%d \n",x);
        }
        x++;
    }while(x<=10);
    return 0;
}
00401028  |.  C745 FC 00000000   |mov     dword ptr [ebp-4], 0
0040102F  |>  8B45 FC            |mov     eax, dword ptr [ebp-4]
00401032  |.  99                 |cdq
00401033  |.  2BC2               |sub     eax, edx
00401035  |.  D1F8               |sar     eax, 1
00401037  |.  85C0               |test    eax, eax
00401039  |.  75 11              |jnz     short 0040104C
0040103B  |.  8B45 FC            |mov     eax, dword ptr [ebp-4]
0040103E  |.  50                 |push    eax                             ; /<%d>
0040103F  |.  68 1C204200        |push    0042201C                        ; |format = "%d"
00401044  |.  E8 57000000        |call    printf                          ; \printf
00401049  |.  83C4 08            |add     esp, 8
0040104C  |>  8B4D FC            |mov     ecx, dword ptr [ebp-4]
0040104F  |.  83C1 01            |add     ecx, 1
00401052  |.  894D FC            |mov     dword ptr [ebp-4], ecx
00401055  |.  837D FC 0A         |cmp     dword ptr [ebp-4], 0A
00401059  |.^ 7E D4              |jle     short 0040102F

FOR

单分支:

int main(int argc, char* argv[])
{
    int x;
    for(x=0;x<=10;x++)
    {
        printf("%d\n",x);
    }
    return 0;
}
00401028  |.  C745 FC 00000000   |mov     dword ptr [ebp-4], 0
0040102F  |.  EB 09              |jmp     short 0040103A
00401031  |>  8B45 FC            |mov     eax, dword ptr [ebp-4]
00401034  |.  83C0 01            |add     eax, 1
00401037  |.  8945 FC            |mov     dword ptr [ebp-4], eax
0040103A  |>  837D FC 0A         |cmp     dword ptr [ebp-4], 0A
0040103E  |.  7F 13              |jg      short 00401053
00401040  |.  8B4D FC            |mov     ecx, dword ptr [ebp-4]
00401043  |.  51                 |push    ecx                             ; /<%d>
00401044  |.  68 1C204200        |push    0042201C                        ; |format = "%d"
00401049  |.  E8 52000000        |call    printf                          ; \printf
0040104E  |.  83C4 08            |add     esp, 8
00401051  |.^ EB DE              |jmp     short 00401031

多分支:

int main(int argc, char* argv[])
{
    int array[2][3] = {{1,2,3},{1,2,3}};
    int x,y;

    for(x=0;x<2;x++)
    {
        for(y=0;y<3;y++)
        {
            printf("%d \n",array[x][y]);
        }
    }
    return 0;
}
0040D779  |. /EB 09              |jmp     short 0040D784
0040D77B  |> |8B45 E4            |mov     eax, dword ptr [ebp-1C]
0040D77E  |. |83C0 01            |add     eax, 1
0040D781  |. |8945 E4            |mov     dword ptr [ebp-1C], eax
0040D784  |> \837D E4 02         |cmp     dword ptr [ebp-1C], 2
0040D788  |.  7D 3A              |jge     short 0040D7C4
0040D78A  |.  C745 E0 00000000   |mov     dword ptr [ebp-20], 0
0040D791  |.  EB 09              |jmp     short 0040D79C
0040D793  |>  8B4D E0            |mov     ecx, dword ptr [ebp-20]
0040D796  |.  83C1 01            |add     ecx, 1
0040D799  |.  894D E0            |mov     dword ptr [ebp-20], ecx
0040D79C  |>  837D E0 03         |cmp     dword ptr [ebp-20], 3
0040D7A0  |.  7D 20              |jge     short 0040D7C2
0040D7A2  |.  8B55 E4            |mov     edx, dword ptr [ebp-1C]
0040D7A5  |.  6BD2 0C            |imul    edx, edx, 0C
0040D7A8  |.  8D4415 E8          |lea     eax, dword ptr [ebp+edx-18]
0040D7AC  |.  8B4D E0            |mov     ecx, dword ptr [ebp-20]
0040D7AF  |.  8B1488             |mov     edx, dword ptr [eax+ecx*4]
0040D7B2  |.  52                 |push    edx                            ; /<%d>
0040D7B3  |.  68 1C204200        |push    0042201C                       ; |format = "%d"
0040D7B8  |.  E8 E338FFFF        |call    printf                         ; \printf
0040D7BD  |.  83C4 08            |add     esp, 8
0040D7C0  |.^ EB D1              |jmp     short 0040D793
0040D7C2  |>^ EB B7              |jmp     short 0040D77B

Switch

基本循环:

int main(int argc, char* argv[])
{
    int temp;
    scanf("%d",&temp);
    switch(temp)
    {
    case 0:
        printf("case 0");break;
    case 1:
        printf("case 1");break;
    case 2:
        printf("case 2");break;
    case 3:
        printf("case 3");break;
    default:
        printf("none");break;
    }
    return 0;
}

jmp dword ptr [edx*4+40FA6B] 跳转的地址.

0040F9F9  |.  8B4D FC       |mov     ecx, dword ptr [ebp-4]
0040F9FC  |.  894D F8       |mov     dword ptr [ebp-8], ecx
0040F9FF  |.  837D F8 03    |cmp     dword ptr [ebp-8], 3
0040FA03  |.  77 46         |ja      short 0040FA4B
0040FA05  |.  8B55 F8       |mov     edx, dword ptr [ebp-8]
0040FA08  |.  FF2495 6BFA40>|jmp     dword ptr [edx*4+40FA6B]
0040FA0F  |>  68 18304200   |push    00423018                         ; /format = "case 0"
0040FA14  |.  E8 8716FFFF   |call    printf                           ; \printf
0040FA19  |.  83C4 04       |add     esp, 4
0040FA1C  |.  EB 3A         |jmp     short 0040FA58
0040FA1E  |>  68 10304200   |push    00423010                         ; /format = "case 1"
0040FA23  |.  E8 7816FFFF   |call    printf                           ; \printf
0040FA28  |.  83C4 04       |add     esp, 4
0040FA2B  |.  EB 2B         |jmp     short 0040FA58
0040FA2D  |>  68 08304200   |push    00423008                         ; /format = "case 2"
0040FA32  |.  E8 6916FFFF   |call    printf                           ; \printf
0040FA37  |.  83C4 04       |add     esp, 4
0040FA3A  |.  EB 1C         |jmp     short 0040FA58
0040FA3C  |>  68 00304200   |push    00423000                         ; /format = "case 3"
0040FA41  |.  E8 5A16FFFF   |call    printf                           ; \printf
0040FA46  |.  83C4 04       |add     esp, 4
0040FA49  |.  EB 0D         |jmp     short 0040FA58
0040FA4B  |>  68 F82F4200   |push    00422FF8                         ; /format = "none"
0040FA50  |.  E8 4B16FFFF   |call    printf                           ; \printf
0040FA55  |.  83C4 04       |add     esp, 4

变量作用域

全局变量:

具有初始值的全局变量,其值在链接时被写入所创建的PE文件中,PE文件首先会加载这些全局变量,所以这些变量可以不受作用域的影响,在程序中的任何位置都可以被访问。

int num1 = 1;
int num2 = 2;

int main(int argc, char* argv[])
{
    scanf("%d",&num1);
    printf("%d",num1);

    num2=10;
    return 0;
}
0040F9E8  |.  68 68514200   push    offset num1
0040F9ED  |.  68 F82F4200   push    00422FF8                         ; /format = "%d"
0040F9F2  |.  E8 79FFFFFF   call    scanf                            ; \scanf
0040F9F7  |.  83C4 08       add     esp, 8
0040F9FA  |.  A1 68514200   mov     eax, dword ptr [num1]
0040F9FF  |.  50            push    eax                              ; /<%d> => 0
0040FA00  |.  68 F82F4200   push    00422FF8                         ; |format = "%d"
0040FA05  |.  E8 9616FFFF   call    printf                           ; \printf
0040FA0A  |.  83C4 08       add     esp, 8
0040FA0D  |.  C705 6C514200>mov     dword ptr [num2], 0A

局部变量:
局部变量的访问是通过栈指针相对间接访问,也就是说局部变量是程序动态创建的,局部变量作用域也仅限于函数内部,且其地址也是一个未知数,编译器无法预先计算。

int main(int argc, char* argv[])
{
    int num1=0;
    int num2=1;

    scanf("%d",&num1);
    printf("%d",num1);

    num2=10;
    return 0;
}
0040F9E8  |.  C745 FC 00000 |mov     dword ptr [ebp-4], 0
0040F9EF  |.  C745 F8 01000>|mov     dword ptr [ebp-8], 1
0040F9F6  |.  8D45 FC       |lea     eax, dword ptr [ebp-4]
0040F9F9  |.  50            |push    eax
0040F9FA  |.  68 F82F4200   |push    00422FF8                         ; /format = "%d"
0040F9FF  |.  E8 6CFFFFFF   |call    scanf                            ; \scanf
0040FA04  |.  83C4 08       |add     esp, 8
0040FA07  |.  8B4D FC       |mov     ecx, dword ptr [ebp-4]
0040FA0A  |.  51            |push    ecx                              ; /<%d>
0040FA0B  |.  68 F82F4200   |push    00422FF8                         ; |format = "%d"
0040FA10  |.  E8 8B16FFFF   |call    printf                           ; \printf
0040FA15  |.  83C4 08       |add     esp, 8
0040FA18  |.  C745 F8 0A000 |mov     dword ptr [ebp-8], 0A

堆变量:

#include 

int main(int argc, char* argv[])
{
    int *pMalloc = (int*) malloc(10);
    printf("%x",pMalloc);
    free(pMalloc);

    char *pChar = new char[10];
    printf("%x",pChar);
    delete [] pChar;
    return 0;
}
00401038  |.  6A 0A         push    0A                               ; /size = A (10.)
0040103A  |.  E8 41350000   call    malloc                           ; \malloc
0040103F  |.  83C4 04       add     esp, 4
00401042  |.  8945 FC       mov     dword ptr [ebp-4], eax
00401045  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
00401048  |.  50            push    eax                              ; /<%x>
00401049  |.  68 1C204200   push    0042201C                         ; |format = "%x"
0040104E  |.  E8 7D000000   call    printf                           ; \printf
00401053  |.  83C4 08       add     esp, 8
00401056  |.  8B4D FC       mov     ecx, dword ptr [ebp-4]
00401059  |.  51            push    ecx                              ; /block
0040105A  |.  E8 A13F0000   call    free                             ; \free

关于函数

无参函数:

void Print()
{
    ::MessageBox(NULL,TEXT("hello lyshark"),TEXT("MSG"),MB_OK);
}

int main(int argc, char* argv[])
{
    Print();
    return 0;
}

上方代码定义无参数传递的函数,观察反汇编代码,函数的开场白与收场白代码,VC6.0如下。

00401020 >  55              push    ebp
00401021    8BEC            mov     ebp, esp
00401023    83EC 40         sub     esp, 40
........    .......         ................
00401058    83C4 40         add     esp, 40
00401062    8BE5            mov     esp, ebp
00401064    5D              pop     ebp
00401065    C3              retn

传递整数: 如下C代码,传递整数参数

int Print(int x,int y)
{
    int sum;
    sum=x+y;
    return (sum);
}

int main(int argc, char* argv[])
{
    int sum;
    sum = Print(10,20);
    printf("%d \n",sum);
    return 0;
}

如下代码片段为,参数调用片段,可以看到在32位汇编中是通过堆栈传递参数的,参数传递是从后向前

00401068    6A 14           push    14            push 20
0040106A    6A 0A           push    0A            push 10
0040106C    E8 9EFFFFFF     call    0040100F

观察 print()函数汇编代码片段,通过ebp指针在堆栈中取出参数,然后进行相加运算,最后将返回值放到eax中。

00401020 >  55              push    ebp
00401021    8BEC            mov     ebp, esp
........
00401038    8B45 08         mov     eax, dword ptr [ebp+8]
0040103B    0345 0C         add     eax, dword ptr [ebp+C]
0040103E    8945 FC         mov     dword ptr [ebp-4], eax
00401041    8B45 FC         mov     eax, dword ptr [ebp-4]
........
00401047    8BE5            mov     esp, ebp
00401049    5D              pop     ebp
0040104A    C3              retn

传递数组:

void Print(int arr[], int size)
{
  int i=0;

  for (i=0; i

观察Print的参数传递,首先传递一个0A也就是10,然后传递数组指针,最后调用Print函数.

004010FE    6A 0A           push    0A
00401100    8D45 D8         lea     eax, dword ptr [ebp-28]
00401103    50              push    eax
00401104    E8 FCFEFFFF     call    00401005
00401038    C745 FC 0000000>mov     dword ptr [ebp-4], 0
0040103F    C745 FC 0000000>mov     dword ptr [ebp-4], 0
00401046    EB 09           jmp     short 00401051
00401048    8B45 FC         mov     eax, dword ptr [ebp-4]
0040104B    83C0 01         add     eax, 1
0040104E    8945 FC         mov     dword ptr [ebp-4], eax
00401051    8B4D FC         mov     ecx, dword ptr [ebp-4]
00401054    3B4D 0C         cmp     ecx, dword ptr [ebp+C]
00401057    7D 19           jge     short 00401072
00401059    8B55 FC         mov     edx, dword ptr [ebp-4]
0040105C    8B45 08         mov     eax, dword ptr [ebp+8]
0040105F    8B0C90          mov     ecx, dword ptr [eax+edx*4]
00401062    51              push    ecx
00401063    68 1C204200     push    0042201C                         ; ASCII "%d \r"
00401068    E8 D3000000     call    printf
0040106D    83C4 08         add     esp, 8
00401070  ^ EB D6           jmp     short 00401048

传递指针:

void Print(char *str)
{
    printf("%s\n",str);
    str = "hello world";
    printf("%s\n",str);
}

int main(int argc, char* argv[])
{
    char *str = "hello lyshark";
    Print(str);
    return 0;
}

函数调用片段。

004010B8    C745 FC C02F4200   mov     dword ptr [ebp-4], 00422FC0      ; ASCII "hello lyshark"
004010BF    8B45 FC            mov     eax, dword ptr [ebp-4]
004010C2    50                 push    eax
004010C3    E8 47FFFFFF        call    0040100F

函数内部调用。

00401038    8B45 08            mov     eax, dword ptr [ebp+8]
0040103B    50                 push    eax
0040103C    68 A42F4200        push    00422FA4                         ; ASCII "%s\n"
00401041    E8 FA000000        call    printf
00401046    83C4 08            add     esp, 8

返回指针:

int GetAddr(int number)
{
    int nAddr;
    nAddr = *(int*)(&number-1);
    return nAddr;
}

int main(int argc, char* argv[])
{
    int address = 0;
    address = GetAddr(100);
    printf("%x\n",address);

    return 0;
}

首先是函数调用代码。

00401078    C745 FC 00000000   mov     dword ptr [ebp-4], 0
0040107F    6A 64              push    64
00401081    E8 7FFFFFFF        call    00401005
00401086    83C4 04            add     esp, 4
00401089    8945 FC            mov     dword ptr [ebp-4], eax

函数内部核心代码,ebp+8 = 64 ,区下面的地址。

00401038    8D45 08            lea     eax, dword ptr [ebp+8]
0040103B    83E8 04            sub     eax, 4
0040103E    8B08               mov     ecx, dword ptr [eax]
00401040    894D FC            mov     dword ptr [ebp-4], ecx
00401043    8B45 FC            mov     eax, dword ptr [ebp-4]

返回结构:

struct tag{
    int x;
    int y;
    char z;
};

tag RetStruct()
{
    tag temp;
    temp.x = 10;
    temp.y = 20;
    temp.z = 'A';
    return temp;
}

int main(int argc, char* argv[])
{
    tag temp;
    temp = RetStruct();

    printf("%d \n",temp.x);
    printf("%d \n",temp.y);
    printf("%d \n",temp.z);
    return 0;
}

观察一下结构的初始化

0040D778    C745 F4 0A000000   mov     dword ptr [ebp-C], 0A
0040D77F    C745 F8 14000000   mov     dword ptr [ebp-8], 14
0040D786    C645 FC 41         mov     byte ptr [ebp-4], 41
0040D78A    8B45 08            mov     eax, dword ptr [ebp+8]
0040D78D    8B4D F4            mov     ecx, dword ptr [ebp-C]
0040D790    8908               mov     dword ptr [eax], ecx
0040D792    8B55 F8            mov     edx, dword ptr [ebp-8]
0040D795    8950 04            mov     dword ptr [eax+4], edx
0040D798    8B4D FC            mov     ecx, dword ptr [ebp-4]
0040D79B    8948 08            mov     dword ptr [eax+8], ecx
0040D79E    8B45 08            mov     eax, dword ptr [ebp+8]
0040D7D4    8B08               mov     ecx, dword ptr [eax]
0040D7D6    894D E8            mov     dword ptr [ebp-18], ecx
0040D7D9    8B50 04            mov     edx, dword ptr [eax+4]
0040D7DC    8955 EC            mov     dword ptr [ebp-14], edx
0040D7DF    8B40 08            mov     eax, dword ptr [eax+8]
0040D7E2    8945 F0            mov     dword ptr [ebp-10], eax
0040D7E5    8B4D E8            mov     ecx, dword ptr [ebp-18]
0040D7E8    894D F4            mov     dword ptr [ebp-C], ecx
0040D7EB    8B55 EC            mov     edx, dword ptr [ebp-14]
0040D7EE    8955 F8            mov     dword ptr [ebp-8], edx
0040D7F1    8B45 F0            mov     eax, dword ptr [ebp-10]
0040D7F4    8945 FC            mov     dword ptr [ebp-4], eax
0040D7F7    8B4D F4            mov     ecx, dword ptr [ebp-C]
0040D7FA    51                 push    ecx
0040D7FB    68 A42F4200        push    00422FA4                         ; ASCII "%d \n"
0040D800    E8 CB38FFFF        call    printf
0040D805    83C4 08            add     esp, 8
0040D808    8B55 F8            mov     edx, dword ptr [ebp-8]
0040D80B    52                 push    edx
0040D80C    68 A42F4200        push    00422FA4                         ; ASCII "%d \n"
0040D811    E8 BA38FFFF        call    printf
0040D816    83C4 08            add     esp, 8

数组与指针

简单的数组: 一维数组的寻址方式。

int main(int argc ,char *argv[])
{
    int array[5] = { 1, 2, 3, 4, 5 };
    int x;
    for (x = 0; x < 5; x++){
        printf("%d \n", array[x]);
    }
    return 0;
}

其反汇编代码如下,其中数组的寻址方式使用了 [ebp+ecx*4-0x14] 比例因子寻址。

00401028  |.  C745 EC 01000>mov     dword ptr [ebp-0x14], 0x1
0040102F  |.  C745 F0 02000>mov     dword ptr [ebp-0x10], 0x2
00401036  |.  C745 F4 03000>mov     dword ptr [ebp-0xC], 0x3
0040103D  |.  C745 F8 04000>mov     dword ptr [ebp-0x8], 0x4
00401044  |.  C745 FC 05000>mov     dword ptr [ebp-0x4], 0x5
0040104B  |.  C745 E8 00000>mov     dword ptr [ebp-0x18], 0x0
00401052  |.  EB 09         jmp     short 0040105D
00401054  |>  8B45 E8       /mov     eax, dword ptr [ebp-0x18]
00401057  |.  83C0 01       |add     eax, 0x1
0040105A  |.  8945 E8       |mov     dword ptr [ebp-0x18], eax
0040105D  |>  837D E8 05     cmp     dword ptr [ebp-0x18], 0x5
00401061  |.  7D 17         |jge     short 0040107A
00401063  |.  8B4D E8       |mov     ecx, dword ptr [ebp-0x18]
00401066  |.  8B548D EC     |mov     edx, dword ptr [ebp+ecx*4-0x14]
0040106A  |.  52            |push    edx                                        ; /<%d>
0040106B  |.  68 1C204200   |push    0042201C                                   ; |format = "%d 
"
00401070  |.  E8 3B000000   |call    printf                                     ; \printf
00401075  |.  83C4 08       |add     esp, 0x8
00401078  |.^ EB DA         \jmp     short 00401054

二维数组:

int main(int argc ,char *argv[])
{
    int array[2][3] ={{1,2,3},{4,5,6}};
    int x =0;
    int *Pointer = &array[0][0];

    printf("Base: %x \n",Pointer);
    printf("%d\n",*(Pointer+2));
    printf("%d\n",*(Pointer+4));
    return 0;
}

如下汇编代码就是二维数组的表现形式,可以看出二维数组的存储方式与一维相同.

00401028    C745 E8 0100000>mov     dword ptr [ebp-18], 1
0040102F    C745 EC 0200000>mov     dword ptr [ebp-14], 2
00401036    C745 F0 0300000>mov     dword ptr [ebp-10], 3
0040103D    C745 F4 0400000>mov     dword ptr [ebp-C], 4
00401044    C745 F8 0500000>mov     dword ptr [ebp-8], 5
0040104B    C745 FC 0600000>mov     dword ptr [ebp-4], 6
00401052    C745 E4 0000000>mov     dword ptr [ebp-1C], 0
00401059    8D45 E8         lea     eax, dword ptr [ebp-18]
0040105C    8945 E0         mov     dword ptr [ebp-20], eax

寻址代码如下,其通过dword ptr [ebp-20]指向第1个元素,通过[edx+8]遍历第3个元素

00401070    8B55 E0         mov     edx, dword ptr [ebp-20] ;指向第1个元素前1个元素
00401073    8B42 08         mov     eax, dword ptr [edx+8]  ;遍历第2个元素
00401076    50              push    eax
00401077    68 1C204200     push    0042201C
0040107C    E8 5F000000     call    printf
00401081    83C4 08         add     esp, 8

稍微修改下代码.

int main(int argc ,char *argv[])
{
    int array[2][3] ={{1,2,3},{4,5,6}};
    int x=0,y=1;

    array[x][y] = 0;
    return 0;
}

在Debug模式下,其公式: 数组首地址 + sizeof(type[一维数组元素]) * x + sizeof(数据类型) * y

0040106E    8B45 E4                 mov     eax, dword ptr [ebp-1C]        ; eax = x 坐标
00401071    6BC0 0C                 imul    eax, eax, 0C                   ; eax = x * 0c 索引数组
00401074    8D4C05 E8               lea     ecx, dword ptr [ebp+eax-18]    ; ecx = y 坐标
00401078    8B55 E0                 mov     edx, dword ptr [ebp-20]        ; edx = 1
0040107B    C70491 00000000         mov     dword ptr [ecx+edx*4], 0 ; 1+1*4=5 4字节中的5,指向第2个元素

上方汇编代码,解释:
1.第1条代码中的EAX是获取到的x的值,此处为C语言中的x=0.
2.第2条代码中的0C: 每个元素占4字节,而每个数组有3个元素,3x4=0C.
3.第3条代码中的ECX: 代表数组的y坐标.
4.第5条代码:ecx + edx * 4相当于数组首地址 + sizeof(int) * y.

三维数组:

int main(int argc, char* argv[])
{
    int Array[2][3][4] = {NULL};
    int x = 0;
    int y = 1;
    int z = 2;

    Array[x][y][z] = 3;
    return 0;
}

针对三维数组 int Array[M][C][H]其下标操作Array[x][y][z]=3数组寻址公式为:
Array + sizeof(type[C][H]) * x + sizeof(type[H])*y + sizeof(type)*z

00401056  |.  8B45 9C       mov     eax, dword ptr [ebp-64]      ; eax=x
00401059  |.  6BC0 30       imul    eax, eax, 30                 ; sizeof(type[C][H])*x
0040105C  |.  8D4C05 A0     lea     ecx, dword ptr [ebp+eax-60]  ; 
00401060  |.  8B55 98       mov     edx, dword ptr [ebp-68]      ; Array[C]
00401063  |.  C1E2 04       shl     edx, 4
00401066  |.  03CA          add     ecx, edx
00401068  |.  8B45 94       mov     eax, dword ptr [ebp-6C]      ; Array[Z]
0040106B  |.  C70481 030000>mov     dword ptr [ecx+eax*4], 3

上方汇编代码,解释:
1.第1条指令中得出eax=x的值.
2.第2条指令eax * 30,相当于求出sizeof(type[C][H]) * x
3.第3条指令求出数组首地址+eax-60也就求出Array[M]位置,并取地址放入ECX
4.第4条指令:[ebp-68]存放Y的值,此处就是求出y的值
5.第5条指令:左移4位,相当于2^4次方也就是16这一步相当于求sizeof(type[H])的值
6.Array[M] + sizeof(type[H])的值求出Array[M][C]的值

存放指针类型数据的数组: 数组中各数据元素都是由相同类型的指针组成,我们就称之为指针数组.

指针数组主要用于管理同种类型的指针,一般用于处理若干个字符串的操作,使用指针数组处理多字符串更加的方便,简洁,高效,需要注意的是,虽然同属于数组但是指针数组,但与常规的数组还有所差别,指针数组中的数据为地址类型,寻址时需要再次进行间接访问才能够获取到真正的数据,这也是他们之间最大的不同.

int main(int argc, char* argv[])
{
    char *pBuffer[3] = {
        "hello ",
        "lyshark ",
        "!\r\n"
    };

    for (int x = 0; x < 3; x++){
        printf(pBuffer[x]);
    }
    return 0;
}

上方的代码经过编译后,我们定位到字符串初始化的位置,发现字符串数组中只向数组的第1个成员赋值字符串的首地址.

00401028  |.  C745 F4 2C204200    mov     dword ptr [ebp-C], 0042202C      ; ASCII "hello "
0040102F  |.  C745 F8 20204200    mov     dword ptr [ebp-8], 00422020      ; ASCII "lyshark "
00401036  |.  C745 FC 1C204200    mov     dword ptr [ebp-4], 0042201C      ; ASCII "!\r\n"

如下片段代码就是打印环节了,由于指针数组本质上也是数组,故寻址方式与我们的数组基本相同.

0040103D  |.  C745 F0 00000000    |mov     dword ptr [ebp-10], 0            ; x=0
00401044  |.  EB 09               |jmp     short 0040104F
00401046  |>  8B45 F0             |mov     eax, dword ptr [ebp-10]          ; 取出x
00401049  |.  83C0 01             |add     eax, 1                           ; 递增1
0040104C  |.  8945 F0             |mov     dword ptr [ebp-10], eax
0040104F  |>  837D F0 03          |cmp     dword ptr [ebp-10], 3            ; 与3作比较
00401053  |.  7D 12               |jge     short 00401067
00401055  |.  8B4D F0             |mov     ecx, dword ptr [ebp-10]          ; 取出下标值
00401058  |.  8B548D F4           |mov     edx, dword ptr [ebp+ecx*4-C]     ; 定位字符串首地址
0040105C  |.  52                  |push    edx                              ; format
0040105D  |.  E8 3E000000         |call    printf                           ; printf
00401062  |.  83C4 04             |add     esp, 4
00401065  |.^ EB DF               |jmp     short 00401046

上方代码定义了1维字符串数组,该数组由3个指针变量组成,故长度为12字节,数组所指向的字符串长度与数组本身没有关系,而2维数组则与之不同,我们接着将上方代码稍加修改:

int main(int argc, char* argv[])
{
    char cArray[2][10] = {
        {"hello"},
        {"lyshark"}
    };

    for(int x=0;x<2;x++)
    {
        printf(cArray[x]);
    }
    return 0;
}

以下反汇编代码可看出,2维字符数组初始化过程中,赋值的不是字符串地址,而是其中的字符数据,据此可以明显的区分它与字符指针数组的区别.

00401057  |.  C745 E8 00000000    |mov     dword ptr [ebp-18], 0
0040105E  |.  EB 09               |jmp     short 00401069
00401060  |>  8B45 E8             |mov     eax, dword ptr [ebp-18]
00401063  |.  83C0 01             |add     eax, 1
00401066  |.  8945 E8             |mov     dword ptr [ebp-18], eax
00401069  |>  837D E8 02          |cmp     dword ptr [ebp-18], 2
0040106D  |.  7D 15               |jge     short 00401084
0040106F  |.  8B4D E8             |mov     ecx, dword ptr [ebp-18]
00401072  |.  6BC9 0A             |imul    ecx, ecx, 0A                    ; 遍历数组每次递增10
00401075  |.  8D540D EC           |lea     edx, dword ptr [ebp+ecx-14]     ; 取出1维数组首地址
00401079  |.  52                  |push    edx                             ; format = "hello"
0040107A  |.  E8 21000000         |call    printf                          ; printf
0040107F  |.  83C4 04             |add     esp, 4
00401082  |.^ EB DC               |jmp     short 00401060

总结:字符指针数组寻址后,得到的是数组成员内容,而2维字符数组寻址后,得到的却是数组中某个1维数组的首地址.

指向数组的指针变量: 当指针变量保存的数据为数组的首地址,且将此地址解释为数组时,此指针变量被称为数组指针.

如下我们定义了指向数组的指针为char (*pArray)[10]=Array,并通过循环遍历指针中的数据.

int main(int argc, char* argv[])
{
    char Array[3][10] = { "hello","lyshark","! \r\n" };
    char (*pArray)[10] = Array;

    for(int x=0;x<3;x++)
    {
        printf(*pArray);
        pArray++;   
    }
    return 0;
}

反汇编代码如下,程序通过lea eax, dword ptr [ebp-20]遍历数组元素,指针[ebp-24]每次递增10,递增10是因为char类型为1个字节,而1维数组为10所以递增为10,如果为整数类型就需要递增4*10

0040D790  |.  8D45 E0             |lea     eax, dword ptr [ebp-20]     ; 取打印元素
0040D793  |.  8945 DC             |mov     dword ptr [ebp-24], eax
0040D796  |.  C745 D8 00000000    |mov     dword ptr [ebp-28], 0       ; 初始化x=0
0040D79D  |.  EB 09               |jmp     short 0040D7A8
0040D79F  |>  8B4D D8             |mov     ecx, dword ptr [ebp-28]
0040D7A2  |.  83C1 01             |add     ecx, 1                      ; x每次递增
0040D7A5  |.  894D D8             |mov     dword ptr [ebp-28], ecx
0040D7A8  |>  837D D8 03          |cmp     dword ptr [ebp-28], 3
0040D7AC  |.  7D 17               |jge     short 0040D7C5
0040D7AE  |.  8B55 DC             |mov     edx, dword ptr [ebp-24]
0040D7B1  |.  52                  |push    edx                         ; format
0040D7B2  |.  E8 E938FFFF         |call    printf                      ; printf
0040D7B7  |.  83C4 04             |add     esp, 4
0040D7BA  |.  8B45 DC             |mov     eax, dword ptr [ebp-24]
0040D7BD  |.  83C0 0A             |add     eax, 0A                     ; 指针递增10
0040D7C0  |.  8945 DC             |mov     dword ptr [ebp-24], eax
0040D7C3  |.^ EB DA               |jmp     short 0040D79F

指向函数的指针: 程序通过CALL指令跳转到函数首地址执行代码,既然是地址那就可以使用指针变量来存储函数的首地址,该指针变量被乘坐函数指针.

void __stdcall Show(){
    printf("hello lyshark\n");
}

int main(int argc, char* argv[])
{
    void (__stdcall *pShow)(void) = Show;
    pShow();
    Show();
    return 0;
}

函数指针的类型由返回值,参数信息,调用约定组成,它决定了函数指针在函数调用过程中参数的传递,返回,以及如何堆栈平衡等,上方的代码中__stdcall就是VC编译器中的默认平栈方式,这里也可以省略.

0040D788  |.  C745 FC 0F104000    mov     dword ptr [ebp-4], 0040100F      ; 入口地址
0040D78F  |.  8B45 FC             mov     eax, dword ptr [ebp-4]
0040D792  |.  8945 FC             mov     dword ptr [ebp-4], eax
0040D795  |.  8BF4                mov     esi, esp
0040D797  |.  FF55 FC             call    dword ptr [ebp-4]                ; 间接调用函数
0040D79A  |.  3BF4                cmp     esi, esp                         ; 检查栈平衡
0040D79C  |.  E8 7F39FFFF         call    _chkesp
0040D7A1  |.  E8 6938FFFF         call    0040100F                         ; 直接调用函数
0040D7A6  |.  33C0                xor     eax, eax

如上代码清单,演示了函数指针的赋值和调用过程,与函数调用最大的区别在于函数是直接调用,而函数指针的调用需要取出指针变量中保存的地址,然后进行call dword ptr [ebp-4]间接的调用,将C代码稍作修改.

int __stdcall Show(int nShow){
    printf("hello %d\n",nShow);
    return nShow;
}

int main(int argc, char* argv[])
{
    int (__stdcall *pShow)(int) = Show;
    
    int Ret = pShow(10);
    printf("Ret = %d \n",Ret);
    return 0;
}

如下反汇编代码,代码中的函数指针调用只是多了参数的传递,返回值的接收,其他地方没有太大变化,都是间接调用函数.

0040D788  |.  C745 FC 14104000    mov     dword ptr [ebp-4], 00401014
0040D78F  |.  8B45 FC             mov     eax, dword ptr [ebp-4]
0040D792  |.  8945 FC             mov     dword ptr [ebp-4], eax
0040D795  |.  8BF4                mov     esi, esp          ; 保存进入函数前的堆栈,用于检查
0040D797  |.  6A 0A               push    0A                               ; 传递参数
0040D799  |.  FF55 FC             call    dword ptr [ebp-4] ; 获取函数指针中的地址,间接调用函数
0040D79C  |.  3BF4                cmp     esi, esp
0040D79E  |.  E8 7D39FFFF         call    _chkesp
0040D7A3  |.  8945 F8             mov     dword ptr [ebp-8], eax  ; 接收函数返回数据
0040D7A6  |.  8B4D F8             mov     ecx, dword ptr [ebp-8]
0040D7A9  |.  51                  push    ecx                              ; <%d>
0040D7AA  |.  68 1C204200         push    0042201C                         ; format = "Re =%d"
0040D7AF  |.  E8 EC38FFFF         call    printf                           ; printf
0040D7B4  |.  83C4 08             add     esp, 8

未完待续............................

结构与联合体

你可能感兴趣的:(C++反汇编: 基础知识(7))