《Undocumented Windows 2000 Secrets》翻译 --- 第三章(1)

第三章  编写内核模式驱动程序

翻译:Kendiv( fcczj@263.net )

更新: Monday, February 07, 2005

 

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

在下一章中,我们会经常访问那些仅在内核模式下才有效的系统资源。大量的示例代码都被设计为内核驱动例程(Kernel-mode driver routine)。因此,需要有关开发此种软件的基本知识。因为我不能假定所有读者都有这方面的经验,我会在此简要地介绍一下内核模式驱动程序编程,不过这仅集中在如何使用驱动开发向导(在本书光盘上)。

 

本章还将讨论Windows 2000服务控制管理器(Service Control ManagerSCM)的基本知识,这包括SCM如何允许在运行时加载、控制和卸载驱动程序,resulting in wonderfully short change-build-test turnaround cycles。本章的题目或许会让人有些误解,驱动一词通常与控制硬件的底层软件相关。事实上,很多内核程序员每天都在做这些事情。不过,Windows 2000的驱动程序分层模式允许做比这更多的事情。内核驱动程序可以完成任意复杂的任务,若不考虑它们运行于更高的CPU特权级别上而且使用不同的开发接口,那它们很像用户模式下的DLL。在此,我们将使用这种强大的开发技术来侦测Windows 2000的内部秘密,使用内核驱动程序就像驾驶从狭小的用户模式飞往Windows 2000内核的太空飞船。

 

创建一个驱动程序的骨架

即使长时间开发Win32应用程序和库的开发人员,在首次编写内核驱动程序时,也会感觉像是一个绝对的初学者。这是因为,内核模式下的代码运行在一个完全不同的操作系统环境中。Win32开发人员的工作仅局限在属于Windows 2000 Win32子系统的几个系统组件上。其他开发人员可能编写POSIOS/2应用程序,Windows 2000的附加子系统为它们提供支持。感谢子系统这个概念,Windows 2000就像一个变色龙---它可通过这些子系统(前面提及的)导出不同的应用程序开发接口来模拟不同的操作系统。与此相反,内核模式的代码可以看到“真实”的Windows 2000操作系统。它们使用的接口可以称之为“最终边界”。当然,这并不是说,内核模式完全摆脱了子系统。在第二章中,我们看到win32k.sys就是Win32 GUI和窗口管理器在内核模式下的分支,将它们放在内核是出于性能考虑。然而,win32k.sys导出的API函数集合中只有一小部分出现在了gdi32.dlluser32.dll中,这也意味着只有这一小部分函数可以作为Win32 API函数来使用,因此,Win32K决不只是Win32踏入内核世界的一脚,实际上,应把它看作是一个高性能的内核模式的图形引擎。

 

Windows 2000 DDKDevice Driver Kit

由于内核模式下的编程使用了不同的系统接口,在Win32编程中经常使用的头文件和库都将无法在内核模式下使用。针对Win32开发,微软提供了Platform Software Development KitSDK)。而与内核模式的驱动开发相关的是,Windows 2000 Device Driver KitDDK)。随文档一起,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包含两组导入库,一组叫做freereleasebuilds,另一组叫做checkeddebugbuilds。其对应的目录为:/NTDDK/libfre/i386/NTDDK/libchk/i386,参见3-2

 

DDK开发环境与Win32模式有所不同,下面给出二者之间的一些明显区别:

l         对于Win32程序员来说,主要的头文件是windows.h,对于内核模式代码来说,应使用ntddk.h替代之。

l         主进入点函数叫做DirverEntry(),而不再是WinMain()main()列表3-1给出了它们的原型。

l         不能再使用一些常见的Win32数据类型,如BYTEWORDDWORDDDK使用UCHARUSHORTULONG等。不过,很容易就能定义你自己喜欢的类型,列表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.0Windows 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/i386lib/i386/checked变成了libchk/i386

 

我不知道微软的这种改变有什么实际意义,不过为了生活,我们还是需要了解其变化J

 

可定制的驱动程序向导

