《Windows内核编程》---语言选择的注意点

调用约定:

调用约定指的是函数被调用时,会按照不同规则,翻译成不同的汇编代码。当一个函数被调用时,首先会将返回地址压入堆栈,紧接着会将函数的参数依次压入堆栈。不同的调用约定,会指明不同的参数入栈顺序,还会指明不同的清理堆栈的方法。用C语言或者C++语言编译器编译程序时,会有四种不同的调用约定去编译函数:

C语言的调用约定,函数由__cdecl修饰;

标准调用约定,函数由__stdcall修饰;

快速调用约定,函数由__fastcall修饰;

C++类成员函数的调用约定,函数由thiscall修饰。

不同的调用约定编译后,会产生不同的汇编代码。下面只介绍C语言调用约定和标准调用约定。

 

C语言调用约定要求在声明函数时用__cdecl对函数进行修饰:

void __cdecl ASCEFunc(int a, int b);

C语言调用会在目标文件中产生一个符号来代表这个函数,此符号形式是:下划线+函数名,且函数体以ret形式返回。即从右至左将参数压入堆栈,执行结束时,以ret返回。此时的堆栈和调用前的堆栈不一致,需要“调用者”恢复堆栈,用add指令将堆栈恢复平衡。

 

标准调用约定要求在声明函数时用__stdcall对函数进行修饰:

void __stdcall ASCEFunc(int a, int b);

C语言调用会在目标文件中产生一个符号来代表该函数,此符号形式为:下划线+函数名+X,其中X代表清理堆栈时需要的数字,函数以ret X形式返回。即从右至左将参数压入堆栈,当函数调用完,函数以ret 8返回函数。ASCEFunc函数负责恢复堆栈,而“调用者”不负责恢复堆栈,这个是C语言调用和标准调用最重要的区别之一。

 

一般程序中很少见到用关键字指定函数的调用约定的,因为编译器会选择默认的调用约定进行编译,在VC编译器中,默认使用C语言的调用约定。而在Windows驱动的编写中,需要使用标准调用约定,尤其是入口函数。系统会寻找_DriverEntry@8作为驱动程序的入口点。如果用C语言调用约定,会将DriverEntry编译成_DriverEntry,而不是_DriverEntry@8,那么就会导致链接错误。

 

函数的导出名:

同样一个函数,用C语言编译器和C++语言编译器编译出来的符号名是不同的。在链接时,链接器不知道源程序的函数名,而只会去目标文件中寻找相应的函数符号表。VC或者DDK提供的编译器c1.exe既可以编译C语言,也可以编译C++语言。默认情况下,编译器会根据源文件的扩展名来判断使用哪种方式。

 

同样使用标准调用约定编译函数:

void __stdcall ASCEFunc(int a, int b);

C++编译器中会编译成符号?ASCEFunc@@YGXHH@Z而在C编译器中会编译成符号_ASCEFunc@8。C++复杂的函数符号名是为了支持函数的重载功能。

 

Windows驱动程序的入口函数规定为_DriverEntry@8,因此用C++编译器时,会导致符号链接错误。解决办法是采用extern C”修饰符:

#pragma INITCODE

extern "C" NTSTATUS DriverEntry(

                                               IN PDRIVER_OBJECT pDriverObject,

                                               IN PUNICODE_STRING pRegistryPath);

注意:编译是将源文件变成目标文件的过程,链接是将目标文件变成最终二进制映像的过程。

 

运行时函数调用:

Windows驱动程序中,不能使用编译器运行时函数,甚至C语言中的malloc函数和C++语言中的默认的new操作符都不能使用(如果用new,必须重载它)。

编译器厂商一般在发布编译器的同时,会同时将其运行时函数一起发布给用户。运行时函数是一个程序运行时所比不可少的函数。它由编译器提供,针对不同的操作系统实现也有所不同,但接口基本上是标准的。例如malloc就是典型的运行时函数。VC编译器中大部分运行时函数是通过Win32 API实现的,而API是针对Ring3层的程序的,Windows驱动运行在Ring0层。内核模式下的程序是无法调用用户模式提供的API函数的。例如new操作符最终调用的是HeapAlloc函数。

Windows为用户提供了内核态的运行时函数,它可以替代应用程序的运行时函数。在内核态的运行时函数一般形如RtlXXXX。当然,也有一般运行时函数,如strcpy等,它们的实现不依赖于Win32 API,因此,可以直接用作Windows驱动的编写。如果必须在驱动中用到new操作符,必须重载new操作符,当然一定也得重载delete操作符啦!

注意:在驱动开发中,尽量完全使用DDK提供的运行时函数。

 

 

 

 

编写Windows内核程序,就意味着这个程序可以执行任意指令,可以访问计算机所有的软件、硬件资源。因此,稍有不慎就有可能将系统变得不稳定。Windows的设计者设计了各种驱动模型或者框架,如NT式内核驱动模型、WDM框架和新推出的WDF框架。在这些模型框架下编程,就使内核编程变得简单,同样也降低了内核程序崩溃的机会。其实,Windows驱动程序员和黑客都在写内核程序,唯一不同的是驱动程序员按照微软设计的模型写程序,而黑客可以不按照这些框架写。Windows设计的这些框架,可以将操作系统的原理隐藏起来,只暴露一些接口,驱动程序员只要把这些接口写好就可以了。从这个角度看,驱动开发并不难,尤其是读完本书后,更会觉得不难了。但是想完成一些特殊的功能,如内核级隐藏进程等,Windows的这些框架就没什么用处了,程序员就需要对Windows内核有全面的了解,通过直接修改Windows内核来实现这些目的。往往黑客对这种技术乐此不疲,通过修改Windows内核,你会发现你的程序几乎无所不能。   编写内核程序是一件很痛苦的事情,回想起这些年学习内核程序开发的经历,真是感慨万千。就如同谭文所说:编写内核程序的人从某种程度讲是孤独的。当一个经验并不丰富的小程序员面对庞大复杂的并且不开源的Windows框架时,那是一种怎样的无助感啊!谭文是我比较钦佩的程序员之一,他对技术非常执着,并且精力充沛。内核程序的知识涉及面非常广,不同类别的内核程序差别也特别大,他几乎都有所涉猎。相信读者在读完这本书后,能对Windows内核开发有比较详细的了解,同时也能结合书中的实例写出很优秀的内核程序了 本书从Windows内核编程出发,全面系统地介绍了串口、键盘、磁盘、文件系统、网络等相关的Windows内核模块的编程技术,以及基于这些技术实现的输入密码保护、防毒引擎、文件加密、网络嗅探、网络防火墙等信息安全软件的核心组件的具体编程。主要知识重包括:Windows串口与键盘过滤驱动、Windows虚拟存储设备与存储设备过滤驱动、Windows文件系统过滤驱动、文件系统透明加密/解密驱动、Windows各类网络驱动(包括TDI过滤驱动及3类NDIS驱动),以及最新的WDF驱动开发模型。有助于读者熟悉Windows内核驱动的体系结构,并精通信息安全类的内核编程技术。本书的大部分代码具有广泛的兼容性,适合从Windows 2000一直到目前最新的Windows 7 Beta版。  本书则基本上介绍的是正统的内核编程技术,是微软在内核编程中给信息安全软件开发者提供的相关接口的大集合,是名门正派的技术,不沾邪气。一个好的内核程序员,“正邪兼修”是有必要的。   本书既适合于有志于成为软件程序员的学生使用,也适合于希望加强自己的技术实力的Windows程序员阅读,同时更适合于从事信息安全行业的Windows软件的开发者作为手头参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值