1) Debug与Release的区别:前者称调试版,后者称发行版。调试版基本不优化,而发行版会经过编译器的极致优化,往往与优化前的高级语言执行流程会大相径庭,但是实现的功能是等价的。

2) 如下for循环语句:

 int MyFunction(int a,int b)
 {
     int c = a + b;
     int i;
      ; i <  ; i ++ )
     {
            c = c + i;
     }
     return c;
 }  

Debug版汇编后代码为:

 int MyFunction(int a, int b)
 {
     //省略现场保护代码
     int c = a + b;
 ]       ;取参数1
 0040D4BB    add eax,dword ptr [ebp+0Ch]     ;取参数2
 ],eax       ;存放局部变量c
     int i;
     for ( i =  ; i < 50 ; i ++ )
 ],         ;局部变量i
 0040D4C8    jmp MyFunction+33h (0040d4d3)   ;跳至第一次循环
 ]       ;改变循环变量
                        ;i自增操作
 ],ecx       ;保存
 ],32h       ;循环条件比较
 0040D4D7    jge MyFunction+44h (0040d4e4)   ;大于等于则跳
     {
         c = c + i;
 ]       ;读变量c到edx
 ]       ;加上i
 ],edx       ;保存
     }
 0040D4E2    jmp MyFunction+2Ah(0040d4ca)    ;继续循环
     return c;
 ]        ;取结果到eax
 }
 //省略现场恢复代码  

大体结构为:

     MOV <循环变量>    <初始值>      ;给循环变量赋初值
     JMP     B                    ;跳至第一次循环
 A:  (改动循环变量)                ;修改循环变量
     …
 B:  CMP <循环变量>    <限制变量>      ;检查循环条件
     JGE     跳出循环
     (循环体)
     …
     JMP     A                   ;跳回去继续修改循环变量  

3) 上述的for循环改为以下do循环:

 ;
 do{
     c = c + i;
 } );  

Debug版本汇编:

 ;
 ],       ;循环变量i初始化
 do{
     c = c + i;
 ]
 ]
 ],ecx       ;相加并保存
 }while( ++i<=  );
 ]  

 ],edx       ;改变循环变量
 ],32h       ;比较循环条件
 0040D4DE    jl  MyFunction+28h (0040d4c8)   ;小于则跳,继续循环  

4) 上述的do循环改为以下while循环:

 ;
 )
 {
      c= c + i;
 }  

Debug版本汇编:

 ;
 ],       ;循环变量i初始化
 while( i++ < )
 ]       ;自增前的循环变量
 ]  

 ],edx       ;循环变量自增并保存
 0040D4D4    cmp ecx,32h                  ;比较(自增前的)
 0040D4D7    jge MyFunction+44h (0040d4e4)   ;不小于则跳转,退出循环
 {
     c = c + i;
 ]
 ]
 ],eax       ;相加并保存
 }
 0040D4E2    jmp MyFunction+28h (0040d4c8)   ;绝对跳转,集训循环  

5) 如下if-else判断分支:

 ,j;
  )
 {
          j= ;
 }
  && i <= )
 {
          j= ;
 }
 else
 {
          j= ;
 }  

Debug版本汇编:

 ,j;
 ],0Ah       ;仅声明的变量不分配内存
 if ( i <=  )
 ],       ;比较
 0040D4C3    jg  MyFunction+2Eh (0040d4ce)   ;大于则跳下一分支
 {
     j = ;
 ],       ;赋值
 }
 else if ( i >  && i <= )
 0040D4CC    jmp MyFunction+4Ah (0040d4ea)   ;结束整个分支
 ],       ;else-if分支开始
 0040D4D2    jle MyFunction+43h (0040d4e3)   ;不大于0则转入else
 ],0Ah       ;判断第二个条件
 0040D4D8    jg  MyFunction+43h (0040d4e3)   ;大于则转入else
 {
     j = ;
 ],       ;赋值
 }
 else
 0040D4E1    jmp MyFunction+4Ah (0040d4ea)   ;结束整个分支
 {
     j = ;
 ],       ;赋值
 }
 0040D4EA    …  

If-else语句使用CMP加绝对跳转指令实现(跳转到下一分支或者整个分支的结束位置),从上可知要排列好比较条件的顺序,以达到最少的比较次数的效果。

6) 如下switch-case判断分支:

 ,j;
 switch( i ){
 :
          j= ;
 :
          j= ;
          break;
 default:
          j= ;
 }  

Debug版本汇编:

 ,j;
 ],0Ah       ;赋初值
 switch( i )
 {
 ]
 0040D4C2    mov dword ptr [ebp-0Ch],eax     ;转移内存单元
        ;与0比较
 0040D4C9    je  MyFunction+33h (0040d4d3)   ;等于则转
        ;与1比较
 0040D4CF    je  MyFunction+3Ah (0040d4da)
 0040D4D1    jmp MyFunction+43h (0040d4e3)   ;绝对跳转到default
 case :
     j = ;
 ],       ;无break转入下一分支
 case :
     j = ;
 ],
 break;
 0040D4E1    jmp MyFunction+4Ah (0040d4ea)   ;break,绝对跳转到结束
 default:
     j = ;
 ],
 }
 0040D4EA    …  

