NT式驱动和WDM式驱动程序

1.Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。


2.NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的驱动要导入的头文件为WDM.H.


3.DriverEntry需要放在INIT标志的内存中。INIT标志指明该函数只是在加载的时候需要载入内存,而当驱动程序加载成功后,该函数可以从内存中卸载掉。


4.C++编写驱动需要注意:

#ifdef  __cplusplus  
extern  “C”  
{  
#endif  

#include <NTDDK.H>  

#ifdef  __cplusplus  
}  
#endif  
  
#pragma  INITCODE  extern  “C”  NTSTATUS   DriverEntry(IN  PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING  pRegistryPath){}


5.来看一个简单的NT式驱动

Driver.h:

#pragma once


#ifdef __cplusplus

extern "C"

{

#endif

#include <NTDDK.h>

#ifdef __cplusplus

}

#endif 



#define PAGEDCODE code_seg("PAGE")

#define LOCKEDCODE code_seg()

#define INITCODE code_seg("INIT")



#define PAGEDDATA data_seg("PAGE")

#define LOCKEDDATA data_seg()

#define INITDATA data_seg("INIT")



#define arraysize(p) (sizeof(p)/sizeof((p)[0]))


typedef struct _DEVICE_EXTENSION {

    PDEVICE_OBJECT pDevice;

    UNICODE_STRING ustrDeviceName;    //设备名称

    UNICODE_STRING ustrSymLinkName;    //符号链接名

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;



// 驱动函数声明

NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);

VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

                                 IN PIRP pIrp);


driver.cpp

/************************************************************************

* 函数名称:DriverEntry

* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象

* 参数列表:

      pDriverObject:从I/O管理器中传进来的驱动对象

      pRegistryPath:驱动程序在注册表的中的路径

* 返回 值:返回初始化驱动状态

*************************************************************************/

#pragma INITCODE

extern "C" NTSTATUS DriverEntry (

            IN PDRIVER_OBJECT pDriverObject,

            IN PUNICODE_STRING pRegistryPath    ) 

{

    NTSTATUS status;

    KdPrint(("Enter DriverEntry\n"));



    //注册其他驱动调用函数入口

    pDriverObject->DriverUnload = HelloDDKUnload;

    pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;

    pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;

    

    //创建驱动设备对象

    status = CreateDevice(pDriverObject);



    KdPrint(("DriverEntry end\n"));

    return status;

}



/************************************************************************

* 函数名称:CreateDevice

* 功能描述:初始化设备对象

* 参数列表:

      pDriverObject:从I/O管理器中传进来的驱动对象

* 返回 值:返回初始化状态

*************************************************************************/

#pragma INITCODE   //指明此函数加载到INIT内存区域(即只在加载的时候需要载入内存,加载成功后可以从内存中卸载掉)

NTSTATUS CreateDevice (

        IN PDRIVER_OBJECT    pDriverObject) 

{

    NTSTATUS status;

    PDEVICE_OBJECT pDevObj;

    PDEVICE_EXTENSION pDevExt;

    

    //创建设备名称

    UNICODE_STRING devName;

    RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

    

    //创建设备

    status = IoCreateDevice( pDriverObject,

                        sizeof(DEVICE_EXTENSION),

                        &(UNICODE_STRING)devName,

                        FILE_DEVICE_UNKNOWN,//此种设备为独占设备

                        0, TRUE,

                        &pDevObj );

    if (!NT_SUCCESS(status))

        return status;



    pDevObj->Flags |= DO_BUFFERED_IO;

    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

    pDevExt->pDevice = pDevObj;

    pDevExt->ustrDeviceName = devName;

    //创建符号链接

    UNICODE_STRING symLinkName;

    RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");

    pDevExt->ustrSymLinkName = symLinkName;

    status = IoCreateSymbolicLink( &symLinkName,&devName );

    if (!NT_SUCCESS(status)) 

    {

        IoDeleteDevice( pDevObj );

        return status;

    }

    return STATUS_SUCCESS;

}



/************************************************************************

* 函数名称:HelloDDKUnload

* 功能描述:负责驱动程序的卸载操作

* 参数列表:

      pDriverObject:驱动对象

* 返回 值:返回状态

*************************************************************************/

#pragma PAGEDCODE

VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject) 

{//遍历系统中所有的此类设备对象,删除设备对象及其符号链接

    PDEVICE_OBJECT    pNextObj;

    KdPrint(("Enter DriverUnload\n"));

    pNextObj = pDriverObject->DeviceObject;

    while (pNextObj != NULL) 

    {

        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)

            pNextObj->DeviceExtension;



        //删除符号链接

        UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;

        IoDeleteSymbolicLink(&pLinkName);

        pNextObj = pNextObj->NextDevice;

        IoDeleteDevice( pDevExt->pDevice );

    }

}



