《天书夜读:从汇编语言到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 ...
随机推荐
- asp.net提高程序性能的技巧(一)
[摘 要] 我只是提供我几个我认为有助于提高写高性能的asp.net应用程序的技巧,本文提到的提高asp.net性能的技巧只是一个起步,更多的信息请参考<Improving ASP.NET Pe ...
- 【转】python数据格式化之pprint
pprint – 美观打印 作用:美观打印数据结构 pprint 包含一个“美观打印机”,用于生成数据结构的一个美观视图.格式化工具会生成数据结构的一些表示,不仅可以由解释器正确地解析,而且便于人类阅 ...
- 【转】 Python调用(运行)外部程序
在Python中可以方便地使用os模块运行其他的脚本或者程序,这样就可以在脚本中直接使用其他脚本,或者程序提供的功能,而不必再次编写实现该功能的代码.为了更好地控制运行的进程,可以使用win32pro ...
- Java面向对象 集合(中)
Java面向对象 集合(中) 知识概要: (1)泛型的体系概念 (2)泛型的特点 (3)自定义泛型类 泛型的体系概念 泛型:JDK1.5版 ...
- 关于Websockets问题:
Websockets是一种与服务器进行全双工,双向通信的信道,它不使用http协议,他有自己的协议即自定义协议,ws协议:它的安全协议为wss协议.这种协议专门为快速传输小数据而设计的.对服务其有一 ...
- asp.net中使用Global.asax文件中添加应用出错代码,写入系统日志文件或数据库
void Application_Error(object sender, EventArgs e) { // 在出现未处理的错误时运行的代码 Exception objErr = Server.Ge ...
- thrift例子:python客户端/java服务端
java服务端的代码请看上文. 1.说明: 这两篇文章其实解决的问题是,当使用python去访问大数据线上集群的时候,遇到两个问题: 1)python-hadoop和python-hive相关包链接不 ...
- java类加载小记
java类只有当创建实体或被调用时才会加载,加载时按 编码顺序 先加载static后加载普通的.static模块和static变量都是同一等级的,谁写前面就先加载谁. 在调用某个静态类的方法时,会按编 ...
- win10 uwp 按下等待按钮
我们经常需要一个按钮,在按下时,后台执行Task,这时不能再次按下按钮. 我们使用自定义控件,首先新建一个类,我把它命名是ProgressButton 一个进度条按钮,也就是我们按下时发生进度条,完成 ...
- 赋值运算符函数__from <剑指Offer>
前段时间忙于项目,难得偷得几日闲,为即将到来的就业季做准备.在面试时,应聘者要注意多和考官交流,只有具备良好的沟通能力,才能充分了解面试官的需求,从而有针对性地选择算法解决问题. 题目来源于<剑 ...