吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6131|回复: 38
收起左侧

[调试逆向] 逆向基础笔记十五 汇编比较三种循环

  [复制链接]
lyl610abc 发表于 2021-3-3 21:03
本帖最后由 lyl610abc 于 2021-3-12 16:32 编辑

继续更新个人的学习笔记,
其它笔记传送门
逆向基础笔记一 进制篇
逆向基础笔记二 数据宽度和逻辑运算
逆向基础笔记三 通用寄存器和内存读写
逆向基础笔记四 堆栈篇
逆向基础笔记五 标志寄存器
逆向基础笔记六 汇编跳转和比较指令
逆向基础笔记七 堆栈图(重点)
逆向基础笔记八 反汇编分析C语言
逆向基础笔记九 C语言内联汇编和调用协定
逆向基础笔记十 汇编寻找C程序入口
逆向基础笔记十一 汇编C语言基本类型
逆向基础笔记十二 汇编 全局和局部 变量
逆向基础笔记十三 汇编C语言类型转换
逆向基础笔记十四 汇编嵌套if else
逆向基础笔记十六 汇编一维数组
逆向基础笔记十七 汇编二维数组 位移 乘法
逆向基础笔记十八 汇编 结构体和内存对齐
逆向基础笔记十九 汇编switch比较if else
逆向基础笔记二十 汇编 指针(一)
逆向基础笔记二十一 汇编 指针(二)
逆向基础笔记二十二 汇编 指针(三)
逆向基础笔记二十三 汇编 指针(四)
逆向基础笔记二十四 汇编 指针(五) 系列完结

汇编比较三种循环

众所周知,在C语言可以使用可以使用三种循环,分别是:while、do...while和for

本笔记从汇编的角度出发,观察这三种循环的差异

范例代码

先贴出三种循环的代码,分别用这三种循环计算

0+1+2+3+4+5+6+7+8+9(从0一直加到9)

#include "stdafx.h"
int loop1(){
        int i=0,j=0;
        for(i=0;i<10;i++){
                j=j+i;
        }
        return j;
}

int loop2(){
        int i=0,j=0;
        while(i<10){
                j=j+i;
                i=i+1;
        }
        return j;
}
int loop3(){
        int i=0,j=0;
        do {
                j=j+i;
                i=i+1;
        } while(i<10);
        return j;
}

int main(int argc, char* argv[])
{
        int result=0;
        result=loop1();
        printf("%d\n",result);
        result=loop2();
        printf("%d\n",result);
        result=loop3();
        printf("%d\n",result);
        return 0;
}

运行结果

image-20210303185910081

显然,这三种循环都能正确地计算出结果,接下来挨个分析这三种循环的汇编代码


for循环

省去汇编代码中保护现场、提升堆栈、初始化堆栈、恢复现场和返回的代码,直接看代码的对应汇编

9:        int i=0,j=0;
0040D748   mov         dword ptr [ebp-4],0
0040D74F   mov         dword ptr [ebp-8],0
10:       for(i=0;i<10;i++){
0040D756   mov         dword ptr [ebp-4],0
0040D75D   jmp         loop1+38h (0040d768)
0040D75F   mov         eax,dword ptr [ebp-4]
0040D762   add         eax,1
0040D765   mov         dword ptr [ebp-4],eax
0040D768   cmp         dword ptr [ebp-4],0Ah
0040D76C   jge         loop1+49h (0040d779)
11:           j=j+i;
0040D76E   mov         ecx,dword ptr [ebp-8]
0040D771   add         ecx,dword ptr [ebp-4]
0040D774   mov         dword ptr [ebp-8],ecx
12:       }
0040D777   jmp         loop1+2Fh (0040d75f)
13:       return j;
0040D779   mov         eax,dword ptr [ebp-8]
14:   }

头两行汇编对应将i和j初始化为0,没什么好说的

9:        int i=0,j=0;
0040D748   mov         dword ptr [ebp-4],0
0040D74F   mov         dword ptr [ebp-8],0
变量 对应地址
i ebp-4
j ebp-8

接下来看循环语句部分:

第一行为:

0040D756   mov         dword ptr [ebp-4],0

对应for(i=0;i<10;i++)中的i=0


接下来是一个绝对跳转指令:

0040D75D   jmp         loop1+38h (0040d768)

跳转的地址为:0040d768

0040D768   cmp         dword ptr [ebp-4],0Ah
0040D76C   jge         loop1+49h (0040d779)

这里就是比较 i 和0Ah(十六进制的10),也就是对应for(i=0;i<10;i++)中的i<10

jge指令:jump greater equal,当大于等于时跳转(有符号)

所以这里就是判断i是否大于10,如果大于10就跳转到0040d779

接下来看0040d779对应的代码:

13:       return j;
0040D779   mov         eax,dword ptr [ebp-8]

这里就是返回的语句了,已经在循环的外部了,即上面的跳转其实就是退出循环的语句


如果前面的i<10,则继续向下看汇编语句:

