登录  | 立即注册

游客您好!登录后享受更多精彩

查看: 39|回复: 0

X86C++反汇编06.条件分支

[复制链接]

84

主题

-6

回帖

97

积分

网站编辑

积分
97
发表于 前天 14:18 | 显示全部楼层 |阅读模式

单分支

当常量为条件的时候会直接折叠,真条件会转为顺序语句,假条件整个if 全部删除

单条件

int main(int argc, char* argv[])
{
      if(argc %  2== 0)
      {
         printf("偶数\r\n");
     }
      return 0;
}

反汇编代码:
mov     eax, [esp+argc]
and     eax, 80000001h
 jns     short loc_401010
dec     eax
or      eax, 0FFFFFFFEh
inc     eax
 loc_401010:                          
 jnz       short loc_40101F
 push    offset asc_407030 ; "偶数\r\n"
 call    sub_401030
add     esp, 4
loc_40101F:      
xor     eax, eax
retn

定式:
  jxx   IF_END
  .......
 IF_END :
还原要点:  看 jxx 反条件还原,因为 x86 语义(或机器语义) 跟  高级语义 是反着,
机器语义是满足条件则跳走,高级语言语义是满足条件则执行


int main(int argc, char* argv[])
{
   if(argc %  2 >= 100)
   {
      printf("偶数\r\n");
   }
   return 0;
}

反汇编代码:

       mov     eax, [esp+argc]
       and     eax, 80000001h
       jns     short loc_401010
       dec     eax
       or      eax, 0FFFFFFFEh
       inc     eax
loc_401010:                        ; CODE XREF: _main+9↑j
       cmp     eax, 64h ; 'd'
       jl      short loc_401022             ; jl 表示有符号,无符号是 jb 
       push    offset asc_407030 ; "偶数\r\n"
       call    sub_401030                     
       add     esp, 4
           ;没有其他跳转,所以是单分支
loc_401022:                        ; CODE XREF: _main+13↑j
       xor     eax, eax
       retn

分析:
jl 小于,那么还原时应该是  >=

多条件

注意: 高版本如果 多条件 情况下 ,如果 条件相互冲突 可能会 被优化成顺序条件 或者 优化无

int main(int argc, char* argv[])
{
    int n = 0;
    scanf("%d",&n);
   if(argc %  n > 100  &&  argc %  n <= 50 )
   {
      printf("偶数\r\n");
   }
   return 0;
}
低版本反汇编代码:
     push    ecx
     lea     eax, [esp+4+var_4]
     mov     [esp+4+var_4], 0
     push    eax
     push    offset Format   ; "%d"
     call    _scanf
     mov     eax, [esp+0Ch+argc]
     add     esp, 8
     cdq
     idiv    [esp+4+var_4]
     cmp     edx, 64h ; 'd'
     jle     short loc_40103B
     cmp     edx, 32h ; '2'
     jg      short loc_40103B
     push    offset asc_408030 ; "偶数\r\n"
     call    sub_401040
     add     esp, 4
loc_40103B:                       ;iF_END                   
     xor     eax, eax
     pop     ecx
     retn

分析:
定式:
       ...
  jl  iF_END
       ...
   jl  iF_END
        xxxx(代码块)
 iF_END
2个跳转都不满足   xxxx  才会执行, 即  第一个 jl  不满足 且 第二个 也不满足才执行

高版本发汇编代码:
lea     eax, [ebp+n]
mov     [ebp+n], 0
push    eax
push    offset _Format  ; "%d"
call    _scanf_s
分析:  因为    argc %  n > 100  &&  argc %  n <= 50   不可能同时实现,所以高版本会把if优化掉

int main(int argc, char* argv[])
{
    int n = 0;
    scanf("%d",&n);
   if(argc %  n > 100  ||  argc %  n <= 50 )
   {
      printf("偶数\r\n");
   }
   return 0;
}

反汇编代码:
      push    ecx
    lea     eax, [esp+4+var_4]
    mov     [esp+4+var_4], 0
    push    eax
    push    offset Format   ; "%d"
    call    _scanf
    mov     eax, [esp+0Ch+argc]
    add     esp, 8
    cdq
    idiv    [esp+4+var_4]
    cmp     edx, 64h ; 'd'
    jg      short loc_40102E    ; 能进   loc_40102E
    cmp     edx, 32h ; '2'
    jg      short loc_40103B      ;反条件能进   loc_40102E
