ztenv 发表于 2012-2-11 10:34:38

从C/C++到汇编

一、循环语句与发汇编后的机器语言
1、for循环
   下面是一段C语言的代码,我们的目的是来看其反汇编的结果:
   int myfunction(int a,int b)
    {
         int c = a+b;
         int i;
         for(i=0;i<50;i++)
         {
             c = c+i;
         }
         return c;
    }
    这里从for的地方开始反汇编,结果如下:
      for(i=0;i<50;i++)
      00412BC7mov      dword ptr ,0   // i=0; 给循环变量赋初值
      00412BCEjmp         myfunction+39h (412BD9h)// 跳到第一次循环处   
>00412BD0mov      eax,dword ptr
|   00412BD3add         eax,1// i++;修改循环变量
|   00412BD6mov      dword ptr ,eax
|   00412BD9cmp      dword ptr ,32h //比较 i 与50的关系, 检查循环条件
|   00412BDDjge          myfunction+4Ah (412BEAh)// 当 i>=50 [即 !(i<50) ] 时则跳出循环
|   {
|      c = c+i;
|   00412BDFmov         eax,dword ptr // 变量 c
|   00412BE2add         eax,dword ptr    // 变量 i
|   00412BE5mov         dword ptr ,eax// c=c+i;
|   }
<00412BE8jmp         myfunction+30h (412BD0h)// 跳回去修改循环变量
      00412BEAmov         eax,dword ptr
   }
      可以看到for循环主要用这么几条指令来实现:mov进行初始化。jmp跳过循环变量改变代码。cmp实现条件判断,jge根据条件跳转。
用jmp回到循环改变代码进行下一次循环。所以for结构有以下的显著特征:

         mov <循环变量>,<初始值>   ; 给循环变量赋初值
       jmp B       ;跳到第一次循环处
    A: (改动循环变量)       ;修改循环变量。
      …
    B: cmp <循环变量>,<限制变量>;检查循环条件
       jgp跳出循环
       (循环体)
          …               
      jmp A       ;跳回去修改循环变量

2、do循环
再看一下do循环,因为 do循环没有修改循环变量的部分,所以比for循环要简单一些。
      do
         {
                  c = c+i;
            00411A55mov      eax,dword ptr
            00411A58add         eax,dword ptr
            00411A5Bmov         dword ptr ,eax
            } while(c< 100);
            00411A5Ecmp      dword ptr ,64h   
            00411A62jl         myfunction+35h (411A55h)
            return c;

   do循环就是一个简单的条件跳转回去。只有两条指令:
   cmp <循环变量>,<限制变量>
    jl <循环开始点>

3、while循环
      while(c<100){
            00411A55cmp         dword ptr ,64h
            00411A59jge         myfunction+46h (411A66h)
               c = c+i;
            00411A5Bmov         eax,dword ptr
            00411A5Eadd         eax,dword ptr
            00411A61mov         dword ptr ,eax
            }
            00411A64jmp         myfunction+35h (411A55h)
            return c;
    很明显,我们会发现while要更复杂一点。因为while除了开始的时候判断循环条件之外,后面还必须有一条无条件跳转回到循环开始的地方,共用三条指令实现:
             A: cmp <循环变量>,<限制变量>
               jgeB
                ( 循环体)
                …
         jmp A
             B: (循环结束了)

这样,我们对C语言中的循环结构的分析,就基本弄完了!当然,我们用同样的方法也可以分析出C语言在“分支语句”、以及其它的数据结构中的C代码与机器反汇编代码的关系了!



二、分支语句

1、if-else 语句
为了观察其汇编语句,下面是一个简单的if判断结构:
      if(a>0 && a<10)
         {
               printf("a>0");
         }
         else if( a>10 && a<100)
         {
               printf("a>10 && a<100");
         }
         else
         {
               printf("a>10 && a<100");
          }
         
    if 判断都是使用cmp再加上条件跳转指令。对于if( A && B)的情况,一般都是使用否决法。如果A不成立,立刻跳下一个分支。依次,如果 B 不成立,同样跳下一分支。
          cmp 条件
          jle 下一个分支
