翻译: Kendiv
更新: Monday, February 07, 2005
在下一章中,我们会经常访问那些仅在内核模式下才有效的系统资源。大量的示例代码都被设计为内核驱动例程( Kernel-mode driver routine )。因此,需要有关开发此种软件的基本知识。因为我不能假定所有读者都有这方面的经验,我会在此简要地介绍一下内核模式驱动程序编程,不过这仅集中在如何使用驱动开发向导(在本书光盘上)。
本章还将讨论 Windows 2000 服务控制管理器( Service Control Manager , SCM )的基本知识,这包括 SCM 如何允许在运行时加载、控制和卸载驱动程序, resulting in wonderfully short change-build-test turnaround cycles 。本章的题目或许会让人有些误解,驱动一词通常与控制硬件的底层软件相关。事实上,很多内核程序员每天都在做这些事情。不过, Windows 2000 的驱动程序分层模式允许做比这更多的事情。内核驱动程序可以完成任意复杂的任务,若不考虑它们运行于更高的 CPU 特权级别上而且使用不同的开发接口,那它们很像用户模式下的 DLL 。在此,我们将使用这种强大的开发技术来侦测 Windows 2000 的内部秘密,使用内核驱动程序就像驾驶从狭小的用户模式飞往 Windows 2000 内核的太空飞船。
创建一个驱动程序的骨架
即使长时间开发 Win32 应用程序和库的开发人员,在首次编写内核驱动程序时,也会感觉像是一个绝对的初学者。这是因为,内核模式下的代码运行在一个完全不同的环境中。 Win32 开发人员的工作仅局限在属于 Windows 2000 Win32 子系统的几个系统组件上。其他开发人员可能编写 POSI 或 OS/2 应用程序, Windows 2000 的附加子系统为它们提供支持。感谢子系统这个概念, Windows 2000 就像一个变色龙 --- 它可通过这些子系统(前面提及的)导出不同的应用程序开发接口来模拟不同的。与此相反,内核模式的代码可以看到“真实”的 Windows 2000 。它们使用的接口可以称之为“最终边界”。当然,这并不是说,内核模式完全摆脱了子系统。在第二章中,我们看到 win32k.sys 就是 Win32 GUI 和窗口管理器在内核模式下的分支,将它们放在内核是出于性能考虑。然而, win32k.sys 导出的 API 函数集合中只有一小部分出现在了 gdi32.dll 和 user32.dll 中,这也意味着只有这一小部分函数可以作为 Win32 API 函数来使用,因此, Win32K 决不只是 Win32 踏入内核世界的一脚,实际上,应把它看作是一个高性能的内核模式的图形引擎。
Windows 2000 DDK ( Device Driver Kit )
由于内核模式下的编程使用了不同的系统接口,在 Win32 编程中经常使用的头文件和库都将无法在内核模式下使用。针对 Win32 开发,微软提供了 Platform Software Development Kit ( SDK )。而与内核模式的驱动开发相关的是, Windows 2000 Device Driver Kit ( DDK )。随文档一起, DDK 还提供了特殊的头文件和导入库,这些都是 Windows 2000 内核模块必须的接口。安装完 DDK 之后,接下来你应该打开 Visual C/C++ ,把 DDK 的路径加入到编译器和链接器的目录列表中。在主菜单中选择 Tools à Options ,然后单击 Directories 。在目录选择下拉列表中选择 Include files ,然后将 DDK 的适当路径加入,如 图 3-1 所示。默认情况下, DDK 将安装到 \NTDDK 目录下, included 文件位于 \NTDDK\inc 子目录中。需要注意的是,请将新添加的路径置于原有路径的上方,这样就会使用新的头文件或者库。

图 3-1 添加 DDK 头文件路径

图 3-2 添加 DDK 导入库路径
在添加完 DDK 头文件路径后,用同样的方法添加导入库的路径。 DDK 包含两组导入库,一组叫做 free ( release ) builds ,另一组叫做 checked ( debug ) builds 。其对应的目录为: \NTDDK\libfre\i386 和 \NTDDK\libchk\i386 ,参见 图 3-2 。
DDK 开发环境与 Win32 模式有所不同,下面给出二者之间的一些明显区别:
l 对于 Win32 程序员来说,主要的头文件是 windows.h ,对于内核模式代码来说,应使用 ntddk.h 替代之。
l 主进入点函数叫做 DirverEntry() ,而不再是 WinMain() 或 main() 。 列表 3-1 给出了它们的原型。
l 不能再使用一些常见的 Win32 数据类型,如 BYTE 、 WORD 和 DWORD 。 DDK 使用 UCHAR 、 USHORT 、 ULONG 等。不过,很容易就能定义你自己喜欢的类型, 列表 3-2 给出了这样的一个示例。
NTSTATUS DriverEntry ( PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING pusRegistryPath);
列表 3-1 DriverEntry 函数的原型
typedef UCHAR BYTE, *PBYTE;
typedef USHORT WORD, *PWORD;
typedef ULONG DWORD, *PDWORD;
列表 3-2 定义常见的 Win32 数据类型
此外,还需要注意 Windows NT 4.0 和 Windows 2000 所使用的 DDK 之间的差别,有三点不同需要注意,如下:
l 默认情况下, Windows NT 4.0 DDK 的主目录叫做 \DDK ,而 Windows 2000 DDK 叫做 \NTDDK
l 在 Windows NT 4.0 DDK 中,主要的头文件 ntddk.h 位于主目录之下。而在 Windows 2000 DDK 中,该文件被移到了 \NTDDK\DDK 子目录下。
l 导入库的路径也发生了变化: lib\i386\free 变成了 libfre\i386 , lib\i386\checked 变成了 libchk\i386 。
我不知道微软的这种改变有什么实际意义,不过为了生活,我们还是需要了解其变化 J 。