期间对i进行转储是Debug版本的特点,目的并不明确。每一个case对应一个cmp与跳转指令je,最后的default要是没有,则跳转到结束处。Case中有break则跳转到结束,没有则继续往下执行。

7) 如下C语言结构体和数组:

 typedef struct {
     float a;
     char b;
     int c;
 }mystruct;  

 int MyFunction(void)
 {
     unsigned ];
     mystruct* strs = (mystruct *)buf;
     int i;
      ; i <  ; i++)
     {
         strs[i].a= 1.2;
         strs[i].b= 'a';
         strs[i].c= ;
     }
     ;
 }  

Debug版本汇编MyFunction函数如下:

 int MyFunction(void)
 {
     push ebp
     mov  ebp,esp
     sub  esp,1D8h                ;1D8H = 472字节
     push ebx
 0040102A    push esi
 0040102B    push edi
 0040102C    lea edi,[ebp-1D8h]
     mov ecx,76h
     mov eax,0CCCCCCCCh
 0040103C    rep stos dword ptr [edi]        ;标准现场保护
     unsigned char *buf[];
 tystruct *strs =(mystruct *)buf;
 0040103E    lea eax,[ebp-190h]
     mov dword ptr [ebp-194h],eax    ;将ebp – 190h送入ebp – 194h
                                       ;ebp– 190h为缓存区地址首址(char)
                                       ;ebp– 194h为str指针(tystruct *strs)
     int i;
     for (i =  ; i< 5 ; i++)  

     )
     mov ecx,dword ptr [ebp-198h]  

 0040105F    mov dword ptr [ebp-198h],ecx  

 0040106C    jge MyFunction+91h (004010b1)    ;标准for循环结构
     {
         strs[i].a = .;
 0040106E    mov edx,dword ptr [ebp-198h]     ;ebp – 198h为循环变量i
     imul edx,edx,0Ch                ;结构体长度为常量0CH,得偏移
     mov eax,dword ptr [ebp-194h]     ;取str中的指针值
 0040107D    mov dword ptr [eax+edx],3F99999Ah
                                       ;得strs[i].a地址,并赋值
         strs[i].b = 'a';
     mov ecx,dword ptr [ebp-198h]
 0040108A    imul ecx,ecx,0Ch
 0040108D    mov edx,dword ptr [ebp-194h]
     ],61h   ;相对与之上多偏移4个字节
         strs[i].c = ;
     mov eax,dword ptr [ebp-198h]
 0040109E    imul eax,eax,0Ch
 004010A1    mov ecx,dword ptr [ebp-194h]
 ],     ;相对与之上,再偏移4个字节
     }
 )
     return ;
 004010B1    xor eax,eax
 }
 //省略现场恢复  

第一点:系统预分配的临时变量堆栈区可变且足够(暂时不知道怎么计算的);第二点:Struct的大小编译以后作为常数保存,对结构体中的变量的访问(点运算符)汇编后采用的偏移量的形式;第三点:偏移量的大小为结构体定义时各个成员变量于系统字长对齐后大小的叠加(mystruct第二个成员为char类型,理论上来说只占用一个字节,但是与4字节对齐,实际占用4个字节)

8) 如下C语言共用体和枚举类型:

 typedef enum {
          ENUM_1= ,
          ENUM_2= ,
          ENUM_3,
          ENUM_4,
 }myenum;  

 typedef  struct{
          inta;
          intb;
          intc;
 }mystruct;  

 typedef union{
          mystructs;
          myenume[];
 }myunion;  

 int MyFunction(void)
 {
          unsigned ] = {  };
          myunion* uns = (myunion *)buf;
          int i;
           ; i <  ; i++)
          {
                    uns[i].s.a= ;
                    uns[i].s.b= ;
                    uns[i].e[]= ENUM_4;
          }
          return0;
 }  

