转载 《基于VC平台下C++反汇编与逆向分析研究——No.4》

转载来自 “鱼C工作室” 作者: 小生我怕怕

分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
        数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...

正文部分:
       本节主要从汇编层面来全面解析程序的基本流程控制语句:while,do-while,goto 如下:

#include < stdio.h > 
int main() 
{
    int x = 1,
    y = 0;
    while (x <= 100) 
    {
        y = x + y;
        x++;
    }
    printf("%d", y);
    return 0;
}

上面是一个使用while语句来计算1+2+3...+100的和的程序,使用了一个计数器!

Debug版本:

00401010 >|> \55            push ebp                                 ;  ebp入栈保存
00401011  |.  8BEC          mov ebp,esp                              ;  esp保存到ebp
00401013  |.  83EC 48       sub esp,0x48                             ;  开辟局部变量空间
00401016  |.  53            push ebx                                 ;  ebx入栈保存
00401017  |.  56            push esi                                 ;  esi入栈保存
00401018  |.  57            push edi                                 ;  edi入栈保存
00401019  |.  8D7D B8       lea edi,[local.18]                       ;  设置CC操作起始化地址
0040101C  |.  B9 12000000   mov ecx,0x12                             ;  设置循环次数
00401021  |.  B8 CCCCCCCC   mov eax,0xCCCCCCCC                       ;  赋值eax CC int 3
00401026  |.  F3:AB         rep stos dword ptr es:[edi]              ;  循环复制cc
00401028  |.  C745 FC 01000>mov [local.1],0x1                        ;  赋值局部变量x
0040102F  |.  C745 F8 00000>mov [local.2],0x0                        ;  赋值局部变量y
00401036  |>  837D FC 64    /cmp [local.1],0x64                      ;  测试x是否为100
0040103A  |.  7F 14         |jg short Test3.00401050                 ;  如果为100就跳出while循环
0040103C  |.  8B45 FC       |mov eax,[local.1]                       ;  变量x值放入eax
0040103F  |.  0345 F8       |add eax,[local.2]                       ;  x+y的值放入eax
00401042  |.  8945 F8       |mov [local.2],eax                       ;  eax的值放入变量y
00401045  |.  8B4D FC       |mov ecx,[local.1]                       ;  变量x的值放入ecx
00401048  |.  83C1 01       |add ecx,0x1                             ;  exc自加1
0040104B  |.  894D FC       |mov [local.1],ecx                       ;  ecx赋值到变量x
0040104E  |.^ EB E6         \jmp short Test3.00401036                ;  无条件跳转到while条件判断处
00401050  |>  8B55 F8       mov edx,[local.2]                        ;  最终y的值放入edx
00401053  |.  52            push edx                                 ; /<%d>
00401054  |.  68 1C204200   push Test3.0042201C                      ; |format = "%d"
00401059  |.  E8 32000000   call Test3.printf                        ; \printf
0040105E  |.  83C4 08       add esp,0x8                              ;  恢复esp
00401061  |.  33C0          xor eax,eax                              ;  eax清零
00401063  |.  5F            pop edi                                  ;  恢复edi
00401064  |.  5E            pop esi                                  ;  恢复esi
00401065  |.  5B            pop ebx                                  ;  恢复ebx
00401066  |.  83C4 48       add esp,0x48                             ;  恢复局部变量空间
00401069  |.  3BEC          cmp ebp,esp                              ;  测试堆栈平衡
0040106B  |.  E8 A0000000   call Test3._chkesp                       ;  调用调试信息
00401070  |.  8BE5          mov esp,ebp                              ;  恢复esp
00401072  |.  5D            pop ebp                                  ;  恢复ebp
00401073  \.  C3            retn                                     ;  返回  ret   add esp,4

while循环基本结构:

cmp ;判断while条件是否满足  
jg ;不满足就跳出while循环
... ;while执行体
jmp ;无条件跳转到条件判断处

Release版本:

