接触windows驱动开发有一个月了,感觉Windows驱动编程并不像传说中的那么神秘。
为了更好地为以后的学习打下基础,记录下来这些学习心得,也为像跟我一样致力于驱动开发却苦于没有门路的菜鸟朋友们抛个砖,引个玉。
我的开发环境:Windows xp 主机+ VMW ARE虚拟机(windows 2003 server系统)。
编译环境:WinDDK6001.18002。
代码编辑工具:SourceInsight。
IDE:VS2005/VC6.0。
调试工具:WinDBG,DbgView.exe, SRVINSTW.EXE
上面所有工具均来自互联网。
对于初学者,DbgView.exe和SRVINSTW.EXE是非常简单有用的两个工具,一定要装上。
前者用于查看日志信息,后者用于加载驱动。
下面从最简单的helloworld说起吧。
Follow me。
驱动程序的入口函数叫做DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegisgryString)。
两个参数,一个是驱动对象,代表该驱动程序;另一个跟注册表相关,是驱动程序在注册表中的服务名,暂时不用管它。
DriverEntry 类似于C语言中的main函数。
它跟main的差别就是,main完全按照顺序调用的方法执行,所有东西都按照程序员预先设定的顺序依次发生;而DriverEntry则有它自己的规则,程序员只需要填写各个子例程,至于何时调用,谁先调,由操作系统决定。
我想这主要是因为驱动偏底层,而底层与硬件打交道,硬件很多都是通过中断来与操作系统通信,中断的话就比较随机了。
但到了上层应用程序,我们是看不到中断的影子的。
说到中断,驱动程序中可以人为添加软中断,__asm int 3或者Int_3();前者是32位操作系统用的,后者是64位用的。
64位驱动不允许内嵌汇编。
下面是我的一个helloworld的源码:
注意第16行的宏。
目前我们的驱动程序暂时不需要和应用程序通信,所以用Macro注释掉。
从这里也可以看出来,想要实现sys和exe的通信,就要编写相应的派遣函数,在本例就是各种MajorFunction。
这个驱动完成的功能很简单,加载时,打印一句话“This is helloworld”
卸载时打印另外一句话“Driver Unloaded”。
DbgPrint跟c语言中的printf类似,也是在终端上输出调试信息。
我们可以利用前面介绍的DbgView来观察输出信息。
这个小东西只有四百多k,不过功能一点都不弱。
它能查看所有的内核日志,前提是要把capture里面的capture kernel选项打上勾。
上面这个图是我debug一个驱动时候的输出信息。
对了,忘了说怎么加载驱动程序了。
Windows驱动多种多样,加载方式也各不相同。
可以编写应用程序加载,不过这就麻烦了,还需要编写exe。
如果仅仅是想做驱动,那么我建议用上面提到的srvinstw工具来加载。
具体可以参考《寒江独钓windows内核安全编程》一书。
这这本书写的非常精彩,图文并茂,尤其是谭文写的那几章,深入浅出。
到这里,一个简单的windows驱动程序就完成了。
不过这个helloworld没干任何有意义的事,仅供演示用。
接下来探讨一下驱动层和应用层的交互问题。
驱动说到底,还是为上层服务。
它们之间一般用dll动态链接库来联系。
Dll其实也应该属于应用层的东西。
它将驱动提供的功能进一步封装,然后暴露接口给应用程序,应用程序利用这些接口api,来完成驱动程序预先设定好的各项功能。
Dll的编写不是本文讨论的内容,各位可以去网上搜一下dll的知识,很简单,按照标准格式填写就好了,只不过编译的时候,VC++或者VisualStudio要选择编译目标是动态链接库。
这里重点说下dll如何来调用驱动程序的各个功能。
也许你已经猜到了,对,我们现在把DriverEntry中16行的
DRIVER_APP_COMMUNICA TE打开。
打开后大家会发现,多了5个重要的东西。
1.创建设备对象
2.创建连接符号
3.填充IRP_MJ_CREATE
4.填充IRP_MJ_CLOSE
5.填充IRP_MJ_DEVICE_CONTROL
第一个和第二个是为了让应用程序找到我编写的驱动。
把应用程序领进门。
那么这里的门牌号就是符号链接LinkString。
要是就是IRP_MJ_CREATE(上层使用CreateFile调到DriverCreate)。
回想一下CreateFile的参数:
这里的lpFileName,其实对应于驱动中的LinkName。
只不过需要做个小转换。
驱动中这个字段一般是”\\??\\helloworldLink”,有些地方也写成\\DosDevices\\helloworldLink,一个意思。
应用程序中一般写作\\\\.\\helloworldLink,具体为啥这样,我还没弄清楚。
对了,设备对象名称也有规定,\\Device\\helloworld,前面这个Device也不是随便写的,读者可以用winobj查看,凡是winobj上面显示的有的,就可以用,否则不可以用。
那么好,现在你通过CreateFile打开了驱动,打开了门,那么我怎么跟门里面的人打交道呢?这就是IoControl要做的事情。
通过预先设定好的暗号,或者叫人名,这个人名应用程序知道,驱动也知道,就可以相互通信了。
所以我们经常看到别人的驱动代码里面DriverControl是一个很大的switch,case式结构。
Dll里面用DeviceIoControl这个api,将人名、驱动需要的参数、参数长度,输出结果等都填进去,驱动程序在switch case里面就可以做出回应。
写的比较乱,有很多东西没写出来。
欢迎大家一块交流。