开发内核驱动程序的主要困难在于Visual C/C++没有提供此种类型的工程向导。幸运的是,MSDN里有一系列不错的关于Windows NT内核驱动开发的文章,是Ruediger R.Asche.19941995年编写的。其中的两篇文章(Asche 1995a1995b)详细说明了如何在Visual C/C++中加入自定义的驱动程序向导,这些文章给了我很大的帮助,尽管原始向导的输出文件不能满足我的所有需求,但这是一个很好的开始。我提供的内核驱动向导将基于Ruediger Asche的原始向导产生的输出文件。

 

我提供的驱动向导的所有源代码位于本书光盘的/src/w2k_wiz目录。通过阅读这些代码,你会发现它实际的标题“SBS Windows 2000 Code Wizard”。事实上,这是一个一般性的Windows 2000程序骨架生成器,该生成器可以产生多种类型的程序,包括Win32 DLL和应用程序。不过,光盘中的配置文件针对内核驱动开发做了一定的修改。基本上来说,我提供的向导是一个文件转换器,它读取一组文件,然后按照一些简单的规则将它们进行转换,最后将结果写入另一组文件中。输入文件是模板,输出文件是C工程文件。通过修改模板文件,该向导可以变成一个DLL向导等等。必须提供7个模板文件(如果丢失了某一个,会产生错误):

l         扩展名为.tw的文件是workspace模板,此种文件将会被保存为Visual Studio的工程文件.dsw

l         扩展名为.tp的文件是工程模板,此种文件将被保存为.dsp文件。工程文件由于之关联的workspace文件引用,工程文件还包含生成工程的所有配置选项。

l         扩展名为.tc.th.tr.td的文件都是C代码文件,这些文件最后会变成相应的.c.h.rc.def文件。

l         扩展名为.ti的是icon文件,该文件会被直接保存为.ico文件。

 

这七个文件是一个新工程所必需的。.def文件以一种较老风格的方法从DLL中导出API函数,不过我更喜欢__declspec(dllexport)方式。因为驱动程序通常不导出函数,所以我省略了.td模板,导致的结果是,在开始时,向导会报告一个错误。我还省略了资源脚本和icon文件,不过经验告诉我,最好提供它们。采用的转换规则也非常简单,仅包含一个很短的字符串替换列表。在扫描模板文件时,转换器查找以%号开始的转义符。当它找到后,会根据%后的字符来决定执行什么样的动作。3-1列出了验证过的转义符。

3-1中有几处需参考配置文件---w2k_wiz.ini示例3-1给出了其默认设置。在使用向导之前,你应该将光盘/src/w2k_wiz/release目录下的w2k_wiz.exew2k_wiz.ini和所有的w2k_wiz.t*模板文件复制到你的硬盘上,然后编辑配置文件,将对应内容改为你自己的设置。你还需要修改IncludeFreeChecked,使其和你的DDK安装相匹配。如果你使用Visual C/C++ 6.0,可以不改变Root的值。如果不,则将其设为你存放工程文件的根目录。如果以一个反斜线结尾,它将作为默认值。在示例3-1中,其键值为:HKEY_CURRENT_USER/SoftWare/Microsofto/DevStudio/6.0/Directories,而WorkspaceDir用来存放基本的工作目录。

 

键入w2k_wiz MyDriver来执行该向导,它会当前目录下创建名为MyDriver的工程目录,该目录将存放向导生成的MyDriver.dswMyDriver.dspMyDriver.cMyDriver.hMyDriver.rcMyDriver.ico文件。如果你指定了具体的路径,则会在你指定的路径下创建该目录。另一个合法的命令选项是星号,如:w2k_wiz *MyDriver。在此种情况下,向导不会在当前目录下创建工程目录,而是去查找Visual C/C++维护的默认的工程根目录,即w2k_wiz.ini中的Root所指向的位置。

 

; w2k_wiz.ini

; 08-27-2000 Sven B. Schreiber

; sbs@orgon.com

 

[Settings]

Text    = <SBS Windows 2000 Code Wizard Project>

Company = <MyCompany>

Author  = <MyName>

Email   = <my@email>

Prefix   = <MyPrefix>

