大理寺少卿 发表于 昨天 14:18

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

## 单分支

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

#### 单条件

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

反汇编代码:
mov   eax,
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,
           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,
       mov   , 0
       push    eax
       push    offset Format   ; "%d"
       call    _scanf
       mov   eax,
       add   esp, 8
       cdq
       idiv   
       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

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

高版本发汇编代码:
lea   eax,
mov   , 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,
        mov   , 0
        push    eax
        push    offset Format   ; "%d"
        call    _scanf
        mov   eax,
        add   esp, 8
        cdq
        idiv   
        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个都不成立才不执行


```

## 双分支

定式:

**jxxELSE_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,
       cdq
       idiv   
       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,
       mov   , 0
       push    eax
       push    offset Format   ; "%d"
       call    _scanf
       mov   eax,
       add   esp, 8
       cdq
       idiv   
       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 优化是以 速度优化为主, 所以 增加了节点而减少了路径 ,增加了代码体积,但是减少了跳转次数
```

!(./notesimg/1657611399521-fd7c1caa-c461-4079-be10-1f497956a212.png)

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

!(./notesimg/1657611556304-95a6f531-3b2d-4163-b83f-b823c1f639f1.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 是不需要保存的,这句起到了subesp , 4 的作用,psush
                              ;指令才1字节,而sub 需要3个字节
       lea   eax,
       mov   , 0
       push    eax
       push    offset Format   ; "%d"
       call    _scanf
       mov   eax,
       add   esp, 8
       cdq
       idiv   
       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");
   }
   elseif(argc % n == 2)
   {
   printf("2\r\n");
   }
   elseif(argc % n == 3)
   {
   printf("3\r\n");
   }
   else
   {
   printf("4\r\n");
   }
   return 0;
}
反汇编代码:
        push    ecx
        lea   eax,
        mov   , 0
        push    eax
        push    offset Format   ; "%d"
        call    _scanf
        mov   eax,
        add   esp, 8
        cdq
        idiv   
        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");
   }
   elseif(argc % n == 1)
   {
      printf("%d\r\n",argc,"1");
   }
   elseif(argc % n == 2)
   {
   printf("2\r\n");
   }
   elseif(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,
       mov   , 0
       push    eax
       push    offset Format   ; "%d"
       call    _scanf
       mov   ecx,
       add   esp, 8
       mov   eax, ecx
       cdq
       idiv   
       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个地方是等价的,在按 ifelseif   还原了
```
页: [1]
查看完整版本: X86C++反汇编06.条件分支