所以开始部分的反汇编为:
         if(a>0 && a<10)
         00411A66cmp       dword ptr ,0
         00411A6Ajle         411A81h   ; 跳下一个else if的判断点
      00411A6Ccmp       dword ptr ,0Ah
         00411A70jge      411A81h   ; 跳下一个else if的判断点
      {
            printf("a>0");
         00411A72push      offset string "a>0" (4240DCh)
         00411A77call      @ILT+1300(_printf) (411519h)
         00411A7Cadd       esp,4
         }

    else if 的和 else 的特点是,开始都有一条无条件跳转到判断结束处,阻止前面的分支执行结束后,直接进入这个分支。这个分支能执行到的唯一途径只是,前面的判断条件不满足。
   else 则在jmp之后直接执行操作。而else if则开始重复if之后的操作,用cmp比较,然后用条件跳转指令时行跳转。
       else if( a>10 && a<100)
          00411A7Fjmp          411AA9h   ;直接跳到判断块外
       00411A81cmp         dword ptr ,0Ah         ;比较+条件跳转,目标为下一个分支处
       00411A85jle          411A9Ch
          00411A87cmp         dword ptr ,64h
          00411A8Bjge          411A9Ch
          {
                printf("a>10 && a<100");
          00411A8Dpush      offset string "a>10 && a<100" (424288h)
          00411A92call          @ILT+1300(_printf) (411519h)
          00411A97add         esp,4
         }
          else
          00411A9Ajmp      411AA9h   ;这里是else,所以只有简单的一条跳转。
       {
             printf("a>10 && a<100");
          00411A9Cpush      offset string "a>10 && a<100" (424288h)
          00411AA1call          @ILT+1300(_printf) (411519h)
          00411AA6add          esp,4
         }
          return c;

2、switch-case 语句
   switch 的特点是有多个判断。因为 swtich 显然不用判断大于小于,所以都是je(因此,C语言中switch语句不支持float类型的变量),分别跳到每个case处。最后一个是无条件跳转,直接跳到default处。以下的代码:
          switch(a)
         {
            case 0:
               printf("a>0");
            case 1:
            {
               printf("a>10 && a<100");
               break;
            }
         default:
                  printf("a>10 && a<100");
         }
反汇编的switch(a)
          00411A66mov         eax,dword ptr
          00411A69mov         dword ptr ,eax
          00411A6Fcmp         dword ptr ,0// case 0:
          00411A76je            411A83h
          00411A78cmp         dword ptr ,1// case 1:
          00411A7Fje            411A90h
          00411A81jmp         411A9Fh// default:
          {
             …
   显然是比较a 是否是0、1这两个数字。汇编指令先把a移动到这个地址,然后再比较,这是调试版本编译的特点。可能是为了防止直接操作堆栈而导致堆栈破坏?最后一条直接跳转到default处。当然,如果没有default,就会跳到swtich{}之外。
   从这里我们可以发现:switch语句里,完成“比较判断”的指令会与“case”指令的两部分,在汇编中,不是按照C语句逐句翻译的,而是分开为两个指令模块来实现的!
       case 0:
                printf("a>0");
          00411A83push      offset string "a>0"(4240DCh)
          00411A88call          @ILT+1300(_printf) (411519h)
          00411A8Dadd         esp,4
          case 1:
          {
               printf("a>10 && a<100");
          00411A90push      offset string "a>10 && a<100" (424288h)
          00411A95call          @ILT+1300(_printf) (411519h)
          00411A9Aadd         esp,4
            break;
          00411A9Djmp         myfunction+8Ch (411AACh)
          }
          default:
               printf("a>10 && a<100");
          00411A9Fpush      offset string "a>10 && c<100" (424288h)
          00411AA4call          @ILT+1300(_printf) (411519h)
          00411AA9add         esp,4
         }
    至于case 和 default分支中,如果有break,则会增加一个无条件跳转汇编指令。若没有break,则就没有任何循环控制代码。
页: [1]
查看完整版本: 从C/C++到汇编