逆向知识第十讲,循环在汇编中的表现形式,以及代码还原

一丶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/

原文地址:https://www.cnblogs.com/gd-luojialin/p/11219843.html

时间: 2024-08-30 01:32:50

逆向知识第十讲,循环在汇编中的表现形式,以及代码还原的相关文章

逆向知识第六讲,取摸优化的几种方式

逆向知识第六讲,取摸优化的几种方式 除法讲完之后,直接开始讲 % 运算符在汇编中表现形式 首先C的高级代码贴上来. 高级代码: // Tedy.cpp : Defines the entry point for the console application. // #include "stdafx.h" int main(int argc, char* argv[]) { unsigned Number; scanf("%d",&Number); //防止

逆向知识第八讲,if语句在汇编中表达的方式

一丶if else的最简单情况还原(无分支情况) 高级代码: #include "stdafx.h" int main(int argc, char* argv[]) { unsigned int nNumber = 0; scanf("%ud",&nNumber); if(argc == 0) { nNumber = 0; //第一种情况下无分支 } else { nNumber = -1; } return nNumber; } 总共两种情况,我们看下R

64位汇编第二讲——64位汇编中局部变量使用及抬栈方法29171230

一.纯写64位汇编时局部变量处理和参数寄存器保存位置 纯写64位汇编和用VS2013写64位C代码生成的汇编会有一些格式上的区别,VS2013写64位C代码生成的汇编中是没用到栈基址寄存器rbp的,但是纯写汇编时只要申明了参数和使用了@LOCAL定义的局部变量,就会用到rbp.且看如下例子:1)用C写64位程序空函数生成的汇编代码, ;C代码 void FunTest2() { } ;汇编代码 000000013F753290 40 57 push rdi 000000013F753292 5F

逆向知识第十四讲,(C语言完结)结构体在汇编中的表现形式

一丶了解什么是结构体,以及计算结构体成员的对其值以及总大小(类也是这样算) 结构体的特性 1.结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合 2.在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类. 3. 结构体可以被声明为变量.指针或数组等,用以实现较复杂的数据结构.结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问. 高级代

汇编循环代码-----个人觉得汇编中最好玩的代码

assume cs:codesg,ss:tacksg,ds:datasg stacksg segment dw 0,0,0,0,0,0,0,0stacksg ends datasg segment db '1. display 'db '2. brows 'db '3. replace 'db '4. modify 'datasg ends codesg segmentstart:mov ax,datasgmov ds,axmov ax,stacksgmov ss,axmov sp,16mov

逆向知识十三讲,汇编中数组的表现形式,以及还原数组

逆向知识十三讲,汇编中数组的表现形式,以及还原数组 讲解数组之前,要了解数组的特性 1.数据具有连续性 2.数据类型相同 比如: int Ary[3] = {0,1,2}; 我们可以看出,上面定义的数组,数据是连续的,其中每个数据类型大小都是int类型(类型也是一样的) 汇编中识别数组: 1.地址连续 2.带有比例因子寻址   (lea  reg32,[xxx  + 4 *xxxx]) 一丶一维数组在汇编中的表现形式 首先说下数组寻址公式,便于下面讲解 公式: 数组首地址 + sizeof(ty

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值. 在反汇编中,我们常常的会看到各种的函数调用,或者通过逆向的手段,单独的使用这个函数,那么此时,我们就需要认识一下怎么识别函数了. 一丶识别__cdecl 函数(俗称C Call),函数参数,函数返回值 首先写一个C Call的函数 1.返回值 int类型, 参数int 类型 高级代码: int __cdecl MyAdd(int a,int b) { return a + b; } int main(int argc, char* ar

逆向课程第四讲逆向中的优化方式,除法原理,以及除法优化上

一丶为什么要熟悉除法的优化,以及除法原理 是这样的,在计算机中,除法运算对应的汇编指令分为 DIV(无符号除法指令) 以及 IDIV(有符号除法指令). 但是,除法指令的执行周期较长效率很低.所以编译器想进办法的用其它指令去代替除法指令. 比如: DIV 指令是100个周期 计算 2 / 2 那么可能在汇编中的表现形式是这样的 CDQ  符号扩展 DIV EDX,2 好,现在100个周期没有了 减法和加法指令,指令周期是4个那么上面的公式可以演化为 mov eax,2 sub eax,2 就算m

C++反汇编第四讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.

目录: 1.多重继承在内存中的表现形式 多重继承在汇编中的表现形式 2.菱形继承 普通的菱形继承 虚继承 汇编中的表现形式 一丶多重继承在内存中的表现形式 高级代码: class Father1 { public: Father1(){}//空构造 virtual ~Father1(){} //空析构 virtual void Player(){} //玩耍的函数 int m_price;//金钱 }; class Father2 { public: Father2(){} virtual ~F