11:           j=j+i;
0040D76E   mov         ecx,dword ptr [ebp-8]
0040D771   add         ecx,dword ptr [ebp-4]
0040D774   mov         dword ptr [ebp-8],ecx

先将j赋值给ecx,然后用ecx加上i,最后将ecx赋值给j,即j=j+i,也就是我们循环里要执行的内容


执行完上面的j=j+1后,下一行指令是一个绝对跳转:

12:       }
0040D777   jmp         loop1+2Fh (0040d75f)

跳转的地址为0040d75f,继续观察:

0040D75F   mov         eax,dword ptr [ebp-4]
0040D762   add         eax,1
0040D765   mov         dword ptr [ebp-4],eax
0040D768   cmp         dword ptr [ebp-4],0Ah

先将i的值赋给eax,然后eax加一后把eax赋给i,对应for(i=0;i<10;i++)中的i++

然后就回到了之前的步骤,比较i是否大于等于10,是则退出循环,否则继续循环执行


for循环总结

通过前面的分析大致可以知道for循环的执行流程为:

image-20210303205219083

while循环

省去汇编代码中保护现场、提升堆栈、初始化堆栈、恢复现场和返回的代码,直接看代码的对应汇编

17:       int i=0,j=0;
0040D7A8   mov         dword ptr [ebp-4],0
0040D7AF   mov         dword ptr [ebp-8],0
18:       while(i<10){
0040D7B6   cmp         dword ptr [ebp-4],0Ah
0040D7BA   jge         loop2+40h (0040d7d0)
19:           j=j+i;
0040D7BC   mov         eax,dword ptr [ebp-8]
0040D7BF   add         eax,dword ptr [ebp-4]
0040D7C2   mov         dword ptr [ebp-8],eax
20:           i=i+1;
0040D7C5   mov         ecx,dword ptr [ebp-4]
0040D7C8   add         ecx,1
0040D7CB   mov         dword ptr [ebp-4],ecx
21:       }
0040D7CE   jmp         loop2+26h (0040d7b6)
22:       return j;
0040D7D0   mov         eax,dword ptr [ebp-8]
23:   }

头两行初始化i和j,和前面一样,不属于循环的内容,这里给出i和j对应的地址

变量 对应地址
i ebp-4
j ebp-8

下面正式来看循环的内容:

18:       while(i<10){
0040D7B6   cmp         dword ptr [ebp-4],0Ah
0040D7BA   jge         loop2+40h (0040d7d0)

比较 i 和 10

jge:jump greater equal,大于等于就跳转(有符号)

综上两句就是用来判断 i<10 对应while(i<10)中的 判断条件 i<10

如果i>=10,则跳转到0040d7d0,退出循环

22:       return j;
0040D7D0   mov         eax,dword ptr [ebp-8]

这里就是返回的语句了,已经在循环的外部了


如果i<10,也就是没有跳转,接着看下面的语句:

19:           j=j+i;
0040D7BC   mov         eax,dword ptr [ebp-8]
0040D7BF   add         eax,dword ptr [ebp-4]
0040D7C2   mov         dword ptr [ebp-8],eax
20:           i=i+1;
0040D7C5   mov         ecx,dword ptr [ebp-4]
0040D7C8   add         ecx,1
0040D7CB   mov         dword ptr [ebp-4],ecx
21:       }

就是执行我们while里面所写的代码(执行循环内的代码)


接着看下面的语句,是一个无条件跳转,跳转到前面的0040d7b6

0040D7CE   jmp         loop2+26h (0040d7b6)

来看看0040d7b6,就是最开始的判断语句,如果i<10则继续执行,否则跳出循环

0040D7B6   cmp         dword ptr [ebp-4],0Ah
0040D7BA   jge         loop2+40h (0040d7d0)

while循环总结

通过前面的分析大致可以知道while循环的执行流程为:

image-20210303203923715

do while循环

省去汇编代码中保护现场、提升堆栈、初始化堆栈、恢复现场和返回的代码,直接看代码的对应汇编

25:       int i=0,j=0;
0040D888   mov         dword ptr [ebp-4],0
0040D88F   mov         dword ptr [ebp-8],0
26:       do {
27:           j=j+i;
0040D896   mov         eax,dword ptr [ebp-8]
0040D899   add         eax,dword ptr [ebp-4]
0040D89C   mov         dword ptr [ebp-8],eax
28:           i=i+1;
0040D89F   mov         ecx,dword ptr [ebp-4]
0040D8A2   add         ecx,1
0040D8A5   mov         dword ptr [ebp-4],ecx
29:       } while(i<10);
0040D8A8   cmp         dword ptr [ebp-4],0Ah
0040D8AC   jl          loop3+26h (0040d896)
30:       return j;
0040D8AE   mov         eax,dword ptr [ebp-8]
31:   }

头两行初始化i和j,和前面一样,不属于循环的内容,这里给出i和j对应的地址

变量 对应地址
i ebp-4
j ebp-8

