NT驱动程序和WDM驱动程序的区别

时间:2022-04-12 19:49:18
1. Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。


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


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


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

[cpp] view plain copy
  1. #ifdef  __cplusplus    
  2. extern  “C”    
  3. {    
  4. #endif    
  5.   
  6. #include <NTDDK.H>    
  7.   
  8. #ifdef  __cplusplus    
  9. }    
  10. #endif    
  11.     
  12. #pragma  INITCODE  extern  “C”  NTSTATUS   DriverEntry(IN  PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING  pRegistryPath){}  


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

Driver.h:

[cpp] view plain copy
  1. #pragma once  
  2.   
  3.   
  4. #ifdef __cplusplus  
  5.   
  6. extern "C"  
  7.   
  8. {  
  9.   
  10. #endif  
  11.   
  12. #include <NTDDK.h>  
  13.   
  14. #ifdef __cplusplus  
  15.   
  16. }  
  17.   
  18. #endif   
  19.   
  20.   
  21.   
  22. #define PAGEDCODE code_seg("PAGE")  
  23.   
  24. #define LOCKEDCODE code_seg()  
  25.   
  26. #define INITCODE code_seg("INIT")  
  27.   
  28.   
  29.   
  30. #define PAGEDDATA data_seg("PAGE")  
  31.   
  32. #define LOCKEDDATA data_seg()  
  33.   
  34. #define INITDATA data_seg("INIT")  
  35.   
  36.   
  37.   
  38. #define arraysize(p) (sizeof(p)/sizeof((p)[0]))  
  39.   
  40.   
  41. typedef struct _DEVICE_EXTENSION {  
  42.   
  43.     PDEVICE_OBJECT pDevice;  
  44.   
  45.     UNICODE_STRING ustrDeviceName;    //设备名称  
  46.   
  47.     UNICODE_STRING ustrSymLinkName;    //符号链接名  
  48.   
  49. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;  
  50.   
  51.   
  52.   
  53. // 驱动函数声明  
  54.   
  55. NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);  
  56.   
  57. VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);  
  58.   
  59. NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,  
  60.   
  61.                                  IN PIRP pIrp);  


driver.cpp

