和本节对应的例子代码见KmdKitexamplessimpleBeepe_第1页
和本节对应的例子代码见KmdKitexamplessimpleBeepe_第2页
和本节对应的例子代码见KmdKitexamplessimpleBeepe_第3页
和本节对应的例子代码见KmdKitexamplessimpleBeepe_第4页
和本节对应的例子代码见KmdKitexamplessimpleBeepe_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、2.服务和本节对应的例子代码见KmdKitexamplessimpleBeeper读者也许有点疑惑:用户模式的服务关内核模式的驱动程序什么事呀?事实上,两者的确风马牛不相及,但是如果我们要和设备驱动程序通讯的话,我们必须首先安装它,启动它,而和设备驱动程序通讯的界面刚好和服务通讯的界面是类似的。Windows服务WindowsNT使用某种机制来启动进程,并让它们不和某个具体的交互式的用户界面相关联,这些进程就被称为服务(service),服务的一个很好的例子就是Web服务器,这些Web服务都没有用户界面,服务是唯一以这种方式运行的应用程序(注:指没有用户界面,当然,严格地说病毒、木马以及所有不

2、想见光的程序也是这样的),服务可以在系统启动的时候自动启动,也可以被手工启动,从这一点来看,设备驱动程序和服务是类似的。WindowsNT还支持驱动程序服务,只要使用的时候遵循设备驱动程序协议就可以了,这和用户模式的服务类似,所以,服务一词既可以指用户模式的服务进程或者内核模式的设备驱动程序,微软不知何故没有明确地区分两者的概念,所以下面的叙述可能看起来有点让人疑惑。可能有的地方我会说到driver词,但在其他的文章中可能说到service词,但既然这篇教程讲的是如何编写内核设备驱动程序,那么我们就约定无论说到service还是driver,我们的意思都是指驱动程序,当的确需要提及服务的时候,

3、我会明确地指出来的。另外,请读者时刻记得,文档中关于服务管理的函数其实是叙述得相当含糊的,因为这些函数既能用于驱动程序也能用于服务,在下面的文章中,我们只强调它们在驱动方面的用途和忽略服务方面的用途。WindowsNT中有三个组件和服务管理相关:服务控制管理器(ServiceControlManager/SCM)-用于启动服务以及和它通讯服务控制程序(ServiceControlProgram/SCP)-用于和SCM进行通讯,告诉它何时启动或者停止服务(咦!第三个哪里去了,我也不知道,原文就这么两个呀,可能后面会提到吧)服务程序中包含可执行代码,这两个组件对服务和驱动程序的处理方式是相同的。我

4、们先来看看前面两个组件,在后面再讲述驱动程序。服务控制管理器(SCM)SCM的代码位于%SystemRoot%System32Services.exe中,当系统启动的时候,SCM被WinLogon进程启动,然后它扫描注册表中HKLMSYSTEMCurrentControlSetServices键下的相关内容,根据这些内容创建一个服务数据库,数据库中包括所有服务的相关参数,如果服务或者驱动被标为自动启动的,那么启动它们并检测启动中是否出错。为了更深入一步,我们可以用注册表编辑器regedit.exe来打开并观察注册表中的HKLMSYSTEMCurrentControlSetServices下面的

5、内容。想要查看系统中安装了哪些服务(注意不是驱动),可以在控制面板中选择管理工具,再打开服务来查看。要查看系统中安装了哪些驱动,可以在控制面板中选择管理工具,再打开计算机管理,在系统信息下的软件环境中,你可以看到所有驱动的列表,但是不幸的是,在WindowsXP中,这个功能被取消了。仔细对比一下上面三个地方的内容,我们可以发现这些内容是很一致的。HKLMSYSTEMCurrentControlSetServices下面有很多子键,表示一个服务的内部名称,每个子键下包含了和这个服务相关的参数。现在来考察一下安装一个服务所需的最低数量的参数,我们拿beeper.sys来举例,以后再来讨论这个驱动本

6、身。图2.1beeper.sys驱动的注册表键值这些参数的含义如下:DisplayName-用户程序访问服务时使用的名称,如果为空,那么注册表的键名会被作为它的名称ErrorControl如果SCM启动服务的时候驱动报错,这个值决定了SCM如何对付这个错误,我们对两种取值有点兴趣:SERVICE_ERROR_IGNORE(0)1/0管理器忽略这个错误,不作记录SERVICE_ERROR_NORMAL(1)如果驱动被装入的时候报错,系统将给用户显示一个告警框,并将错误记录到系统日志中你可以在控制面板中的管理工具中选择事件查看器来查看系统日志,例如,beeper.sys驱动在初始化的时候做完了所有

