《天书夜读:从汇编语言到windows内核编程》二 C语言的流程与处理
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语言的流程与处理的更多相关文章
- 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建
(原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...
- 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作
1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...
- 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求
1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...
- 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序
---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...
- 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建
1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...
- 《天书夜读:从汇编语言到windows内核编程》十 线程与事件
1)驱动中使用到的线程是系统线程,在system进程中.创建线程API函数:PsCreateSystemThread:结束线程(线程内自行调用)API函数:PsTerminateSystemThrea ...
- 《天书夜读:从汇编语言到windows内核编程》九 时间与定时器
1)使用如下自定义函数获取自系统启动后经历的毫秒数:KeQueryTimeIncrement.KeQueryTickCount void MyGetTickCount(PULONG msec) { L ...
- 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存
1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...
- 《天书夜读:从汇编语言到windows内核编程》三 练习反汇编C语言程序
1) Debug版本算法反汇编,现有如下3×3矩阵相乘的程序: #define SIZE 3 int MyFunction(int a[SIZE][SIZE],int b[SIZE][SIZE],in ...
随机推荐
- Django 1.10中文文档-聚合
Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法. 然而,有时候你要获取的值需要根据一组对象聚合后才能得到. 这个主题指南描述了如何使用Django的查询来生成和返 ...
- python数据结构之栈与队列
python数据结构之栈与队列 用list实现堆栈stack 堆栈:后进先出 如何进?用append 如何出?用pop() >>> >>> stack = [3, ...
- C#中 计时器用法
有时候我们会需要计算某段代码运行的时间比如一个sql查询,记录一段代码所花费的时间等等代码如下: System.Diagnostics.Stopwatch watch = new System.Dia ...
- 关于Struts与Ajax整合时的异常处理
关于Struts与Ajax整合时的异常处理问题: 问题还原: 从而当有异常发出时,会将异常信息发送到页面上.如下图所示:这是一个比较经典的过程: 错误提示页面: 由于sendError()方法里 ...
- css3制作网页中常见的小箭头
/* css3三角形(向上 ▲) */ div.arrow-up { width:0px; height:0px; border-left:5px solid transparent; /* 右透明 ...
- Activiti 用户任务关联自定义表单
问题阐述 通常每一个"用户任务"都会对应一个表单,以供用户录入信息.尤其是在"流程定义"拥有多个版本的情形下,明确的指定表单显得极其重要. 一份新版本的&quo ...
- SELECT与SET对变量赋值
SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT. 对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们并没有注意,其实这两 ...
- 当谈到 GitLab CI 的时候,我们该聊些什么(上篇)
"微服务"这个概念近两年非常热,正在慢慢改变 DevOps 的思路.微服务架构把一个庞大的业务系统拆解开来,每一个组件变得更加独立自治.松耦合.但是,同时也伴随着部署单元粒度越来越 ...
- FormView控件下DropDownList是否可以绑定
在网站下,FormView控件下是可以通过绑定DropDownList的SelectedValue属性来绑定字段来显示 举例: 1 <asp:DropDownList ID="cboU ...
- 走进 UITest for Xamarin.Forms
上一篇 走进 Prism for Xamarin.Forms 讲了简单的创建一个项目,然后添加了几个页面来回切换,这篇想先搞下 UITest 官方详细地址:https://developer.xam ...