[3] 以逆向的角度来看循环语句——do、while、for的比较
1. do循环
先执行循环体,后比较判断
#include <stdio.h>
int main(int argc, char* argv[]) {
int sum = 0;
int i = 0;
do {
sum += i;
i++;
}
while(i <= argc);
return sum;
}
汇编标识:
;x86_vs
00401000 push ebp
00401001 mov ebp, esp
00401003 sub esp, 8
00401006 mov dword ptr [ebp-8], 0 ;sum=0
0040100D mov dword ptr [ebp-4], 0 ;i=0
{
00401014 mov eax, [ebp-8] ;do_while语句块代码
00401017 add eax, [ebp-4]
0040101A mov [ebp-8], eax ;sum+=i
0040101D mov ecx, [ebp-4]
00401020 add ecx, 1
00401023 mov [ebp-4], ecx ;i+=1
}00401026 mov edx, [ebp-4]
00401029 cmp edx, [ebp+8]
0040102C jle short loc_401014 ;如果i<=argc,跳转到do_while语句块代码
0040102E mov eax, [ebp-8] ;do_while结束代码块
00401031 mov esp, ebp
00401033 pop ebp
00401034 retn
;x86_gcc
00401510 push ebp
00401511 mov ebp, esp
00401513 and esp, 0FFFFFFF0h
00401516 sub esp, 10h
00401519 call ___main
0040151E mov dword ptr [esp+0Ch], 0 ;sum=0
00401526 mov dword ptr [esp+8], 0 ;i=0
{
0040152E mov eax, [esp+8] ;do_while语句块代码
00401532 add [esp+0Ch], eax ;sum+=i
00401536 add dword ptr [esp+8], 1 ;i+=1
0040153B mov eax, [esp+8]
0040153F cmp eax, [ebp+8]
00401542 jg short loc_401546 ;如果i>argc,跳转到do_while结束代码块
}
00401544 jmp short loc_40152E ;跳转到do_while语句块代码
00401546 mov eax, [esp+0Ch] ;do_while结束代码块
逆向总结:
x86_vs:while语句的比较数是相同的
{
DO_WHILE语句代码块
...
}
执行影响标志位指令
JXX跳转到DO_WHILE语句代码块
x86_gcc:while语句的比较数是相反的
{
DO_WHILE语句代码块
执行影响标志位指令
JXX跳转到DO_WHILE结束代码块
...
}
跳转到DO_WHILE语句代码块
DO_WHILE结束代码块
2. while循环:先比较判断,后执行循环体
#include <stdio.h>
int main(int argc, char* argv[]) {
int sum = 0;
int i = 0;
while(i <= argc) {
sum += i;
i++;
}
return sum;
}
汇编标识:
00401000 push ebp
00401001 mov ebp, esp
00401003 sub esp, 8
00401006 mov dword ptr [ebp-8], 0 ;sum=0
0040100D mov dword ptr [ebp-4], 0 ;i=0
{
00401014 mov eax, [ebp-4] ;while语句代码块
00401017 cmp eax, [ebp+8]
0040101A jg short loc_401030 ;如果i>argc,则跳转到while结束代码块
0040101C mov ecx, [ebp-8]
0040101F add ecx, [ebp-4]
00401022 mov [ebp-8], ecx ;sum+=i
00401025 mov edx, [ebp-4]
00401028 add edx, 1
0040102B mov [ebp-4], edx ;i+=1
}
0040102E jmp short loc_401014 ;跳转到while语句代码块
00401030 mov eax, [ebp-8] ;while结束代码块
注意:
while循环结构中使用了两次跳转指令完成循环,因为多使用了一次跳转指令,所以while循环比do循环效率低一些
while循环结构很可能被优化成do循环结构,被转换后的while结构需要检查是否可以被成功执行一次,通常会被嵌套在if单分支结构中
逆向总结:
{
while语句代码块
执行影响标志位指令
JXX跳转到while结束代码块
...
}
JMP跳转到while语句代码块
while结束代码块
3. for循环:先初始化,再比较判断,最后执行循环体
#include <stdio.h>
int main(int argc, char* argv[]) {
int sum = 0;
for (int i = 0; i <= argc ; i++) {
sum += i;
}
return sum;
}
汇编标识:
00401006 mov dword ptr [ebp-8], 0 ;sum=0
{
0040100D mov dword ptr [ebp-4], 0 ;赋初值语句代码块,i=0
}
00401014 jmp short loc_40101F ;跳转到for语句代码块
{
00401016 mov eax, [ebp-4] ;步长语句代码块
00401019 add eax, 1
0040101C mov [ebp-4], eax ;i+=1
}
{
0040101F mov ecx, [ebp-4] ;for语句代码块
00401022 cmp ecx, [ebp+8]
00401025 jg short loc_401032 ;如果i>argc,则跳转到for结束语句块
00401027 mov edx, [ebp-8]
0040102A add edx, [ebp-4]
0040102D mov [ebp-8], edx ;sum+=i}
00401030 jmp short loc_401016 ;跳转到步长语句代码块
00401032 mov eax, [ebp-8] ;for结束语句块
逆向总结:
赋初值语句代码块
JMP跳转到for语句代码块
{
步长语句代码块
...
}
{
for语句代码块
执行影响标志位的指令
JXX跳转到for结束语句块
...
}
JMP跳转到步长语句代码块
for结束语句块
计数器变量被赋初值后,利用jmp跳过第一次步长计算。然后,通过3个跳转指令还原for循环的各个组成部分:
1)第一个jmp指令之前的代码为初始化部分;
2)从第一个jmp指令到循环条件比较处之间的代码为步长计算部分;
3)在条件跳转指令jxx之后寻找向上跳转的jmp指令,且其目标是到步长计算的位置,在jxx和这个jmp指令之间的代码即为循环语句块
4. 三种循环结构的效率比较
Release优化
1)do循环效率最高
2)while循环执行两次JMP跳转,效率低于do循环,可将其优化为if嵌套的do循环提高效率
int LoopWhile(int count){
int sum = 0;
int i = 0; if(count >= 0){
do {
sum += i;
i++;
}
while(i <= count)
}
return sum;
}
3)for循环执行三次JMP跳转,效率最低,可将其优化为if嵌套的do循环提高效率
标签:语句,do,逆向,代码,mov,while,ebp,跳转 From: https://www.cnblogs.com/XiuzhuKirakira/p/17087897.html