7、该做的事(这个例子会让喇叭发声音,但是发声功能是在初始化函数DriverEntry中做的,初始化函数执行完,后面就没什么事了),所以它就返回一个错误,系统就会将它从内存中卸载。但是这里的ErrorControl参数等于SERVICE_ERROR_IGNORE,所以系统日志中并没有错误记录。ImagePath-指驱动文件的全路径文件名,如果该参数没有指定路径,那么系统会在%SystemRoot%Drivers目录下查找Start-指明何时装载驱动,这里我们关心的也是两个取值SERVICE_AUTO_START(2)驱动在系统启动的时候装载SERVICE_DEMAND_START(3)驱动由SCM

8、根据用户要求装载如果驱动的Start参数为SERVICE_AUTO_START(2),那么SCM会在系统启动的时候就装载它,这样的驱动被称为自动启动的服务,如果驱动的执行依赖于其他的驱动,SCM也会把其他的驱动也启动起来(要控制设备驱动被装载的顺序,可以使用Group、Tag和DependOnGroup等参数值;要控制服务被装载的顺序,可以使用Group和DependOnService参数)。Start参数还有其他的取值,如SERVICE_BOOT_START(0),但这个参数只能供设备驱动程序使用,I/O管理器将在用户模式的进程启动之前把装载这些驱动程序,这时SCM还没有启动呢!Type-用

9、于指定服务的类型,既然我们这里讲的是KMD的编程,那么我们只对一个取值感兴趣,那就是SERVICE_KERNEL_DRIVER(1)仔细观察图2.1后,你对beeper.sys有什么要说的吗?好的,我们看到beeper这个内核模式驱动程序位于C:masm32RingOKmdArticle2beeper目录下,它的名称为NiceMelodyBeeper,由用户控制启动,出错信息不被记录。Path前面的?前缀的含义你下面就会知道!如果我们要启动SCM数据库中不存在的驱动程序,那么可以在任何时刻在SCP的帮助下动态装入(也许称为DCP/devicecontrolprogram更为贴切,但是微软的术语

10、库中并没有这个词)。2.3服务控制程序(SCP)从名称理解,服务控制程序(servicecontrolprogram/SCP)可以控制服务或者设备驱动程序,这些功能是在SCM的管理下,通过调用适当的函数来完成的,这些函数位于%SystemRoot%System32advapi.dll(AdvancedAPI)中。这里是一段关于使用SCP来控制beeper.sys驱动的代码例子ServiceControlProgramforbeeperdriver.386.modelflat,stdcalloptioncasemap:noneINCLUDEFILESincludemasm32includewin

11、dows.incincludemasm32includekernel32.incincludemasm32includeuser32.incincludemasm32includeadvapi32.incincludelibmasm32libkernel32.libincludelibmasm32libuser32.libincludelibmasm32libadvapi32.libincludemasm32MacrosStrings.mac.codestartproclocalhSCManager:HANDLElocalhService:HANDLElocalacDriverPathMAX_

12、PATH:CHARinvokeOpenSCManager,NULL,NULL,SC_MANAGER_CREATE_SERVICE.ifeax!=NULLmovhSCManager,eaxpusheaxinvokeGetFullPathName,$CTA0(beeper.sys),sizeofacDriverPath,addracDriverPath,esppopeaxinvokeCreateService,hSCManager,$CTA0(beeper),$CTA0(NiceMelodyBeeper),SERVICE_START+DELETE,SERVICE_KERNEL_DRIVER,SER

13、VICE_DEMAND_START,SERVICE_ERROR_IGNORE,addracDriverPath,NULL,NULL,NULL,NULL,NULL.ifeax!=NULLmovhService,eaxinvokeStartService,hService,0,NULLinvokeDeleteService,hServiceinvokeCloseServiceHandle,hService.elseinvokeMessageBox,NULL,$CTA0(Cantregisterdriver.),NULL,MB_ICONSTOP.endifinvokeCloseServiceHand

