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

时间:2022-11-06 19:26:35

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&plus;&plus;编写内核程序

    ---恢复内容开始--- 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. table边框单线的实现方法

    1.实现方法一:    <table border="0" cellspacing="1" style="    实现原理:利用table的单元 ...

  2. Color Space

    色域(Color Space),又被称为色彩空间,它代表了一个色彩影像所能表现的色彩具体情况.我们经常用到的色彩空间主要有RGB.CMYK.Lab等,而RGB色彩 空间又有AdobeRGB.Apple ...

  3. 《深入Java虚拟机学习笔记》- 第3章 安全

    3.1为什么需要安全性 Java的安全模型是其多个重要结构特点之一,它使Java成为适于网络环境的技术.因为网络提供了一条攻击连人的计算机的潜在途径,因此安全性是非常重要的.Java安全模型侧重于保护 ...

  4. WPF DataGrid模拟click实现效果

    WPF的DataGrid原生是不支持Click事件的,然而在开发过程中,经常遇到需要实现类似效果的. 举个栗子:表格第一列是一个CheckBox,需要实现功能点击行选中,再点击取消选中. 第一想法是R ...

  5. Python 简单理解多线程

    进程,是一个或多个线程的集合,每个进程在内存中是相对独立的. 线程,是计算机最小的运算单元,每个进程至少要有一个线程,多个线程时,每个线程间之间共享内存. 分别举例常规运行和多线程运行: 0)常规运行 ...

  6. FtpHelper ftp操作类库

    FtpHelper ftp操作类库 using System; using System.Collections.Generic; using System.Linq; using System.Te ...

  7. python之常用模块一&lpar;time、random、os、sys&rpar;

    摘要:时间模块time .随机模块random .os模块.sys模块 一.时间模块 三种格式 时间戳时间:浮点数 单位为秒 时间戳起始时间: 1970.1.1 0:0:0 英国伦敦时间 1970.1 ...

  8. javaweb-Excel导入导出后台代码

    前言: 导入导出后台java代码写好很久了,但是...但是...前台不会写啊. 先把后台代码帖上吧 1.excelToDb package util; /** * 代码解释:此方法将传入一个URL,即 ...

  9. 常用的JVM配置参数

    一.Trace 跟踪参数 在Eclipse中,如何打开GC的监控日志 选择菜单栏Run -> Run Configurations -> Java Application -> 选择 ...

  10. &lbrack;转载&rsqb;Java集合框架的常见面试题

    http://www.jfox.info/40-ge-java-ji-he-lei-mian-shi-ti-he-da-an 整理自上面链接: Java集合框架为Java编程语言的基础,也是Java面 ...