/************************************************************************

* 函数名称:HelloDDKDispatchRoutine

* 功能描述:对读IRP进行处理

* 参数列表:

      pDevObj:功能设备对象

      pIrp:从IO请求包

* 返回 值:返回状态

*************************************************************************/

#pragma PAGEDCODE

NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,

                                 IN PIRP pIrp) 

{

    KdPrint(("Enter HelloDDKDispatchRoutine\n"));

    NTSTATUS status = STATUS_SUCCESS;

    // 完成IRP

    pIrp->IoStatus.Status = status;

    pIrp->IoStatus.Information = 0;    // bytes xfered

    IoCompleteRequest( pIrp, IO_NO_INCREMENT );

    KdPrint(("Leave HelloDDKDispatchRoutine\n"));

    return status;

}
驱动虽然有了设备名称,但是这种设备名只能在内核态可见,而对于应用程序时不可见的。因此,驱动需要暴露一个符号链接,该链接指向真正的设备名称。
编译好生成的sys文件,我们可以使用 DriverMonitor加载,加载->启动->停止->卸载。


6.再来看看WDM式驱动有什么不同。头文件就直接忽略了。
DriverEntry入口函数:

/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
      pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE 
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
                                IN PUNICODE_STRING pRegistryPath)
{
    KdPrint(("Enter DriverEntry\n"));

    pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
    pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = 
    pDriverObject->MajorFunction[IRP_MJ_READ] = 
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
    pDriverObject->DriverUnload = HelloWDMUnload;

    KdPrint(("Leave DriverEntry\n"));
    return STATUS_SUCCESS;
}
这里增加了一个设置AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP(即插即用)管理器调用。并设置对IRP_MJ_PNP的IRP的回调函数。这都是NT和WDM驱动最大的不同点。而且在WDM驱动中,大部分卸载工作都不是由DriverUnload来处理,而是放在对IRP_MN_REMOVE_DEVICE的IRP的处理函数中处理。


下面是AddDevice回调函数的处理。

/************************************************************************
* 函数名称:HelloWDMAddDevice
* 功能描述:添加新设备
* 参数列表:
      DriverObject:从I/O管理器中传进来的驱动对象
      PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
* 返回 值:返回添加新设备状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{ 
    PAGED_CODE();
    KdPrint(("Enter HelloWDMAddDevice\n"));

    NTSTATUS status;
    PDEVICE_OBJECT fdo;
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");
    status = IoCreateDevice(
        DriverObject,
        sizeof(DEVICE_EXTENSION),
        &(UNICODE_STRING)devName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &fdo);
    if( !NT_SUCCESS(status))
        return status;
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
    pdx->fdo = fdo;
    pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");

    pdx->ustrDeviceName = devName;
    pdx->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);

    if( !NT_SUCCESS(status))
    {
        IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
        status = IoCreateSymbolicLink(&symLinkName,&devName);
        if( !NT_SUCCESS(status))
        {
            return status;
        }
    }

    fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
    fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    KdPrint(("Leave HelloWDMAddDevice\n"));
    return STATUS_SUCCESS;
}
其中PAGED_CODE是一个DDK提供的宏,只在check版中有效。当此例程所在的中断请求及超过APC_LEVEL时,会产生一个断言。


本例对IRP_MN_REMOVE_DEVICE的处理。

/************************************************************************
* 函数名称:HandleRemoveDevice
* 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理
* 参数列表:
      fdo:功能设备对象
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
    PAGED_CODE();
    KdPrint(("Enter HandleRemoveDevice\n"));

    Irp->IoStatus.Status = STATUS_SUCCESS;
    NTSTATUS status = DefaultPnpHandler(pdx, Irp);
    IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);

    //调用IoDetachDevice()把fdo从设备栈中脱开:
    if (pdx->NextStackDevice)
        IoDetachDevice(pdx->NextStackDevice);
    
    //删除fdo:
    IoDeleteDevice(pdx->fdo);
    KdPrint(("Leave HandleRemoveDevice\n"));
    return status;
}
而对卸载例程的处理可以什么都不用做。


其他PNP的IRP如果不需要做处理,那么直接传递到底层驱动,并将底层驱动的结果返回。

/************************************************************************
* 函数名称:DefaultPnpHandler
* 功能描述:对PNP IRP进行缺省处理
* 参数列表:
      pdx:设备对象的扩展
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/ 
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
    PAGED_CODE();
    KdPrint(("Enter DefaultPnpHandler\n"));
    IoSkipCurrentIrpStackLocation(Irp);
    KdPrint(("Leave DefaultPnpHandler\n"));
    return IoCallDriver(pdx->NextStackDevice, Irp);
}
安装WDM式驱动需要一个inf文件。inf文件描述了WDM驱动程序的操作硬件设备的信息和驱动程序的一些信息。
可以直接右击这个inf文件进行安装即可。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值