首页
社区
课程
招聘
[分享]从C语言到汇编,从汇编到C语言
发表于: 2009-5-20 17:03 31632

[分享]从C语言到汇编,从汇编到C语言

2009-5-20 17:03
31632

要从事“逆向工程”工作,或对其感兴趣,请必然要接触到汇编语言。然而我们在对计算机语言的了解大都是从高级语言(如C、VB、Dephi)开始的。那当我们阅读由高级语言翻译成的机器语言时肯定会遇到很多障碍。下面是我在看了相关书籍后,做的一些笔记,希望有点参考价值!

一、循环语句与发汇编后的机器语言
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++)
      00412BC7  mov        dword ptr [i],0   // i=0; 给循环变量赋初值
      00412BCE  jmp         myfunction+39h (412BD9h)// 跳到第一次循环处   
  >  00412BD0  mov        eax,dword ptr [i]  
  |   00412BD3  add         eax,1  // i++;修改循环变量
  |   00412BD6  mov        dword ptr [i],eax  
  |   00412BD9  cmp        dword ptr [i],32h //  比较 i 与50的关系, 检查循环条件
  |   00412BDD  jge          myfunction+4Ah (412BEAh)  // 当 i>=50 [即 !(i<50) ] 时则跳出循环
  |   {
  |      c = c+i;
  |   00412BDF  mov         eax,dword ptr [c]  // 变量 c
  |   00412BE2  add         eax,dword ptr [i]   // 变量 i
  |   00412BE5  mov         dword ptr [c],eax  // c=c+i;
  |   }
  <  00412BE8  jmp         myfunction+30h (412BD0h)  // 跳回去修改循环变量
      00412BEA  mov         eax,dword ptr [c]  
     }
      可以看到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;
            00411A55  mov        eax,dword ptr [c]  
            00411A58  add         eax,dword ptr [i]  
            00411A5B  mov         dword ptr [c],eax  
            } while(c< 100);
            00411A5E  cmp        dword ptr [c],64h   
            00411A62  jl           myfunction+35h (411A55h)  

            return c;

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

3、while循环
        while(c<100){
            00411A55  cmp         dword ptr [c],64h
            00411A59  jge         myfunction+46h (411A66h)  

               c = c+i;
            00411A5B  mov         eax,dword ptr [c]  
            00411A5E  add         eax,dword ptr [i]  
            00411A61  mov         dword ptr [c],eax  
              }
            00411A64  jmp         myfunction+35h (411A55h)
            return c;
    很明显,我们会发现while要更复杂一点。因为while除了开始的时候判断循环条件之外,后面还必须有一条无条件跳转回到循环开始的地方,共用三条指令实现:
             A: cmp <循环变量>,<限制变量>
                 jge  B
                ( 循环体)
                …
           jmp A
             B: (循环结束了)

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


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (76)
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
需要老大鉴定下贴子质量,呵
2009-5-20 18:57
0
雪    币: 419
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
@@:
inc eax
cmp dword pr [eax],12345678h
jne @B

这叫什么循环
2009-5-20 19:08
0
雪    币: 306
活跃值: (153)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
VC++的反汇编
2009-5-20 19:29
0
雪    币: 398
活跃值: (59)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
[QUOTE=fixfix;627937]@@:
inc eax
cmp dword pr [eax],12345678h
jne @B

这叫什么循环[/QUOTE]

这是一个空循环,在一些程序中用来进行简单的“延时”;


unsigned i = 0;
while( i++ != 0x12345678 );  // 空循环
2009-5-20 20:15
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习,谢谢!!!!!!!!!!!!!!!1
2009-5-20 20:17
0
雪    币: 419
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我不赞同你的 延时 说法

我用来搜索特征码的

就叫他做 调试后的极端循环吧

你写的 while(i<0x12345678)

也不是我写的代码while写法