14、le,hSCManager.elseinvokeMessageBox,NULL,$CTA0(CantconnecttoServiceControlManager.),NULL,MB_ICONSTOP.endifinvokeExitProcess,0startendpendstart2.3.1建立到SCM的连接在上面的例子中,我们首先要做的事情是使用OpenSCManager函数来建立到SCM的连接,以便在指定的计算机上打开服务数据库。OpenSCManagerprotolpMachineName:LPSTR,lpDatabaseName:LPSTR,dwDesiredAccess:DWORD函

15、数使用的参数说明如下:lpMachineName指向需要打开的计算机名字符串,字符串以NULL结尾,如果参数指定为NULL,表示连接到本机上的SCMlpDatabaseName指向以NULL结尾的包含SCM数据库名称的字符串,字符串应该指定为ServicesActive,如果参数指定为NULL,则默认打开ServicesActive.constszActiveDatabasedbServicesActive,0SERVICES_ACTIVE_DATABASEequoffsetszActiveDatabase现在我们要打开的就是这个当前被激活的数据库,所以我们使用了NULL参数dwDesired

16、Access-指定访问SCM的权限,这个参数告诉SCM我们需要进行什么样的操作,常用的取值有三个:SC_MANAGER_CONNECT允许连接到SCM,这个取值是默认值,它的定义就是0SC_MANAGER_CREATE_SERVICE允许创建服务SC_MANAGER_ALL_ACCESS允许进行所有的操作我们可以使用下面的代码连接到SCM:invokeOpenSCManager,NULL,NULL,SC_MANAGER_CREATE_SERVICE.ifeax!=NULLmovhSCManager,eax如果OpenSCManager函数执行成功,那么返回值就是被连接的SCM的句柄,我们在以后

17、使用其他函数在对SCM数据库进行操作的时候会用到这个句柄。另外,忘了提醒大家,安装内核模式驱动程序需要超级用户的权限,为了安全起见,普通权限的用户没有被授权的话是无法执行特权代码的。当然,本文的例子总是假设你是有超级用户权限的。安装一个新的驱动打开SCM后,我们可以用CreateService函数将驱动添加到服务数据库中,这里是该函数的原型,CreateService函数远不止三个参数,但不要害怕,这些参数都是很简单的:CreateServiceprotohSCManager:HANDLE,lpServiceName:LPSTR,lpDisplayName:LPSTR,dwDesiredAcc

18、ess:DWORD,dwServiceType:DWORD,dwStartType:DWORD,dwErrorControl:DWORD,lpBinaryPathName:LPSTR,lpLoadOrderGroup:LPSTR,lpdwTagId:LPDWORD,lpDependencies:LPSTR,lpServiceStartName:LPSTR,lpPassword:LPSTR参数说明如下:hSCManager-不用说了吧?就是上一节中得到的SCM句柄lpServiceName-指向一个以0字符结尾的表示服务名称的字符串,字符串的最大长度是256个字符,名称中不允许使用/或者字符(因

19、为这些字符会和注册表的路径表示方式冲突),这个值和注册表中的键名是相对应的lpDisplayName-指向一个以0字符结尾表示服务名称的字符串,这个名称是供用户界面程序识别函数时使用的,同样,它的最大长度也是256个字符。这个值和注册表中的DisplayName键的值是相对应的dwDesiredAccess-指定需要访问服务的操作,可以有以下取值:SERVICE_ALL_ACCESS可以进行所有操作SERVICE_START允许调用StartService函数来启动服务SERVICE_STOP允许调用ControlService函数来停止服务DELETE-允许调用DeleteService函数

20、来删除服务在这里我们只需要做两件事情:启动驱动和删除驱动,所以例子中使用了SERVICE_START和DELETE,我们不需要停止服务的操作,因为上面已经说过,这个驱动在初始化的时候就会返回错误(所以它不会有已经启动的状态)。dwServiceType-服务的类型,我们的教程中只用得到SERVICE_KERNEL_DRIVER,这个值和注册表中的Type键的值是相对应的dwStartType-表示在什么时候启动服务,如果我们需要手动启动驱动的话,那么使用SERVICE_DEMAND_START参数,如果驱动程序需要在系统启动的时候就被启动,那么使用SERVICE_AUTO_START参数,这个

21、取值和注册表中的Start键的取值是相对应的dwErrorControl-表示当驱动初始化的时候出错该如何处理,取值SERVICE_ERROR_IGNORE表示忽略错误,取值SERVICE_ERROR_NORMAL表示将错误记录到系统日志中去,这个取值和注册表中的ErrorControl键值是相对应的lpBinaryPathName-指向以0结尾的表示驱动程序文件名的字符串,这个值和注册表中的ImagePath的键值是相对应的lpLoadOrderGroup-指向以0结尾的表示组名称的字符串,表示该驱动属于哪个组,既然我们的例子程序不属于任何组,那么这里就用NULL好了lpdwTagld指向一