Include  = E:/NTDDK/inc

Free    = E:/NTDDK/libfre/i386

Checked = E:/NTDDK/libchk/i386

Root = HKEY_CURRENT_USER/Software/Microsoft/DevStudio/6.0/Directories/WorkspaceDir

示例3-1.  向导支持的自定义选项

 

运行驱动向导

现在,来试试这个驱动向导。示例3-2给出了在Windows 2000控制台下执行w2k_wiz *TestDrv后的输出。这将在Visual C/C++默认的工程根目录下创建一个名为TestDrv的工程目录。

 

显然,除了将.td模板转换为.def时出了错,其余转换都成功的完成了。因为该向导生成的驱动程序骨架不需要.def文件,所以不需要提供.td模板文件。现在,用Visual C/C++打开一个新的WorkSpace,然后你会发现一个名为TestDrv的新目录,该目录中包含一个名为TestDrv.dswWorkSpace文件。该文件可以被正确的打开。接下来,你因该为生成项目选择活动的配置信息。驱动向导生成的.dsp文件提供了如下两个可用配置:

1.         Win2k Kernel-mode Driver(debug)

2.         Win2k Kernel-mode Driver(release)

 

默认情况下,将使用debug配置来生成项目,但是你可在任何时候从Visual C/C++菜单Build/Set Active Configuration来选择不同的项目配置。最后,你要将光盘中的/src/common/include/DrvInfo.h复制到你自己的头文件目录中。在打开TestDrv.rc时,应使用文本模式来打开(如3-3所示),这是因为该文件使用了来自DrvInfo.h中的复杂的宏定义,这些宏会导致资源编辑器异常退出。这个错误从Visual C/C++ 5.0开始,在我印象中,一直没有被改正过。和编辑器不同,资源编译器(Resource Compiler)可以正常的处理这些宏。

3-3.  以文本模式打开TestDrv.cTestDrv.hTestDrv.rc

 

现在,已经为第一次编译做好了所有准备。在示例3-3中,我通过选择Build/Rebuild菜单来建立DriverRelease版,看起来一切都正常。顺便说一下,头两行末尾的省略号表示我截断了Build命令的输出。

 

    链接器会在DebugRelease目录下创建了一个名为TestDrv.sys的可执行文件,这依赖于你的生成配置。Test DriverRelease版大小为5.5KB,其Debug版为8KB。你可以使用本书光盘中的MFVDasmPEView来验证TestDrv.sys是否包含有效的代码和数据。

 

深入驱动程序的骨架

列表3-3展示了向导生成的TestDrv.c。与之相关的头文件TestDrv.h列表3-4中。在列表3-3中,请注意标题处的<MyName><MyCompany>标志。如果w2k_wiz.ini中的作者和公司名称正确,那你自己的名字和相应的公司名称将会替代它们。

 

// TestDrv.c

// 08-07-2000 <MyName>

// Copyright @2005 <MyCompany>

 

#define  _TESTDRV_SYS_

#include <ntddk.h>

#include "TestDrv.h"

 

// =================================================================

// DISCLAIMER

// =================================================================

 

/*

 

This software is provided "as is" and any express or implied

warranties, including, but not limited to, the implied warranties of

merchantability and fitness for a particular purpose are disclaimed.

In no event shall the author <MyName> be liable for any

direct, indirect, incidental, special, exemplary, or consequential

damages (including, but not limited to, procurement of substitute

goods or services; loss of use, data, or profits; or business

interruption) however caused and on any theory of liability,

whether in contract, strict liability, or tort (including negligence

or otherwise) arising in any way out of the use of this software,

even if advised of the possibility of such damage.

 

*/

 

// =================================================================

// REVISION HISTORY

// =================================================================

 

/*

 

08-07-2000 V1.00 Original version.

 

*/

 

// =================================================================

// GLOBAL DATA

// =================================================================

 

PRESET_UNICODE_STRING (usDeviceName,       CSTRING (DRV_DEVICE));

PRESET_UNICODE_STRING (usSymbolicLinkName, CSTRING (DRV_LINK  ));

 

