在内核模式驱动程序中调用 DLL

如果您从事过任何 Windows 编程,那么您可能写过至少一个 DLL。如果您刚开始编写 Windows 驱动程序,那么您可能试图从驱动程序调用 DLL,但是这并不可行。为什么呢?

内核模式中 DLL 的基本问题是 DLL 是否调用任何用户模式代码。如果 DLL 包含除本地内核 API 调用之外的任何内容,那么如果您在编译时试图将驱动程序与其链接,就会产生链接程序错误(内核根本不会加载它)。显然,编译成 Win32 库的 DLL 属于这种类别。即使您在 DLL 源代码中避免进行显式用户模式 API 调用,编译器仍然会在进行堆栈检查、溢出检查和类似操作时经常生成隐式的用户模式支持调用。所以,在 Windows 用户模式开发环境(例如 Visual Studio)中编译的任何 DLL 都不能在内核模式中使用。

您应该做什么?

使用 DDK 编译环境编译导出驱动程序。导出驱动程序是一个内核模式 DLL,为其他要调用的驱动程序提供例程。与任何标准驱动程序一样,导出驱动程序只包含解析内核模式函数的例程。但是,与标准驱动程序不同的是,导出驱动程序不接受 IRP 或占据驱动程序堆栈中的空间,它也不会被认为是一种系统服务。

您可以将任何标准驱动程序编译成导出驱动程序,在以常规的方式加载后它将作为标准驱动程序运行,并且还导出其他驱动程序可以调用的函数。但是,如果您只是想要编写库的内核模式等价物,那么您可以忽略完整的驱动程序所需的许多元素。导出驱动程序至少必须有一个 DriverEntry 例程;这可以是一个空的存根来满足编译脚本的要求,导出驱动程序的 DriverEntry 决不会被即插即用调用。导出驱动程序还应该具有标准入口点和卸载例程(DllInitializeDLLUnload),从而操作系统可以跟踪导出驱动程序的函数被其它驱动程序导入的次数,并在最后一次调用驱动程序卸载时卸载导出驱动程序。

您可以通过在导出驱动程序源代码中使用 DECLSPEC_EXPORT 宏声明函数将其导出,或者您可以采用更简单的方法,那就是在模块定义文件 (.def) 中将这些函数列出为导出。(即使您使用 DECLSPEC_EXPORT,您的 .def 文件仍然至少必须包含 DllInitializeDLLUnload,这样您才可以将这些函数标记为 PRIVATE。)

在驱动程序的 sources 文件中,将 TARGETTYPE 设置为 EXPORT_DRIVER 并且将 DLLDEF 设置为您的 .def 文件的路径,然后编译驱动程序。编译过程生成一个扩展名为 .lib 的导出库和一个扩展名为 .sys 的导出驱动程序。.sys 文件是您的内核模式 DLL。

导出驱动程序的静态链接本质上与用户模式 DLL 相同:将导出驱动程序库添加到将要调用库的驱动程序的目标库列表。在调用驱动程序的源代码中,使用 DECLSPEC_IMPORT 宏来声明它将调用的函数。(还应使用 ntdef.h 中定义的 EXTERN_C 来避免调用中出现任何不希望的名称修饰。)将导出驱动程序 .sys 文件安装到 %windir%/system32/drivers 目录中。它将在任何其它驱动程序第一次调用它时被加载。

动态链接稍微复杂一些,因为没有 GetProcAddress 的内核模式等价物。(MmGetSystemRoutineAddress 与其类似,但是它只从 Ntoskrnl.exe 或 HAL 动态解析导出。)这是用于动态链接到导出驱动程序的一些技术:

在导出驱动程序中,定义一个 I/O 控制码 (IOCTL) 来实例化一个类并返回一个其它驱动程序可以用来以常规方式调用接口对象上方法的接口指针。该类应该提供一个用于释放对象的方法,并且头文件应该只包含纯粹的抽象方法。

设计导出驱动程序来导出直接调用接口,以响应来自其它驱动程序的 IRP_MN_QUERY_INTERFACE 请求。

http://www.microsoft.com/china/whdc/driver/tips/KmDLL.mspx

http://www.wd-3.com/archive/KernelDlls.htm

你可能感兴趣的:(Windows驱动)