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]