22、个32位的缓冲区,用来接收驱动在lpLoadOrderGroup参数指定的组中的唯一的标识,我们的例子中不需要用到这个表示,所以参数指定为NULLlpDependencies对于驱动程序来说,这个参数没什么用途,设置为NULL好了lpServiceStartName-指向一个以0结尾的表示帐号名称的字符串,用于指定服务允许在哪个帐号下运行,如果服务类型是SERVICE_KERNEL_DRIVER的话,该帐号就是系统装入服务的模块名称,我们在这里使用NULL,表示由默认的模块装入lpPassword对于驱动程序来说,这个参数没什么用途,设置为NULL好了现在来总结一下,最后的5个参数总是设置为N

23、ULL,我们就把它抛到脑后去好了,第一个参数是SCM句柄,而dwDesiredAccess参数也是很好理解的,剩下的参数是什么?聪明的你一定已经猜到了-它们实际上就是和注册表里面的键一一对应的!看看下表就明白了:CreateService函数的参数注册表lpServiceNamelpDisplayNamedwServiceTypedwStartTypedwErrorControllpBinaryPathName键名DisplayNameTypeStartErrorControlImagePath表2.1参数和注册表键的对应关系好了,现在回过头来看看例子代码:pusheaxinvokeGetFu

24、llPathName,$CTA0(beeper.sys),sizeofacDriverPath,addracDriverPath,esppopeaxinvokeCreateService,hSCManager,$CTA0(beeper),$CTA0(NiceMelodyBeeper),SERVICE_START+DELETE,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,addracDriverPath,NULL,NULL,NULL,NULL,NULL.ifeax!=NULLmovhService,eax首先,我

25、们调用GetFullPathName函数来获取全路径的驱动程序文件名,并把它传递给CreateService函数。然后CreateService函数将这个驱动程序加入到SCM的数据库中,并创建对应的注册表键,正如表2.1所示的,所有这些键将被CreateService函数加入到注册表中,如果你在源代码中把DeleteService行去掉,将csp.asm重新编译并执行,就可以验证我说的了。不要认为使用RegXXX之类的函数将相同的信息写入注册表就可以达到相同的结果,这样操作的话,键值是写到注册表里面了,但是SCM的数据库里面可什么都没有哦!如果SCM数据库中指定的设备驱动程序已经存在,那么Cr

26、eateService函数会返回一个错误,这时可以调用GetLastError函数获取具体原因,上例中会得到ERROR_SERVICE_EXISTS。如果CreateService函数成功地将驱动加入到了SCM数据库中,函数的返回值就是驱动的句柄,这个句柄在后面的驱动管理函数中将会被用到。启动驱动程序下一步要调用的函数是StartService,它的原型申明如下:StartServiceprotohService:HANDLE,dwNumServiceArgs:DWORD,lpServiceArgVectors:LPSTR参数说明如下:hService-就是上一小节中由CreateServic

27、e返回的驱动的句柄dwNumServiceArgs-用于驱动程序的时候,这个参数总是设置为NULLlpServiceArgVectors-同上,也为NULL启动驱动的方法就是这样的:invokeStartService,hService,0,NULLStartService函数的执行过程和装入用户模式的DLL的过程类似,驱动程序文件的映像被装入到系统的地址空间中,文件可以被装入到任何地址中,然后系统会根据PE文件中的重定位表对其进行重定位操作,这样驱动程序的内存映像就被准备好了,接下来系统调用驱动的入口函数,也就是DriverEntry子程序,和装入DLL不同的是,DriverEntry子程序

28、的执行是在系统进程的上下文中进行的。StartService函数的调用是同步执行的,也就是说,只有驱动程序的DriverEntry过程返回后,函数才会返回(回想一下,如果函数不等人家执行完就直接返回了,那叫什么那是异步!)。如果驱动初始化成功,那么DriverEntry过程应该返回STATUS_SUCCESS,这样StartService会返回一个非0值,这时,我们又回到了调用StartService的用户模式的上下文中了。在这个例子中,我们并不关心StartService函数的返回值,理由前面已经说过了,那就是beeper驱动程序在DriverEntry中进行了发声音功能的演示,并返回一个错误码,后面再没

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论