loc_40102E:                          
    push    offset asc_408030 ; "偶数\r\n"
    call    sub_401040
    add     esp, 4
loc_40103B:                            ;  iF_END 
    xor     eax, eax
    pop     ecx
    retn
分析:  2个都不成立才不执行

双分支

定式:

jxx ELSE_BEGIN

IF_BEGIN:

.....

jmp ELSE_BEGIN ;无这个跳转是单分支,向上跳是循环,向下跳是双或多分支

ELSE_BEGIN:

.....

ELSE_END:

int main(int argc, char* argv[])
{
    int n = 0;
    scanf("%d",&n);
   if(argc % n == 0 )
   {
      printf("偶数\r\n");
   }
   else
   {
     printf("奇数\r\n");
   }
   return 0;
}
debug 版反汇编代码
     push    eax
     push    offset aD       ; "%d"
     call    _scanf
     add     esp, 8
     mov     eax, [ebp+argc]
     cdq
     idiv    [ebp+var_4]
     test    edx, edx
     jnz     short loc_40F9AA
     push    offset Format   ; "偶数\r\n"
     call    _printf
     add     esp, 4
     jmp     short loc_40F9B7     ;产生了 jmp,向下跳
loc_40F9AA:             ;ELSE_BEGIN:                 
     push    offset asc_42201C ; "奇数\r\n"
     call    _printf
     add     esp, 4
loc_40F9B7:             ; ELSE_END:                    
     xor     eax, eax

release反汇编代码:    会有优化(节点优化)
     push    ecx
     lea     eax, [esp+4+var_4]
     mov     [esp+4+var_4], 0
     push    eax
     push    offset Format   ; "%d"
     call    _scanf
     mov     eax, [esp+0Ch+argc]
     add     esp, 8
     cdq
     idiv    [esp+4+var_4]
     test    edx, edx
     jnz     short loc_401039
     push    offset asc_408038 ; "偶数\r\n"
     call    sub_401050
     add     esp, 4
     xor     eax, eax
     pop     ecx
     retn
loc_401039:                          
     push    offset asc_408030 ; "奇数\r\n"
     call    sub_401050
     add     esp, 4
     xor     eax, eax
     pop     ecx
     retn

分析: release 优化是以 速度优化为主, 所以 增加了节点而减少了路径 ,增加了代码体积,但是减少了跳转次数

image.png

但是如果增加节点的代价比较大,那么就不会这么优化,则可能出现另一种优化, 在无法减少路径的情况就会考虑减少节点,即 会把 B1 和 B2 的公共部分外提

image.png

int main(int argc, char* argv[])
{
    int n = 0;
    scanf("%d",&n);
   if(argc % n == 0 )
   {
      printf("偶数\r\n");
   }
   else
   {
     printf("奇数\r\n");
   }

   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   printf("hello world\r\n");
   return 0;
}
反汇编代码;
     push    ecx   ;这里使用了强度削弱的优化,ecx 是不需要保存的,这句起到了  sub  esp , 4 的作用,psush
                                ;指令才1字节,而sub 需要3个字节
     lea     eax, [esp+4+var_4]
     mov     [esp+4+var_4], 0
     push    eax
     push    offset Format   ; "%d"
     call    _scanf
     mov     eax, [esp+0Ch+argc]
     add     esp, 8
     cdq
     idiv    [esp+4+var_4]
     test    edx, edx
     jnz     short loc_40102F
     push    offset asc_408048 ; "偶数\r\n"
     jmp     short loc_401034
loc_40102F:                          
     push    offset asc_408040 ; "奇数\r\n"     ;只有push没法还原代码,因此肯定有外提的公共代码段
loc_401034:                          
     call    sub_4010A0      ;外提的公共部分
     add     esp, 4
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     xor     eax, eax
     add     esp, 24h
     retn
分析: 上式 还原的话可以看到 分支里面只有 push ,在不内联汇编的情况下,push 是无法还原的,所以肯定有外提的公共代码段,人工找到后,把外提的代码分别复制到各个分支里面,再还原

多分支

定式:
jxx   ELSE1_BEGIN
        ... 
       jmp   ELSE_END
ELSE1_BEGIN:
jxx   ELSE2_BEGIN 
         ... 
        jmp   ELSE_END