PDEVICE_OBJECT  gpDeviceObject  = NULL;

PDEVICE_CONTEXT gpDeviceContext = NULL;

 

// =================================================================

// DISCARDABLE FUNCTIONS

// =================================================================

 

NTSTATUS DriverInitialize (PDRIVER_OBJECT  pDriverObject,

                           PUNICODE_STRING pusRegistryPath);

 

NTSTATUS DriverEntry      (PDRIVER_OBJECT  pDriverObject,

                           PUNICODE_STRING pusRegistryPath);

 

// -----------------------------------------------------------------

 

#ifdef ALLOC_PRAGMA

 

#pragma alloc_text (INIT, DriverInitialize)

#pragma alloc_text (INIT, DriverEntry)

 

#endif

 

// =================================================================

// DEVICE REQUEST HANDLER

// =================================================================

 

NTSTATUS DeviceDispatcher (PDEVICE_CONTEXT pDeviceContext,

                           PIRP            pIrp)

    {

    PIO_STACK_LOCATION pisl;

    DWORD              dInfo = 0;

    NTSTATUS           ns    = STATUS_NOT_IMPLEMENTED;

 

    pisl = IoGetCurrentIrpStackLocation (pIrp);

 

    switch (pisl->MajorFunction)

        {

        case IRP_MJ_CREATE:

        case IRP_MJ_CLEANUP:

        case IRP_MJ_CLOSE:

            {

            ns = STATUS_SUCCESS;

            break;

            }

        }

    pIrp->IoStatus.Status      = ns;

    pIrp->IoStatus.Information = dInfo;

 

    IoCompleteRequest (pIrp, IO_NO_INCREMENT);

    return ns;

    }

 

// =================================================================

// DRIVER REQUEST HANDLER

// =================================================================

 

NTSTATUS DriverDispatcher (PDEVICE_OBJECT pDeviceObject,

                           PIRP           pIrp)

    {

    return (pDeviceObject == gpDeviceObject

            ? DeviceDispatcher (gpDeviceContext, pIrp)

            : STATUS_INVALID_PARAMETER_1);

    }

 

// -----------------------------------------------------------------

 

void DriverUnload (PDRIVER_OBJECT pDriverObject)

    {

    IoDeleteSymbolicLink (&usSymbolicLinkName);

    IoDeleteDevice       (gpDeviceObject);

    return;

    }

 

// =================================================================

// DRIVER INITIALIZATION

// =================================================================

 

NTSTATUS DriverInitialize (PDRIVER_OBJECT  pDriverObject,

                           PUNICODE_STRING pusRegistryPath)

    {

    PDEVICE_OBJECT pDeviceObject = NULL;

    NTSTATUS       ns = STATUS_DEVICE_CONFIGURATION_ERROR;

 

    if ((ns = IoCreateDevice (pDriverObject, DEVICE_CONTEXT_,

                              &usDeviceName, FILE_DEVICE_CUSTOM,

                              0, FALSE, &pDeviceObject))

        == STATUS_SUCCESS)

        {

        if ((ns = IoCreateSymbolicLink (&usSymbolicLinkName,

                                        &usDeviceName))

            == STATUS_SUCCESS)

            {

            gpDeviceObject  = pDeviceObject;

            gpDeviceContext = pDeviceObject->DeviceExtension;

 

            gpDeviceContext->pDriverObject = pDriverObject;

            gpDeviceContext->pDeviceObject = pDeviceObject;

            }

        else

            {

            IoDeleteDevice (pDeviceObject);

            }

        }

    return ns;

    }

 

// -----------------------------------------------------------------

 