Debug版本汇编MyFunction函数如下:

 int MyFunction(void)
 {
     //现场保护代码省略
     unsigned charbuf[] = {  };
         ;ebp – 64的1个字节置0
     mov ecx,18h                  ;循环18H(24)次
     xor eax,eax                  ;eax置0
     lea edi,[ebp-63h]             ;从ebp – 63位置开始清
 0040104C    rep stos dword ptr [edi]        ;内存清0(24*4=96字节)
 0040104E    stos word ptr [edi]             ;继续清2个字节
     stos byte ptr [edi]             ;继续清1个字节
                                       ;合计100字节
     myunion *uns =(myunion *)buf;
     lea eax,[ebp-64h]
     mov dword ptr [ebp-68h],eax     ;ebp-68h为myunion *uns指针变量
     int i;
     for (i =  ; i< 5 ; i++)
            ; ebp-6Ch为局部变量i
 )
     mov ecx,dword ptr [ebp-6Ch]  

     mov dword ptr [ebp-6Ch],ecx  

 0040106D    jge MyFunction+83h (004010a3)   ;标准for循环
     {
         uns[i].s.a = ;
 0040106F    mov edx,dword ptr [ebp-6Ch]
     imul edx,edx,0Ch              ;0CH为共用体大小
     mov eax,dword ptr [ebp-68h]
            ;偏移,赋值
         uns[i].s.b = ;
 0040107F    mov ecx,dword ptr [ebp-6Ch]
     imul ecx,ecx,0Ch
     mov edx,dword ptr [ebp-68h]
     ],     ;相对上面多偏移4字节
         uns[i].e[]= ENUM_4;
     mov eax,dword ptr [ebp-6Ch]
     imul eax,eax,0Ch
     mov ecx,dword ptr [ebp-68h]
     ],     ;相对上面再偏移4字节
     }
 )
     return ;
 004010A3    xor eax,eax
 }
 //省略现场恢复代码  

第一:从100字节缓存区的初始化可知,Debug版本的代码没经过优化,很stupid;第二:这两种类型的汇编访问方式同数组与结构体如出一辙,并没有什么不同。

《天书夜读:从汇编语言到windows内核编程》二 C语言的流程与处理的更多相关文章

  1. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...

  2. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  3. 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

    1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...

  4. 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序

    ---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...

  5. 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建

    1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...

  6. 《天书夜读:从汇编语言到windows内核编程》十 线程与事件

    1)驱动中使用到的线程是系统线程,在system进程中.创建线程API函数:PsCreateSystemThread:结束线程(线程内自行调用)API函数:PsTerminateSystemThrea ...

  7. 《天书夜读:从汇编语言到windows内核编程》九 时间与定时器

    1)使用如下自定义函数获取自系统启动后经历的毫秒数:KeQueryTimeIncrement.KeQueryTickCount void MyGetTickCount(PULONG msec) { L ...

  8. 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存

    1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...

  9. 《天书夜读:从汇编语言到windows内核编程》三 练习反汇编C语言程序

    1) Debug版本算法反汇编,现有如下3×3矩阵相乘的程序: #define SIZE 3 int MyFunction(int a[SIZE][SIZE],int b[SIZE][SIZE],in ...

随机推荐

  1. Python自学笔记-lambda函数(来自廖雪峰的官网Python3)

    感觉廖雪峰的官网http://www.liaoxuefeng.com/里面的教程不错,所以学习一下,把需要复习的摘抄一下. 以下内容主要为了自己复习用,详细内容请登录廖雪峰的官网查看. 匿名函数 通过 ...

  2. DataGridView的使用记录

    首先初始化 1 this.CheckView.Columns.Clear(); 2 DataGridViewComboBoxColumn dcomo = new DataGridViewComboBo ...

  3. Jquery滚动到页面底部自动Ajax加载图文列表,类似图片懒加载效果,带加载效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. epoll模型的使用

    1. 创建epoll句柄 int epfd = epoll_create(int size); 该函数生成一个epoll专用的文件描述符.它其实是在内核申请一空间,用来存放你想关注的socket fd ...

  5. web项目jsp出现The superclass javax.servlet.http.HttpServlet was not found on the Java Build Path错误

    原因是Javaweb工程类中没有添加Tomcat运行时相关类导致. 解决方式如下:出错的文件---->>build path---->>config build path--- ...

  6. ABAP 动态内表 动态ALV

    DATA: DY_TABLE TYPE REF TO DATA, DY_WA TYPE REF TO DATA. FIELD-SYMBOLS: <DYN_TABLE> TYPE TABLE ...

  7. wpf GifBitmapDecoder 解析 gif 格式

    在网上有很多图片都是gif,那么如何在 wpf 解析 gif? 本文告诉大家如何使用 GifBitmapDecoder 把gif分开为一张一张,获得他的信息. 如果需要把一个 gif 分开,使用的代码 ...

  8. win10 uwp 隐藏实时可视化

    新的vs有个功能,实时可视化 但是他会挡我们界面,想要隐藏 点击转到实时可视化,就是点击横线看到,接着就可以看到下面的选项 点击在应用程序中显示运行时,就是不选中 很简单就看到,没有那个 本作品采用知 ...

  9. web前端开发初学者必看的学习路线(附思维导图)

    很多同学想学习WEB前端开发,虽然互联网有很多的教程.网站.书籍,可是却又不知从何开始如何选取.看完网友高等游民白乌鸦无私分享的原标题为<写给同事的前端学习路线>这篇文章,相信你会有所收获 ...

  10. C语言位操作

    #include <stdio.h> void print(int); int main(void) { int flag = 0xffe4; print(flag); printf(&q ...