00401000  /$  B8 01000000   mov eax,1                                ;  eax赋值为1
00401005  |.  33C9          xor ecx,ecx                              ;  清零ecx,计数之用
00401007  |>  03C8          /add ecx,eax                             ;  eax加上ecx放入ecx
00401009  |.  40            |inc eax                                 ;  eax自加1
0040100A  |.  83F8 64       |cmp eax,64                              ;  和100做比较
0040100D  |.^ 7E F8         \jle short Test3.00401007                ;  如果不满足条件就跳回add ecx,eax
0040100F  |.  51            push ecx                                 ;  参数
00401010  |.  68 30704000   push Test3.00407030                      ;  ASCII "%d"
00401015  |.  E8 06000000   call Test3.00401020                      ;  printf函数
0040101A  |.  83C4 08       add esp,8                                ;  恢复esp
0040101D  |.  33C0          xor eax,eax                              ;  eax清零
0040101F  \.  C3            retn                                     ;  返回  ret  add esp,4

while循环基本结构:

自动判断是否满足while条件,如果否就直接跳过while,这里满足
... ;while执行体
cmp ;判断while条件是否满足  
jle ;不满足就跳出while循环

总结:根据反汇编两种不同的编译方式可以看出,编译器已经自动识别出原始条件是否满足首次while条件,如满足则直接执行while内容,不满足就直接跳过while。 

#include < stdio.h > 
int main() 
{
    int x;
    do 
    {
        printf("请输入一个数字");
        scanf("%d", &x);
        printf("你输入的数字是:%d\n", x);
    } while ( x != 0 );
    return 0;
}

上面程序是判断用户的输入数值,不等于0就一直输入,直到等于0就跳出do-while循环

Debug版本:

00401010   |> \55             push ebp                         ;  ebp入栈保存
00401011   |.  8BEC           mov ebp,esp                      ;  esp保存到ebp
00401013   |.  83EC 44        sub esp,44                       ;  开辟局部变量空间
00401016   |.  53             push ebx                         ;  ebx入栈保存
00401017   |.  56             push esi                         ;  esi入栈保存
00401018   |.  57             push edi                         ;  edi入栈保存
00401019   |.  8D7D BC        lea edi,dword ptr ss:[ebp-44]    ;  设置CC操作起始地址
0040101C   |.  B9 11000000    mov ecx,11                       ;  设置循环次数
00401021   |.  B8 CCCCCCCC    mov eax,CCCCCCCC                 ;  赋值 eax CC  int 3
00401026   |.  F3:AB          rep stos dword ptr es:[edi]      ;  循环复制 CC
00401028   |>  68 B82F4200    /push Test3.00422FB8             ; /参数入栈
0040102D   |.  E8 5E000000    |call Test3.00401090             ; \调用printf函数
00401032   |.  83C4 04        |add esp,4                       ;  恢复esp
00401035   |.  8D45 FC        |lea eax,dword ptr ss:[ebp-4]    ;  取变量x的地址到eax
00401038   |.  50             |push eax                        ; /参数入栈
00401039   |.  68 1C204200    |push Test3.0042201C             ; |Arg1 = 0042201C ASCII "%d"
0040103E   |.  E8 BDE80000    |call Test3.0040F900             ; \调用scanf函数
00401043   |.  83C4 08        |add esp,8                       ;  恢复esp
00401046   |.  8B4D FC        |mov ecx,dword ptr ss:[ebp-4]    ;  变量x的值放入ecx
00401049   |.  51             |push ecx                        ; /参数入栈
0040104A   |.  68 A42F4200    |push Test3.00422FA4             ; |参数入栈
0040104F   |.  E8 3C000000    |call Test3.00401090             ; \调用printf函数
00401054   |.  83C4 08        |add esp,8                       ;  恢复esp
00401057   |.  837D FC 00     |cmp dword ptr ss:[ebp-4],0      ;  判断x值是否为0
0040105B   |.^ 75 CB          \jnz short Test3.00401028        ;  不为0即跳到到do循环出执行
0040105D   |.  33C0           xor eax,eax                      ;  eax清零
0040105F   |.  5F             pop edi                          ;  恢复edi
00401060   |.  5E             pop esi                          ;  恢复esi
00401061   |.  5B             pop ebx                          ;  恢复ebx
00401062   |.  83C4 44        add esp,44                       ;  恢复局部变量空间
00401065   |.  3BEC           cmp ebp,esp                      ;  测试堆栈平衡
00401067   |.  E8 A4000000    call Test3.00401110              ;  调用调试信息
0040106C   |.  8BE5           mov esp,ebp                      ;  恢复esp
0040106E   |.  5D             pop ebp                          ;  恢复ebp
0040106F   \.  C3             retn                             ;  返回  ret  add esp,4