NTSTATUS DriverEntry (PDRIVER_OBJECT  pDriverObject,

                      PUNICODE_STRING pusRegistryPath)

    {

    PDRIVER_DISPATCH *ppdd;

    NTSTATUS          ns = STATUS_DEVICE_CONFIGURATION_ERROR;

 

    if ((ns = DriverInitialize (pDriverObject, pusRegistryPath))

        == STATUS_SUCCESS)

        {

        ppdd = pDriverObject->MajorFunction;

 

        ppdd [IRP_MJ_CREATE                  ] =

        ppdd [IRP_MJ_CREATE_NAMED_PIPE       ] =

        ppdd [IRP_MJ_CLOSE                   ] =

        ppdd [IRP_MJ_READ                    ] =

        ppdd [IRP_MJ_WRITE                   ] =

        ppdd [IRP_MJ_QUERY_INFORMATION       ] =

        ppdd [IRP_MJ_SET_INFORMATION         ] =

        ppdd [IRP_MJ_QUERY_EA                ] =

        ppdd [IRP_MJ_SET_EA                  ] =

        ppdd [IRP_MJ_FLUSH_BUFFERS           ] =

        ppdd [IRP_MJ_QUERY_VOLUME_INFORMATION] =

        ppdd [IRP_MJ_SET_VOLUME_INFORMATION  ] =

        ppdd [IRP_MJ_DIRECTORY_CONTROL       ] =

        ppdd [IRP_MJ_FILE_SYSTEM_CONTROL     ] =

        ppdd [IRP_MJ_DEVICE_CONTROL          ] =

        ppdd [IRP_MJ_INTERNAL_DEVICE_CONTROL ] =

        ppdd [IRP_MJ_SHUTDOWN                ] =

        ppdd [IRP_MJ_LOCK_CONTROL            ] =

        ppdd [IRP_MJ_CLEANUP                 ] =

        ppdd [IRP_MJ_CREATE_MAILSLOT         ] =

        ppdd [IRP_MJ_QUERY_SECURITY          ] =

        ppdd [IRP_MJ_SET_SECURITY            ] =

        ppdd [IRP_MJ_POWER                   ] =

        ppdd [IRP_MJ_SYSTEM_CONTROL          ] =

        ppdd [IRP_MJ_DEVICE_CHANGE           ] =

        ppdd [IRP_MJ_QUERY_QUOTA             ] =

        ppdd [IRP_MJ_SET_QUOTA               ] =

        ppdd [IRP_MJ_PNP                     ] = DriverDispatcher;

        pDriverObject->DriverUnload            = DriverUnload;

        }

    return ns;

    }

 

// =================================================================

// END OF PROGRAM

// =================================================================

列表3-3.  驱动程序骨架的源代码

 

// TestDrv.h

// 08-07-2000 <MyName>

// Copyright @2005 <MyCompany>

 

// =================================================================

// PROGRAM IDENTIFICATION

// =================================================================

 

#define DRV_BUILD           1

#define DRV_VERSION_HIGH    1

#define DRV_VERSION_LOW     0

 

// -----------------------------------------------------------------

 

#define DRV_DAY             07

#define DRV_MONTH           02

#define DRV_YEAR            2005

 

// -----------------------------------------------------------------

// Customize these settings by editing the configuration file

// D:/etc32/w2k_wiz.ini

 

#define DRV_MODULE          TestDrv

#define DRV_NAME            <SBS Windows 2000 Code Wizard Project>

#define DRV_COMPANY         <MyCompany>

#define DRV_AUTHOR          <MyName>

#define DRV_EMAIL           <my@email>

#define DRV_PREFIX          <MyPrefix>

 

// =================================================================

// HEADER FILES

// =================================================================

 

#include "drvinfo.h"        // defines more DRV_* items

 

#ifndef _RC_PASS_

 

// =================================================================

// CONSTANTS

// =================================================================

 

#define FILE_DEVICE_CUSTOM      0x8000

 

// =================================================================

// STRUCTURES

// =================================================================

 

typedef struct _DEVICE_CONTEXT

    {

    PDRIVER_OBJECT pDriverObject;

    PDEVICE_OBJECT pDeviceObject;

    }

    DEVICE_CONTEXT, *PDEVICE_CONTEXT, **PPDEVICE_CONTEXT;

 

#define DEVICE_CONTEXT_ sizeof (DEVICE_CONTEXT)

 

#endif // #ifndef _RC_PASS_

 

// =================================================================

// END OF FILE

// =================================================================

列表3-4.  驱动程序骨架的头文件

 
........................待续.........................

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值