[cpp] view plain copy
  1. /************************************************************************ 
  2.  
  3. * 函数名称:DriverEntry 
  4.  
  5. * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 
  6.  
  7. * 参数列表: 
  8.  
  9.       pDriverObject:从I/O管理器中传进来的驱动对象 
  10.  
  11.       pRegistryPath:驱动程序在注册表的中的路径 
  12.  
  13. * 返回 值:返回初始化驱动状态 
  14.  
  15. *************************************************************************/  
  16.   
  17. #pragma INITCODE  
  18.   
  19. extern "C" NTSTATUS DriverEntry (  
  20.   
  21.             IN PDRIVER_OBJECT pDriverObject,  
  22.   
  23.             IN PUNICODE_STRING pRegistryPath    )   
  24.   
  25. {  
  26.   
  27.     NTSTATUS status;  
  28.   
  29.     KdPrint(("Enter DriverEntry\n"));  
  30.   
  31.   
  32.   
  33.     //注册其他驱动调用函数入口  
  34.   
  35.     pDriverObject->DriverUnload = HelloDDKUnload;  
  36.   
  37.     pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;  
  38.   
  39.     pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;  
  40.   
  41.     pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;  
  42.   
  43.     pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;  
  44.   
  45.       
  46.   
  47.     //创建驱动设备对象  
  48.   
  49.     status = CreateDevice(pDriverObject);  
  50.   
  51.   
  52.   
  53.     KdPrint(("DriverEntry end\n"));  
  54.   
  55.     return status;  
  56.   
  57. }  
  58.   
  59.   
  60.   
  61. /************************************************************************ 
  62.  
  63. * 函数名称:CreateDevice 
  64.  
  65. * 功能描述:初始化设备对象 
  66.  
  67. * 参数列表: 
  68.  
  69.       pDriverObject:从I/O管理器中传进来的驱动对象 
  70.  
  71. * 返回 值:返回初始化状态 
  72.  
  73. *************************************************************************/  
  74.   
  75. #pragma INITCODE   //指明此函数加载到INIT内存区域(即只在加载的时候需要载入内存,加载成功后可以从内存中卸载掉)  
  76.   
  77. NTSTATUS CreateDevice (  
  78.   
  79.         IN PDRIVER_OBJECT    pDriverObject)   
  80.   
  81. {  
  82.   
  83.     NTSTATUS status;  
  84.   
  85.     PDEVICE_OBJECT pDevObj;  
  86.   
  87.     PDEVICE_EXTENSION pDevExt;  
  88.   
  89.       
  90.   
  91.     //创建设备名称  
  92.   
  93.     UNICODE_STRING devName;  
  94.   
  95.     RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");  
  96.   
  97.       
  98.   
  99.     //创建设备  
  100.   
  101.     status = IoCreateDevice( pDriverObject,  
  102.   
  103.                         sizeof(DEVICE_EXTENSION),  
  104.   
  105.                         &(UNICODE_STRING)devName,  
  106.   
  107.                         FILE_DEVICE_UNKNOWN,//此种设备为独占设备  
  108.   
  109.                         0, TRUE,  
  110.   
  111.                         &pDevObj );  
  112.   
  113.     if (!NT_SUCCESS(status))  
  114.   
  115.         return status;  
  116.   
  117.   
  118.   
  119.     pDevObj->Flags |= DO_BUFFERED_IO;  
  120.   
  121.     pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;  
  122.   
  123.     pDevExt->pDevice = pDevObj;  
  124.   
  125.     pDevExt->ustrDeviceName = devName;  
  126.   
  127.     //创建符号链接  
  128.   
  129.     UNICODE_STRING symLinkName;  
  130.   
  131.     RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");  
  132.   
  133.     pDevExt->ustrSymLinkName = symLinkName;  
  134.   
  135.     status = IoCreateSymbolicLink( &symLinkName,&devName );  
  136.   
  137.     if (!NT_SUCCESS(status))   
  138.   
  139.     {  
  140.   
  141.         IoDeleteDevice( pDevObj );  
  142.   
  143.         return status;  
  144.   
  145.     }  
  146.   
  147.     return STATUS_SUCCESS;  
  148.   
  149. }  
  150.   
  151.   
  152.   
  153. /************************************************************************ 
  154.  
  155. * 函数名称:HelloDDKUnload 
  156.  
  157. * 功能描述:负责驱动程序的卸载操作 
  158.  
  159. * 参数列表: 
  160.  
  161.       pDriverObject:驱动对象 
  162.  
  163. * 返回 值:返回状态 
  164.  
  165. *************************************************************************/  
  166.   
  167. #pragma PAGEDCODE  
  168.   
  169. VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)   
  170.   
  171. {//遍历系统中所有的此类设备对象,删除设备对象及其符号链接  
  172.   
  173.     PDEVICE_OBJECT    pNextObj;  
  174.   
  175.     KdPrint(("Enter DriverUnload\n"));  
  176.   
  177.     pNextObj = pDriverObject->DeviceObject;  
  178.   
  179.     while (pNextObj != NULL)   
  180.   
  181.     {  
  182.   
  183.         PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)  
  184.   
  185.             pNextObj->DeviceExtension;  
  186.   
  187.   
  188.   
  189.         //删除符号链接  
  190.   
  191.         UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;  
  192.   
  193.         IoDeleteSymbolicLink(&pLinkName);  
  194.   
  195.         pNextObj = pNextObj->NextDevice;  
  196.   
  197.         IoDeleteDevice( pDevExt->pDevice );  
  198.   
  199.     }  
  200.   
  201. }  
  202.   
  203.   
  204.   
  205. /************************************************************************ 
  206.  
  207. * 函数名称:HelloDDKDispatchRoutine 
  208.  
  209. * 功能描述:对读IRP进行处理 
  210.  
  211. * 参数列表: 
  212.  
  213.       pDevObj:功能设备对象 
  214.  
  215.       pIrp:从IO请求包 
  216.  
  217. * 返回 值:返回状态 
  218.  
  219. *************************************************************************/  
  220.   
  221. #pragma PAGEDCODE  
  222.   
  223. NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,  
  224.   
  225.                                  IN PIRP pIrp)   
  226.   
  227. {  
  228.   
  229.     KdPrint(("Enter HelloDDKDispatchRoutine\n"));  
  230.   
  231.     NTSTATUS status = STATUS_SUCCESS;  
  232.   
  233.     // 完成IRP  
  234.   
  235.     pIrp->IoStatus.Status = status;  
  236.   
  237.     pIrp->IoStatus.Information = 0;    // bytes xfered  
  238.   
  239.     IoCompleteRequest( pIrp, IO_NO_INCREMENT );  
  240.   
  241.     KdPrint(("Leave HelloDDKDispatchRoutine\n"));  
  242.   
  243.     return status;  
  244.   
  245. }  
驱动虽然有了设备名称,但是这种设备名只能在内核态可见,而对于应用程序时不可见的。因此,驱动需要暴露一个符号链接,该链接指向真正的设备名称。
编译好生成的sys文件,我们可以使用 DriverMonitor 加载,加载->启动->停止->卸载。


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