do-while基本结构:
.... ;do循环执行体
cmp ;条件判断
jnz ;如果不满足条件就跳出循环

Release版本:

00401000  /$  51            push ecx                                 ;  ecx入栈保存
00401001  |>  68 48804000   /push Test3.00408048                     ;  参数
00401006  |.  E8 4C000000   |call Test3.00401057                     ;  printf函数
0040100B  |.  8D4424 04     |lea eax,dword ptr ss:[esp+4]            ;  取变量x地址到eax
0040100F  |.  50            |push eax                                ;  参数入栈
00401010  |.  68 44804000   |push Test3.00408044                     ;  ASCII "%d"
00401015  |.  E8 26000000   |call Test3.00401040                     ;  scanf函数
0040101A  |.  8B4C24 0C     |mov ecx,dword ptr ss:[esp+C]            ;  变量值赋值到ecx
0040101E  |.  51            |push ecx                                ;  参数
0040101F  |.  68 30804000   |push Test3.00408030                     ;  参数
00401024  |.  E8 2E000000   |call Test3.00401057                     ;  printf函数
00401029  |.  8B4424 14     |mov eax,dword ptr ss:[esp+14]           ;  变量值赋值到eax中
0040102D  |.  83C4 14       |add esp,14                              ;  恢复esp
00401030  |.  85C0          |test eax,eax                            ;  判断eax是否为零
00401032  |.^ 75 CD         \jnz short Test3.00401001                ;  如果不为零,就跳回执行
00401034  |.  33C0          xor eax,eax                              ;  eax清零
00401036  |.  59            pop ecx                                  ;  恢复ecx
00401037  \.  C3            retn                                     ;  返回 ret  add esp,4

do-while基本结构:
.... ;do循环执行体
test ;条件判断
jnz ;如果不满足条件就跳出循环

两种编译模式的汇编判断流程很相似,这也就是do-while和while的区别,不管是否满足条件,都会先执行一次再做判断!

  ___________________________________________________________________________________________________

goto语句:

#include < stdio.h > 
int main() 
{
    int a;
    qq: printf("请输入一个数字:");
    scanf("%d", &a);
    if (a == 0)   
        goto qq;
    return 0;
}

上面程序是判断用户输入,如果值为0就跳回重新输入判断,这里使用了goto语句

Debug版本:

00401010 >|> \55            push ebp                                                  ;  保存ebp
00401011  |.  8BEC          mov ebp,esp                                               ;  保存esp到ebp
00401013  |.  83EC 44       sub esp,44                                                ;  开辟局部变量空间
00401016  |.  53            push ebx                                                  ;  ebx入栈保存
00401017  |.  56            push esi                                                  ;  esi入栈保存
00401018  |.  57            push edi                                                  ;  edi入栈保存
00401019  |.  8D7D BC       lea edi,[local.17]                                        ;  设置CC操作起始地址
0040101C  |.  B9 11000000   mov ecx,11                                                ;  设置循环次数
00401021  |.  B8 CCCCCCCC   mov eax,CCCCCCCC                                          ;  赋值 eax CC  int 3
00401026  |.  F3:AB         rep stos dword ptr es:[edi]                               ;  循环复制 CC
00401028  |>  68 20504200   /push offset Test3.??_C@_0BB@FLPH@?G?k?J?d?H?k?R?$LL?$LI?>;  参数
0040102D  |.  E8 AE000000   |call Test3.printfgvdbgind_blockeressges                  ;  printf函数
00401032  |.  83C4 04       |add esp,4                                                ;  恢复esp
00401035  |.  8D45 FC       |lea eax,[local.1]                                        ;  取变量地址到eax
00401038  |.  50            |push eax                                                 ;  参数
00401039  |.  68 1C504200   |push offset Test3.??_C@_02MECO@?$CFd?$AA@4k?5at?50x?$CF0>;  ASCII "%d"
0040103E  |.  E8 3D000000   |call Test3.scanfalloc_basee_block_pages                  ;  scanf函数
00401043  |.  83C4 08       |add esp,8                                                ;  恢复esp
00401046  |.  837D FC 00    |cmp [local.1],0                                          ;  比较是否为0
0040104A  |.  75 02         |jnz short Test3.0040104E                                 ;  为0就执行if语句内容
0040104C  |.^ EB DA         \jmp short Test3.00401028                                 ;  jmp无条件跳转
0040104E  |>  33C0          xor eax,eax                                               ;  eax清零
00401050  |.  5F            pop edi                                                   ;  恢复edi
00401051  |.  5E            pop esi                                                   ;  恢复esi
00401052  |.  5B            pop ebx                                                   ;  恢复ebx
00401053  |.  83C4 44       add esp,44                                                ;  恢复局部变量空间
00401056  |.  3BEC          cmp ebp,esp                                               ;  测试堆栈平衡
00401058  |.  E8 03010000   call Test3.__chkespleBuffers@4ingsW@4location?5size?3?5?$>;  调用调试信息
0040105D  |.  8BE5          mov esp,ebp                                               ;  恢复esp
0040105F  |.  5D            pop ebp                                                   ;  恢复ebp
00401060  \.  C3            retn                                                      ;  返回  ret  add esp,4