.while (dword ptr [eax]!=12345678h)
inc eax
.endw
2009-5-20 20:19
0
雪    币: 398
活跃值: (59)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
为了知识的完整性,这里补充一下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)
         00411A66  cmp       dword ptr [c],0  
         00411A6A  jle         411A81h   ; 跳下一个else if的判断点
      00411A6C  cmp       dword ptr [c],0Ah  
         00411A70  jge        411A81h   ; 跳下一个else if的判断点
      {
            printf("a>0");
         00411A72  push      offset string "a>0" (4240DCh)  
         00411A77  call        @ILT+1300(_printf) (411519h)  
         00411A7C  add       esp,4  
         }

    else if 的和 else 的特点是,开始都有一条无条件跳转到判断结束处,阻止前面的分支执行结束后,直接进入这个分支。这个分支能执行到的唯一途径只是,前面的判断条件不满足。
   else 则在jmp之后直接执行操作。而else if则开始重复if之后的操作,用cmp比较,然后用条件跳转指令时行跳转。
       else if( a>10 && a<100)
          00411A7F  jmp          411AA9h     ;直接跳到判断块外
       00411A81  cmp         dword ptr [c],0Ah         ;比较+条件跳转,目标为下一个分支处
       00411A85  jle          411A9Ch  
          00411A87  cmp         dword ptr [c],64h  
          00411A8B  jge          411A9Ch  
          {
                printf("a>10 && a<100");
          00411A8D  push        offset string "a>10 && a<100" (424288h)  
          00411A92  call          @ILT+1300(_printf) (411519h)  
          00411A97  add         esp,4  
           }
          else  
          00411A9A  jmp        411AA9h   ;这里是else,所以只有简单的一条跳转。
       {
             printf("a>10 && a<100");
          00411A9C  push        offset string "a>10 && a<100" (424288h)  
          00411AA1  call          @ILT+1300(_printf) (411519h)  
          00411AA6  add          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)
          00411A66  mov         eax,dword ptr [a]  
          00411A69  mov         dword ptr [ebp-0E8h],eax  
          00411A6F  cmp         dword ptr [ebp-0E8h],0  // case 0:
          00411A76  je            411A83h  
          00411A78  cmp         dword ptr [ebp-0E8h],1  // case 1:
          00411A7F  je            411A90h  
          00411A81  jmp         411A9Fh  // default:
          {
             …
   显然是比较a 是否是0、1这两个数字。汇编指令先把a移动到[ebp-0E8h]这个地址,然后再比较,这是调试版本编译的特点。可能是为了防止直接操作堆栈而导致堆栈破坏?最后一条直接跳转到default处。当然,如果没有default,就会跳到swtich{}之外。
   从这里我们可以发现:switch语句里,完成“比较判断”的指令会与“case”指令的两部分,在汇编中,不是按照C语句逐句翻译的,而是分开为两个指令模块来实现的!
       case 0:
                printf("a>0");
          00411A83  push        offset string "a>0"  (4240DCh)  
          00411A88  call          @ILT+1300(_printf) (411519h)  
          00411A8D  add         esp,4  
          case 1:
          {
               printf("a>10 && a<100");
          00411A90  push        offset string "a>10 && a<100" (424288h)  
          00411A95  call          @ILT+1300(_printf) (411519h)  
          00411A9A  add         esp,4  
              break;
          00411A9D  jmp         myfunction+8Ch (411AACh)  
          }
          default:
               printf("a>10 && a<100");
          00411A9F  push        offset string "a>10 && c<100" (424288h)  
          00411AA4  call          @ILT+1300(_printf) (411519h)  
          00411AA9  add         esp,4  
           }
    至于case 和 default分支中,如果有break,则会增加一个无条件跳转汇编指令。若没有break,则就没有任何循环控制代码。

   小结:如果在反汇编代码中发现连续多个“比较cmp”和“相等跳转je”就会让人联想到“switch”语句了!
2009-5-20 21:07
0
雪    币: 1333
活跃值: (306)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
学习了 比较容易看懂 (*^__^*) 嘻嘻……
2009-5-20 21:30
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
10
支持一下,不过已经有不少人做过这样的工作了,当然我不是不鼓励这样的文章,而是希望能以不同的视角或者不同的方式表达,方便不同性格的人学习才是重要的
2009-5-24 23:09
0
雪    币: 107
活跃值: (404)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
我来看看笨笨.....

支持一下笨笨的逆向分析教程
2009-5-25 07:50
0
雪    币: 227
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zpf
12
貌似在某本书上看到过
2009-5-25 10:14
0
雪    币: 167
活跃值: (1574)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
13
这篇文章我喜欢 简约而又不简单 希望楼主今后多发几篇这样的分析
2009-5-25 10:55
0
雪    币: 167
活跃值: (1574)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
14
有了这个原理 就可以IDA 的 F5了 O(∩_∩)O~
2009-5-25 10:57
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
很踏实的学习
2009-5-25 14:35
0
雪    币: 416
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
学习了,受教
2009-5-31 14:52
0
雪    币: 86
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
谢谢楼主发布分享

TC
2009-5-31 15:25
0
雪    币: 141
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
@@:
inc eax
cmp dword pr [eax],12345678h
jne @B
很简单啊
for(;i!=0x12345678;i++)
差不多就是这一句
2009-5-31 16:57
0
雪    币: 141
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
while (dword ptr [eax]!=12345678h)
inc eax
.endw
masm的高级语法
2009-5-31 16:58
0
雪    币: 141
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
20
invoke ==  push push..... +call
2009-5-31 17:10
0
雪    币: 141
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
太基础了没啥好夸的 我太菜了
2009-5-31 17:16
0
雪    币: 196
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
不错,我也来学习了啊
2009-5-31 18:50
0
雪    币: 224
活跃值: (70)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
23
汇编语言艺术里面有类似的分析。呵呵
2009-5-31 18:58
0
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
24
人肉F5诞生了。
2009-5-31 22:30
0
雪    币: 82
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
收藏一下今天太晚了,明天再看
2009-5-31 23:43
0
游客
登录 | 注册 方可回帖
返回
//