[cpp] view plain copy
  1. /************************************************************************ 
  2. * 函数名称:DriverEntry 
  3. * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 
  4. * 参数列表: 
  5.       pDriverObject:从I/O管理器中传进来的驱动对象 
  6.       pRegistryPath:驱动程序在注册表的中的路径 
  7. * 返回 值:返回初始化驱动状态 
  8. *************************************************************************/  
  9. #pragma INITCODE   
  10. extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,  
  11.                                 IN PUNICODE_STRING pRegistryPath)  
  12. {  
  13.     KdPrint(("Enter DriverEntry\n"));  
  14.   
  15.     pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;  
  16.     pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;  
  17.     pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =   
  18.     pDriverObject->MajorFunction[IRP_MJ_CREATE] =   
  19.     pDriverObject->MajorFunction[IRP_MJ_READ] =   
  20.     pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;  
  21.     pDriverObject->DriverUnload = HelloWDMUnload;  
  22.   
  23.     KdPrint(("Leave DriverEntry\n"));  
  24.     return STATUS_SUCCESS;  
  25. }  
这里增加了一个设置AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP(即插即用)管理器调用。并设置对IRP_MJ_PNP的IRP的回调函数。这都是NT和WDM驱动最大的不同点。而且在WDM驱动中,大部分卸载工作都不是由DriverUnload来处理,而是放在对IRP_MN_REMOVE_DEVICE的IRP的处理函数中处理。


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

[cpp] view plain copy
  1. /************************************************************************ 
  2. * 函数名称:HelloWDMAddDevice 
  3. * 功能描述:添加新设备 
  4. * 参数列表: 
  5.       DriverObject:从I/O管理器中传进来的驱动对象 
  6.       PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象 
  7. * 返回 值:返回添加新设备状态 
  8. *************************************************************************/  
  9. #pragma PAGEDCODE  
  10. NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,  
  11.                            IN PDEVICE_OBJECT PhysicalDeviceObject)  
  12. {   
  13.     PAGED_CODE();  
  14.     KdPrint(("Enter HelloWDMAddDevice\n"));  
  15.   
  16.     NTSTATUS status;  
  17.     PDEVICE_OBJECT fdo;  
  18.     UNICODE_STRING devName;  
  19.     RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");  
  20.     status = IoCreateDevice(  
  21.         DriverObject,  
  22.         sizeof(DEVICE_EXTENSION),  
  23.         &(UNICODE_STRING)devName,  
  24.         FILE_DEVICE_UNKNOWN,  
  25.         0,  
  26.         FALSE,  
  27.         &fdo);  
  28.     if( !NT_SUCCESS(status))  
  29.         return status;  
  30.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  31.     pdx->fdo = fdo;  
  32.     pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);  
  33.     UNICODE_STRING symLinkName;  
  34.     RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");  
  35.   
  36.     pdx->ustrDeviceName = devName;  
  37.     pdx->ustrSymLinkName = symLinkName;  
  38.     status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);  
  39.   
  40.     if( !NT_SUCCESS(status))  
  41.     {  
  42.         IoDeleteSymbolicLink(&pdx->ustrSymLinkName);  
  43.         status = IoCreateSymbolicLink(&symLinkName,&devName);  
  44.         if( !NT_SUCCESS(status))  
  45.         {  
  46.             return status;  
  47.         }  
  48.     }  
  49.   
  50.     fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;  
  51.     fdo->Flags &= ~DO_DEVICE_INITIALIZING;  
  52.   
  53.     KdPrint(("Leave HelloWDMAddDevice\n"));  
  54.     return STATUS_SUCCESS;  
  55. }  
其中PAGED_CODE是一个DDK提供的宏,只在check版中有效。当此例程所在的中断请求及超过APC_LEVEL时,会产生一个断言。


本例对IRP_MN_REMOVE_DEVICE的处理。

[cpp] view plain copy
  1. /************************************************************************ 
  2. * 函数名称:HandleRemoveDevice 
  3. * 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理 
  4. * 参数列表: 
  5.       fdo:功能设备对象 
  6.       Irp:从IO请求包 
  7. * 返回 值:返回状态 
  8. *************************************************************************/  
  9. #pragma PAGEDCODE  
  10. NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)  
  11. {  
  12.     PAGED_CODE();  
  13.     KdPrint(("Enter HandleRemoveDevice\n"));  
  14.   
  15.     Irp->IoStatus.Status = STATUS_SUCCESS;  
  16.     NTSTATUS status = DefaultPnpHandler(pdx, Irp);  
  17.     IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);  
  18.   
  19.     //调用IoDetachDevice()把fdo从设备栈中脱开:  
  20.     if (pdx->NextStackDevice)  
  21.         IoDetachDevice(pdx->NextStackDevice);  
  22.       
  23.     //删除fdo:  
  24.     IoDeleteDevice(pdx->fdo);  
  25.     KdPrint(("Leave HandleRemoveDevice\n"));  
  26.     return status;  
  27. }  
而对卸载例程的处理可以什么都不用做。


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

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