Release版本:

00401000  /$  51            push ecx                                                  ;  ecx入栈保存
00401001  |.  68 34804000   push Test3.00408034                                       ;  参数
00401006  |.  E8 5C000000   call Test3.00401067                                       ;  printf函数
0040100B  |.  8D4424 04     lea eax,dword ptr ss:[esp+4]                              ;  取变量地址到eax
0040100F  |.  50            push eax                                                  ;  参数
00401010  |.  68 30804000   push Test3.00408030                                       ;  ASCII "%d"
00401015  |.  E8 36000000   call Test3.00401050                                       ;  scanf函数
0040101A  |.  8B4424 0C     mov eax,dword ptr ss:[esp+C]                              ;  变量值赋值到eax
0040101E  |.  83C4 0C       add esp,0C                                                ;  恢复esp
00401021  |.  85C0          test eax,eax                                              ;  测试eax是否为0
00401023  |.  75 24         jnz short Test3.00401049                                  ;  不满足就跳出if语句
00401025  |>  68 34804000   /push Test3.00408034                                      ;  参数
0040102A  |.  E8 38000000   |call Test3.00401067                                      ;  printf函数
0040102F  |.  8D4C24 04     |lea ecx,dword ptr ss:[esp+4]                             ;  取变量地址到ecx
00401033  |.  51            |push ecx                                                 ;  参数
00401034  |.  68 30804000   |push Test3.00408030                                      ;  ASCII "%d"
00401039  |.  E8 12000000   |call Test3.00401050                                      ;  scanf函数
0040103E  |.  8B4424 0C     |mov eax,dword ptr ss:[esp+C]                             ;  变量值赋值到eax
00401042  |.  83C4 0C       |add esp,0C                                               ;  恢复esp
00401045  |.  85C0          |test eax,eax                                             ;  测试eax是否为0
00401047  |.^ 74 DC         \je short Test3.00401025                                  ;  满足就跳回执行if语句
00401049  |>  33C0          xor eax,eax                                               ;  eax清零
0040104B  |.  59            pop ecx                                                   ;  恢复ecx
0040104C  \.  C3            retn                                                      ;  返回  ret  add esp,4

goto语句框架:

在debug版本中,goto语句等价鱼jmp指令,但在这个Release版本中却没有goto语句的影子,原因在于由于if语句的原因,这里直接忽略了goto语句,而是把goto语句对应的内容直接拷贝到了if语句下!换种情况,也是jmp指令!

本节主要对程序的流程化控制逆向分析完毕,各位私下还需要多多逆向联系,对日后大有帮助!好了,基础篇到此为止,中级篇主要讲解数组,指针,函数,以及类和对象...

posted @ 2014-11-26 17:48  我是小三  阅读(263)  评论(0)    收藏  举报