ELSE2_BEGIN:
jxx   ELSE_BEGIN 
        ... 
       jmp   ELSE_END
       .....
ELSE_BEGIN:
       ....
ELSE_END:
int main(int argc, char* argv[])
{
   int n = 0;
   scanf("%d",&n);
   if(argc % n == 0 )
   {
      printf("1\r\n");
   }
   else  if(argc % n == 2)
   {
     printf("2\r\n");
   }
   else  if(argc % n == 3)
   {
     printf("3\r\n");
   }
   else 
   {
     printf("4\r\n");
   }
   return 0;
}
反汇编代码:
    push    ecx
    lea     eax, [esp+4+var_4]
    mov     [esp+4+var_4], 0
    push    eax
    push    offset Format   ; "%d"
    call    _scanf
    mov     eax, [esp+0Ch+argc]
    add     esp, 8
    cdq
    idiv    [esp+4+var_4]
    test    edx, edx
    jnz     short loc_40102F
    push    offset a1  
    jmp     short loc_40104C
loc_40102F:                        
    cmp     edx, 2
    jnz     short loc_40103B
    push    offset a2   
    jmp     short loc_40104C
loc_40103B:                        
    cmp     edx, 3
    jnz     short loc_401047
    push    offset a3   
    jmp     short loc_40104C
loc_401047:                         
    push    offset a4   
loc_40104C:                                ; ELSE_END:                                             
    call    sub_401070
    add     esp, 4
    xor     eax, eax
    add     esp, 0Ch
    retn
上面这种是防止优化后的代码外提的

int main(int argc, char* argv[])
{
   int n = 0;
   scanf("%d",&n);
   if(argc % n == 0 )
   {
      printf("0");
   }
   else  if(argc % n == 1)
   {
      printf("%d\r\n",argc,"1");
   }
   else  if(argc % n == 2)
   {
     printf("2\r\n");
   }
   else  if(argc % n == 3)
   {
     printf("%d\r\n",argc,"3");
   }
   else 
   {
     printf("4\r\n");
   }
   printf("hello world\r\n");
   printf("hello world\r\n");
   return 0;
}

反汇编代码:

     push    ecx
     lea     eax, [esp+4+var_4]
     mov     [esp+4+var_4], 0
     push    eax
     push    offset Format   ; "%d"
     call    _scanf
     mov     ecx, [esp+0Ch+argc]
     add     esp, 8
     mov     eax, ecx
     cdq
     idiv    [esp+4+var_4]
     test    edx, edx
     jnz     short loc_401031
     push    offset a0       ; "0"
     jmp     short loc_401076
loc_401031:                             ; CODE XREF: _main+28↑j
     cmp     edx, 1
     jnz     short loc_40104B
     push    offset a1       ; "1"
     push    ecx
     push    offset aD       ; "%d\r\n"
     call    sub_4010A0
     add     esp, 0Ch
     jmp     short loc_40107E
loc_40104B:                             ; CODE XREF: _main+34↑j
     cmp     edx, 2
     jnz     short loc_401057
     push    offset a2       ; "2\r\n"
     jmp     short loc_401076
loc_401057:                             ; CODE XREF: _main+4E↑j
     cmp     edx, 3
     jnz     short loc_401071
     push    offset a3       ; "3"
     push    ecx
     push    offset aD       ; "%d\r\n"
     call    sub_4010A0
     add     esp, 0Ch
     jmp     short loc_40107E
loc_401071:                        
     push    offset a4       ; "4\r\n"
loc_401076:                        
     call    sub_4010A0
     add     esp, 4
loc_40107E:                         
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     push    offset aHelloWorld ; "hello world\r\n"
     call    sub_4010A0
     xor     eax, eax
     add     esp, 0Ch
     retn
分析: 上面有的分支有的 跳   loc_40107E      有的跳      loc_401076    ,还原时发现 跳 loc_401076    的,push无法还原,说明有代码外提, ,在 loc_401076    拷贝到 跳转到 该地址的地方,在删除,会发现  loc_40107E      和   loc_401076     重叠了.说明他俩是一样,跳2个地方是等价的,在按 if  elseif   还原了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|断点社区 |网站地图

GMT+8, 2025-5-4 08:26 , Processed in 0.106041 second(s), 23 queries , Yac On.

Powered by XiunoBBS

Copyright © 2001-2025, 断点社区.

快速回复 返回顶部 返回列表