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. php根据ip段以及子网掩码,判断某ip是否处于某子网下

        为了检测客户端ip是否位于指定的网络里(如防火墙过滤有时候需要用到这个技术),有如下方法: 1.第一种 public function netMatch($client_ip, $server ...

  2. DotNetCore跨平台~配置文件与配置代码如何共存

    回到目录 古人云<一山不容二虎>,而进行dotnet core时代之后,我们可以看到这样的一些官方的DEMO,它将数据连接串和其它配置项都直接硬编码在代码里,即在startup中进行定义, ...

  3. 第九章 MySQL中LIMIT和NOT IN案例

    第九章 MySQL中LIMIT和NOT IN案例 一.案例的项目 1.创建数据库语句: #创建数据库 CREATE DATABASE `schoolDB`; USE `schoolDB`; #创建学生 ...

  4. linux 计划任务(crontab)

    每天写一点,总有一天我这条咸鱼能变得更咸 cron服务是一个linux下 的定时执行工具,可以在无需人工干预的情况下运行作业.频率可以划分为 分钟 小时 天 月 周,格式如下: 1.crontab 服 ...

  5. win10 UWP 标题栏后退

    设置里,标题栏有后退按钮 在win平板,可以有后退键,手机也有 pc可以在标题栏 在OnLaunched //最后 Windows.UI.Core.SystemNavigationManager.Ge ...

  6. Jvm加载jar包的顺序

    使用-XX:+TraceClassPaths或者在服务器上执行jinfo时,都能得到classpath包含的jar包,例如: java.class.path = local/aaa/lib/sprin ...

  7. tp5引入第三方类库

    1.在/public/index.php中添加 define('EXTEND_PATH', '../extend/'); 2./extend/lib 中添加第三方类,类文件的名称和类名一样,命名空间为 ...

  8. LeetCode 190. Reverse Bits (反转位)

    Reverse bits of a given 32 bits unsigned integer. For example, given input 43261596 (represented in ...

  9. 教你用SVG画出一条龙

    先看demo,九十七度 其实使用svg画出这条龙很简单,关键不在于怎么使用svg,而在于你的美术功底,哈哈. 好吧,当然基础是不能忽略的,先看下这条龙的代码: <svg id="lon ...

  10. SrpingDruid数据源加密数据库密码

    前言 在工作中遇到这样一个问题:开发过程中将数据库的账号.密码等信息配置在了一个单独的properties配置文件中(使用明文).但运维人员要求在配置文件中的密码一律不得出现明文. 环境 Spring ...