下面正式来看循环的内容:

26:       do {
27:           j=j+i;
0040D896   mov         eax,dword ptr [ebp-8]
0040D899   add         eax,dword ptr [ebp-4]
0040D89C   mov         dword ptr [ebp-8],eax
28:           i=i+1;
0040D89F   mov         ecx,dword ptr [ebp-4]
0040D8A2   add         ecx,1
0040D8A5   mov         dword ptr [ebp-4],ecx

直接执行我们do while里面所写的代码(执行循环内的代码)


接着看下面的代码:

29:       } while(i<10);
0040D8A8   cmp         dword ptr [ebp-4],0Ah
0040D8AC   jl          loop3+26h (0040d896)

先是比较 i 和 10

然后 jl :jump less (有符号数),当i<10的时候才跳转

跳转地址为0040d896,也就是前面的代码

26:       do {
27:           j=j+i;
0040D896   mov         eax,dword ptr [ebp-8]

如果没有跳转则执行下面的代码

30:       return j;
0040D8AE   mov         eax,dword ptr [ebp-8]

注意到这里已经是退出循环的状态了,返回j


do while循环总结

通过前面的分析大致可以知道do while循环的执行流程为:

image-20210303204001738

三种循环总结

首先汇总三种循环的流程图:

for循环:

image-20210303205219083

while循环:

image-20210303203923715

do while循环:

image-20210303204001738


比较

从流程图就不难看出,三种循环的复杂程度:

for循环>while循环>do while循环

因此执行效率则是:

do while循环>while循环>for循环


  • for循环是先判断条件是否不满足i<10,也就是是否满足i>=10,不满足条件则跳出循环并返回;满足条件则执行循环内的代码,执行完循环内代码后无条件跳转到计数增加i=i+1再回到判断条件
  • while循环也是先判断条件是否不满足i<10,也就是是否满足i>=10,不满足条件则跳出循环并返回;满足条件则执行循环内的代码,执行完循环内代码后无条件跳转到判断条件处
  • do while循环则是先执行循环内的语句,然后再判断条件,判断条件是否满足i<10,满足条件则跳回去继续执行循环内的代码

免费评分

参与人数 11吾爱币 +11 热心值 +11 收起 理由
junjia215 + 1 + 1 用心讨论,共获提升!
networkld + 1 + 1 我很赞同!
zhan + 1 + 1 我很赞同!
海天一色001 + 1 + 1 谢谢@Thanks!
xiaobai1989 + 1 谢谢@Thanks!
墨石不菲 + 1 + 1 谢谢@Thanks!
IBinary + 2 + 1 更新下除法乘法运算等汇编表现形式
zhczf + 1 + 1 我很赞同!
在线小学生 + 1 + 1 谢谢@Thanks!
朱朱你堕落了 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Yhyhoward + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| lyl610abc 发表于 2021-3-15 23:06
kof888 发表于 2021-3-15 22:47
突然想到一个的问题

在汇编指令里面,赋值的指令到底是种指令最快?

xor r8,r8最快
首先给出3种方法的反汇编及其硬编码
[Asm] 纯文本查看 复制代码
00463700      B8 00000000   mov eax,0x0
00463705      33C0          xor eax,eax
00463707      2BC0          sub eax,eax

可以发现mov指令的长度最长
xor和sub两条指令长度相同
当然指令长度只是执行速度因素的一种
再来比较xor和sub
xor指令为异或指令,直接按位进行异或,使用一个异或门电路即可完成
而对于sub指令:
首先明确计算机中的减法操作都会转化为加上一个负数
所以需要先将eax取相反数
然后再进行加法操作,加法操作对应的电路为加法器,较之 异或门电路复杂
于是xor指令的速度最快
涉及知识点:反汇编、硬编码、数字逻辑

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
kof888 + 1 + 1 热心回复!

查看全部评分

kof888 发表于 2021-3-4 11:44
抬下杠

同等条件循环的情况下,自然是先运算一遍效率快,因为少了一次循环

但是这个得看情况来吧,如果是必须先执行一遍的,自然是do效率快,但是如果要先判断的情况下呢?


免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
lyl610abc + 1 + 1 用心讨论,共获提升!

查看全部评分

Yhyhoward 发表于 2021-3-3 21:15
虫子虫子丶 发表于 2021-3-3 21:25
学习一下
在线小学生 发表于 2021-3-3 22:26
大佬高产呀。厉害厉害。
1ray 发表于 2021-3-3 22:28
好帖,支持支持
marlborogolo 发表于 2021-3-4 02:46
学习一下
xxuusong 发表于 2021-3-4 07:50
很不错,学习了
绫音 发表于 2021-3-4 08:52
非常实用  感谢分享!!!
qwert0312 发表于 2021-3-4 08:59
一个认真码自己文案的人很好!
我看书上一般说DO WHILE基本没有人用了!
mo211683 发表于 2021-3-4 09:12
如此高产吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 06:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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