zoukankan      html  css  js  c++  java
  • 逆向知识第十讲,循环在汇编中的表现形式,以及代码还原

    一丶do While在汇编中的表现形式

    1.1高级代码:

    #include "stdafx.h"
    
    int main(int argc, char* argv[])
    {
        int nSum = 0;
        int i = 0;
        do 
        {
            nSum = nSum + i;
        } while (i <=100);
        return 0;
    }

    高级代码很简单,只是一个简单的求1~100的累加

    1.2 Debug版本下的汇编表现形式

    代码定式很简单

    ADDR

      .....do While逻辑代码块

      xxxx 条件

      JXX  Addr

    注意,在 do while中, 汇编代码的语义和高级代码语义是一样的.

    比如我们以前的if   jle的时候(也就是小于等于) 我们的if则会写成  > (jg)也就是反向还原,而循环地址向上增量的条件不用取反

    代码还原:

      do

        int nVar4 = nvar4 + nvar8;

      while(nVar8 <= 100)  注意条件,jle就是jle

    还需要注意的是,地址是低地址,也就是跳转是往上跳转的

    1.3 Release版本下的优化

    高级代码:

    int main(int argc, char* argv[])
    {
    
        int nSum = 0;
        int i = 0;
        do 
        {
            nSum = nSum + i;
            i++;
        } while (i <= argc);
    
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
    
        return argc;
    }

    Release版本下的汇编代码:

     

    代码也是最精简的了.和Debug一样,只不过优化为寄存器使用了.效率更快.

    二丶while 循环在汇编中的表达形式

    2.1高级代码:

    #include "stdafx.h"
    
    int main(int argc, char* argv[])
    {
        int nSum = 0;
        int i = 0;
        while(i <= 100) 
        {
            printf("%d",nSum);
            nSum = nSum + i;
        }
        return argc;
    }

    2.2 Debug版本下的汇编表达形式

     

    请注意,while循环回合if else的汇编代码类似

    但是又有质的不同,在if else中, else语句块,其JMP跳转的地址是往增量地址跳转的,而在while中其跳转的地址是往减量地址跳转的

    汇编代码定式:

    LowAddr

        jxxx  HighAddr

        ....... while语句块代码

        JMP  LowAddr

    HighAddr

    可以看出,while循环有两个跳转

    上下界的分别

    jg  highaddr  找到while循环的下界

    jmp  lowaddr 找到while循环的上界

    注意,这里的定式我并没有写条件,因为条件只要会影响标志位即可,有可能不是cmp,反正能影响标志位的即可.

    代码还原:

      while(nvar8 <= 100)  (语义相反,只有do while的语义按正常还原 jg(高于),相反则是小于等于)

      {

        printf("%d",nvar4);

        nvar4 = nvar4 + nvar8;

      }

    PS: 在第一个跳转之前的所有代码,都作为while循环中的条件

    三丶for循环在汇编中的表达形式

    3.1高级代码:

    int main(int argc, char* argv[])
    {
        int nSum = 0;
        int i = 0;
        for(i = 0; i < 100;i++) 
        {
            printf("%d",nSum);
            nSum = nSum + i;
        }
        return argc;
    }

    3.2Debug下的汇编表现形式

     

     for循环因为有了 步长的概念,所以Debug下的代码可能有点难看懂

    说下代码定式把

      JMP  forCMPaddr  跳转到代码比较

    FOR_STEMAddr

        for_Step    其代码是步长代码 (i++ j++)

    forCMPaddr

      for_cmp     代码比较

      jxxx  forEndAddr  和while循环类似,跳转到结尾,条件不成立则退出,看此跳转则找到for循环的下界

      .....循环体

       JMP FOR_STEMADDR 执行完循环体之后,执行步长代码.

    FOR_ENDADDR

    修改为代码定式模样

     代码还原:

    第一步: JMP FOR_CMP 所以找到for循环的比较代码位置

    第二步: 找到jxx For_end 找到for循环的下界.则当前位置是代码的上界

    第三步: jmp FOR_STEP 找到for循环的步长部分

    for(nVar8 = 0; nVar8 < 100; nVar8++)

    {

      printf("%d",nVar4);

      nVar4 = nVar4 +nVar8;

    }

    还原for的时候,主要是找到 比较部分,代码步长部分.以及循环体部分.

    浅谈Release版本下的循环

    上面版本都是Debug版本下的表达形式,但是Release版本下则会优化

    主要从几方面来讲解

    1.减少跳转的优化方式

    2.常量传播的优化方式

    3.代码外提的优化方式

    4.强度削弱的优化方式

    一丶While在汇编中的Release的优化

    因为dowhile是最优化的方式了,所以没有更好的优化方式了

    1.1 while循环下的减少跳转的优化方式

     首先说下为什么减少跳转.

    我们知道,do while就一个跳转,而while在Debug版本下是两个跳转,for循环在Debug版本下是3个跳转

    那么如果减少了跳转,那么则会大大的增加效率.

    1.1.2高级代码:

    #include "stdafx.h"
    
    int main(int argc, char* argv[])
    {
    
        int nSum = 0;
        int i = 0;
        while(i <= argc) 
        {
            nSum = nSum + i;
            i++;
        } 
    
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
        printf("HelloWorld");
    
        return argc;
    }

    1.1.3 Release版本下的代码

    看到这个汇编代码,我们发现jl的时候,是和if相似的.

    而 jle的时候,地址是减量跳转,则是do while的条件

    那么此时我们可能会还原成 if里面包含do while

    但其实,也是这样还原的.这样是为了减少跳转

    说下为什么减少跳转

    1.首先判断,如果不成立,则不执行循环语句块

    2.当第一个条件成立,则循环语句块,此时我知道你的条件是成立的,所以我只需要变为do while去循环即可.

    这样就减少跳转了,比如我们的while循环20000次,那么跳转就要 *2,那么此时变成if 包含do while的时候

    那么此时跳转就是 200001次,大大的优化了效率

    还原代码:

    if(argc >= 0)
    {
      do
        ecx = ecx + eax;
        eax ++;
      while(eax <= argc )
    }

    识别此类的循环要注意

    1.首先if中的条件和 do while中的条件有相关性

    2.注意如果是dowhile那么其地址跳转是往减量跳转.

    当然如果你喜欢还原为while那么也是可以了

    while (eax <= argc)

    {

      ecx = ecx + eax;

      eax ++;

    }

    第一种还原方式,如果条件有相关性,则还原出的汇编代码是和这个的二进制代码是一摸一样的.2.

     1.2 常量传播下的优化方式

    在常量传播下,则直接变成了do while了.

    看下高级代码:

    int nSum = 0;
        int i = 0;
        while(i < 100)
        {
            nSum = nSum + i;
            i++;
        }

    常量传播后,则i变成了常量.所以直接变成do while即可.

    Release汇编

     

    1.3代码外提优化

    高级代码:

    int nSum = 0;
        int i = 0;
        while(i < argc /7)
        {
            nSum = nSum + i;
            i++;
        }

    其中 argc/7并没有在循环体中使用所以可以单独提取出来.

    int temp = argc / 7;

    while(i < temp)...

    Release版本下的汇编代码:

    上面则是代码外提的情况,此时还原代码也可以还原为 if 包含do while的形式

    PS: 代码外提不支持函数

    比如 

      for(i = 0; i < strlen("hello");i++) ... 其中 strlen是函数,所以不会代码外提

    二丶减少跳转优化(For循环)

    for循环在Debug版本下有三层跳转.那么减少跳转之后,则和上方while一样,也变为if包含 do While了.

    PS: 注意,在常量传播下,所有的循环都变成了do while类型去执行循环了

    PS: 注意,代码外提的情况下,所有循环都变成 if 加 do while的形式,代码放到外面执行了.

    2.1高级代码:

     int nSum = 0;
        int i = 0;
        for (i = 0; i < argc; i++)
        {
            nSum = nSum + i;
            i++;
        }

    Release版本下的汇编

     

    其也变成了if 包含do while循环的形式

    还原代码同上。

    循环中的BreakContinue的区别

    循环中有continue和break

    其中continue是跳过当前循环进行下一次循环.

    break是跳出循环体

    所以我们知道了,break会跳出循环.而continue不会跳出循环.

    一丶观看For循环的Debug版本.Releas版本,观察continuebreak的区别.

    1.1高级代码

     int nSum = 0;
        int i = 0;
        for ( i = 0; i < argc ;i++)
        {
            nSum = nSum + i;
            if(argc == 0)
            {
                break;
            }
            else if(argc == 1)
            {
                continue;
            }
            i++;
        }

    1.2Debug汇编break和Continue的表达形式.

    break执行会跳出循环体,而continue则会跳转到补偿代码执行

    1.3Release版本下的汇编

     

    也可以看出,break会跳出循环,而continue则不会跳出循环

    总结:

    1. do while总结

      Debug版本下

      1.do while有一次跳转,其中跳转的代码是往减量地址跳转(低地址)

      2.还原心得,因为其地址往减量跳转,所以汇编语义与高级语言语义一样,正常代码还原

      Release版本下

      1.常量传播下,直接就是do while了,和Debug版本下一样,一次跳转,还原方式正常跳转

      

     2. While循环总结

      Debug版本下

      1.有两次跳转,代码特别像 if else,但是又有质的不同,其中第一次跳转其地址是往增量跳转,第二次跳转其地址是往减量地址跳转(if else则都是往增量地址跳转)

      2.还原心得,第一次跳转之前的代码都作为while循环中的条件,其条件是反向还原,语义相反.第一次跳转可以找到while的下界,其当前位置则是while的上界.

        Release版本下

      1.常量传播的优化方式下,其代码会变成do while执行

      2.代码外提的情况下,其代码会变成if 包含 do while执行,其中代码的条件外提.注意,函数不可以作为代码外提

      3.还原心得: 如果是 if包含do while的形式,则判断两个条件是否有相关性.如果有相关性则可以还原成while或者自己喜好的 if +do while的形式.

      4.第一次跳转是相反语义,第二次跳转是正常语义.

    3.for循环总结

       Debug版本下

        1.for循环因为有步长的问题,所以多一次跳转. 其中 第一步跳转到 条件位置处,第二此跳转则判断条件是否成立,不成立则退出,此时找到for的下界,当前位置可以当做if的上界.

         条件成立之后代码继续执行,则此时又来了一次跳转,此跳转跳转到步长执行代码

       Release版本下

      1.常量传播方式下 代码变为do while执行

      2.代码外提情况下,代码变成了 if + do while的形式 

    转载于:

    作者:IBinary
    出处:http://www.cnblogs.com/iBinary/

  • 相关阅读:
    对于作用域和闭包的理解
    响应式开发学习(3)——图片优化
    响应式开发(2)
    响应式开发(1)
    数据结构
    进阶题目
    集合
    数组
    内存相关
    线程
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/11219843.html
Copyright © 2011-2022 走看看