驱动程序进阶篇

           我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。
           驱动程序分为2类,一个是 Kernel(内核) 模式驱动,另一个是 Windows (用户窗口层)模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。
           驱动程序同普通的 EXE,DLL 一样,都属于PE文件,而且都有一个入口函数。但EXE中,入口函数是main() / WinMain() 和 Unicode 的 wmain() / wWinmain(),DLL的入口函数则可有可无,它是DllMain()。

           所以驱动程序也有入口函数,而且是必须的,它是 DriverEntry(),再次提示,它是必须的,因为I/O管理器会首先调用驱动程序的DriverEntry(),它的作用就像DllMain()--完成一些初始化工作。

          虽然我们有时候把 DriverEntry 比作main(),但二者在本质上不同,DriverEntry 的生命周期非常短,其作用仅是将内核文件镜像加载到系统中时进行驱动初始化,调用结束后驱动程序的其他部分依旧存在,并不随它而终止。

          所以我们一般可把 DriverEntry 称为“入口函数”,而不可称为“主函数”。因此作为内核驱动来说,它没有一个明确的退出点,这应该是atexit无法在内核中实现的原因吧。

           DriverEntry()一共有2个参数:

           1)PDRIVER_OBJECT DriverObject,指向驱动程序对象的指针,我们操作驱动程序,全靠它,它是由 I/O 管理器传递进来的;

           2)PUNICODE_STRING RegistryPath,驱动程序的服务主键,这个参数的使用并不多,但要注意,在DriverEntry()返回后,它可能
会消失,所以如果需要使用,记住先要保存下来

            DriverEntry() 的返回一个 NTSTATUS 值,它是一个 ULONG 值,具体的定义,请参见 DDK 中的 NTSTATUS.H 头文件,里边有详细
的定义。
            既然要写驱动版的“Hello World”,就需要确定如何来与驱动程序通信,常用的共享内存,共享事件,IOCTL宏,或者直接用ReadFile() 或 WriteFile() 进行读写,在本文里我就采用一种简单的、但又很常用的 IOCTL 宏,它依赖的 IRP派遣例程是IRP_MJ_DEVICE_CONTROL,Win32程序使用 DeviceIoControl() 与驱动进行通信,根据不同的IOCTL宏,输出不同的调试信息。

            为了简便,我并没有使用 ReadFile() 将信息读出来,而是直接用 DbgPrint() 输出,所以需要使用 DbgView 查看,其他调试工具也可以。PS:偷懒!
            驱动程序与 I/O 管理器通信,使用的是 IRP,即 I/O 请求包。IRP 分为2部分:IRP 首部;IRP堆栈。

             IRP 首部信息如下:
             IRP 首部:
                      IO_STATUS_BLOCK IoStatus                 //包含 I/O 请求的状态
                      PVOID AssociatedIrp.SystemBuffer       //  如果执行缓冲区 I/O,这个指针指向系统缓冲区
                      PMDL MdlAddress                        //  如果直接 I/O,这个指针指向用户缓冲区的存储器描述符表
                      PVOID UserBuffer                         // I/O 缓冲区的用户空间地址
             IRP堆栈:
                      UCHAR MajorFunction              //(主要类型) 指示 IRP_MJ_XXX派遣例程
                      UCHAR MinorFunction              //(IRP 的子类型) 同上,一般文件系统和 SCSI 驱动程序使用它
             union Parameters                  //MajorFunction的联合类型
             {
              struct Read                       //IRP_MJ_READ的参数
              ULONG Length
              ULONG Key
              LARGE_INTEGER ByteOffset
              struct Write                      //IRP_MJ_WRITE的参数
              ULONG Length
              ULONG Key
              LARGE_INTEGER ByteOffset
              struct DeviceIoControl            //IRP_MJ_DEVICE_CONTROL和IRP_MJ_INTERNAL_DEVICE_CONTROL的参数
              ULONG OutputBufferLength
              ULONG InputBufferLength
              ULONG IoControlCode
              PVOID Type3InputBuffer
             } 
             PDEVICE_OBJECT DeviceObject       //请求的目标设备对象的指针
             PFILE_OBJECT FileObject           //请求的目标文件对象的指针,如果有的话
            操作 IRP。对于不同的 IRP 函数,操作也是不同的:有的只操作 IRP 首部;有的只操作 IRP 堆栈;还有操作 IRP 整体,
            下面是一些常用的函数:
            IRP整体:
             名称                                        描述                                             调用者
             IoStartPacket                    发送IRP到Start I/O例程                Dispatch (派遣)
             IoCompleteRequest       表示所有的处理完成                       DpcForIsr (连接中断和注册)
             IoStartNextPacket           发送下一个IRP到Start I/O例程     DpcForIsr
             IoCallDriver                       发送IRP请求                                    Dispatch
             IoAllocateIrp                    请求另外的IRP                                 Dispatch
             IoFreeIrp                           释放驱动程序分配的IRP                 I/O Completion (I/0完成)

            IRP堆栈:
            名称                                                     描述                                                        调用者
            IoGetCurrentIrpStackLocation      得到调用者堆栈的指针                           Dispatch
            IoMarkIrpPending                           为进一步的处理标记调用者I/O堆栈       Dispatch
            IoGetNextIrpStackLocation           得到下一个驱动程序的I/O堆栈的指针   Dispatch
            IoSetNextIrpStackLocation            将I/O堆栈指针压入堆栈                         Dispatc


           在驱动程序中,IRP 派遣例程起着很重要的作用,每个 IRP 派遣例程,几乎都有对应的Win32函数,下面是几个常用的:
            IRP派遣例程:
            名称                                                              描述                                             调用者
            IRP_MJ_CREATE                                         请求一个句柄                              CreateFile
            IRP_MJ_CLEANUP                                      在关闭句柄时取消悬挂的IRP     CloseHandle
            IRP_MJ_CLOSE                                           关闭句柄                                      CloseHandle
            IRP_MJ_READ                                             从设备得到数据                           ReadFile
            IRP_MJ_WRITE                                            传送数据到设备                           WriteFile
            IRP_MJ_DEVICE_CONTROL                       控制操作(利用IOCTL宏)        DeviceIoControl
            IRP_MJ_INTERNAL_DEVICE_CONTROL   控制操作(只能被内核调用)         N/A
            IRP_MJ_QUERY_INFORMATION              得到文件的长度                           GetFileSize
            IRP_MJ_SET_INFORMATION                     设置文件的长度                           SetFileSize
            IRP_MJ_FLUSH_BUFFERS                          写输出缓冲区或丢弃输入缓冲区 FlushFileBuffers FlushConsoleInputBuffer PurgeComm
            IRP_MJ_SHUTDOWN                                  系统关闭                                      InitiateSystemShutdown

=================================================================================================================================
          先介绍一下开始写我们的驱动版的“Hello World”的流程,程序很简单:
          1,用 DriverEntry 驱动入口函数来将内核文件加载到系统文件中进行驱动化。

          2,定义一个具有返回值的函数,来完成创建设备和相关功能等操作。

          3,调用  IoCreateDevice()  API函数来创建一个设备,并返回一个设备对象。

          4,调用  IoCreateSynbolicLink()  API函数来创建一个符号连接,使Win32程序可以使用驱动程序。      

          5,调用 RtlInitUnicodeString API 函数来初始化定义的设备名称和符号链接。

          6,调用 IoCreateSymbolicLink API 函数将符号链接和设备进行绑定。

          7,用 siwitch 语句来判断是否绑定成功,!NT_STATUS(Status)

          8,定义一个具有返回值的函数,来完成 IRP 的派遣和完成的操作。

          9,在在入口函数 DriverEntry 中注册派遣函数,设置 IRP_MJ_DEVICE_CONTROL 派遣例程 HelloWorldDispatch()和卸载例程 HelloWorldUnLoad()。如果 Win32 程序使用 DeviceIoControl(),则执行 HelloWorldDispatch() 函数。

          10,调用 IoGetCurrentIrpStackLocation() 得到当前调用者的IRP指针。

          11,定义一个无符号字符或无符号长整形的变量,来进行判断堆栈上的 IRP 的类型指针。

          12,然后创建一个应用程序,用来和驱动进行通信,比如控制台应用程序,在_tmain()函数中编写获取文件句柄的代码。

          13,调用 CreateFile 函数打开或创建一个文件,来得到这个文件的句柄。

          11,取得IO控制代码,完成后使用IoCompleteRequest()完成IRP操作,如果使用ControlService()停止驱动程序,则执行HelloWorldUnLoad()函数。

          10,调用IoDeleteSymbolicLink()删除符号连接。

          11,调用IoDeleteDevice()删除已建立的设备 。                

=================================================================================================================================

         下面介绍代码循序:
         驱动入口 DriverEntry()
          //创建设备
          IoCreateDevice(DriverObject,        //驱动程序对象
                                        0,                   //扩展设备的大小,由于不需要,所以置0
                                        &DeviceNameString,   //设备名称
                                        FILE_DEVICE_UNKNOWN, //设备类型
                                        0,                   //指示设备允许的操作
                                        FALSE,               //如果为TRUE,表示只能有一个线程使用该设备,为FALSE,则没有限制
                                        &lpDeviceObject);    //返回的设备对象

          //创建符号连接
           IoCreateSymbolicLink(&DeviceLinkString,   //存放符号连接的UNICODE_STRING
                                                    &DeviceNameString);  //设备名称


         //注册派遣例程和卸载例程
          DriverObject->MajorFunction[IRP_MJ_CREATE]=
          DriverObject->MajorFunction[IRP_MJ_CLOSE]=
          DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
          DriverObject->DriverUnload=HelloWorldUnLoad;
 

          IRP派遣例程HelloWorldDispatch()
          IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP堆栈

          //获取IO控制代码,并执行指定操作,这里只是DbgPrint()
          IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
          switch (IoControlCodes)

          {
            ......
          IoCompleteRequest(pIrp,IO_NO_INCREMENT);     //完成IRP操作

           卸载例程HelloWorldUnLoad()
           IoDeleteSymbolicLink(&DeviceLinkString);  //删除符号连接
           IoDeleteDevice(DriverObject->DeviceObject);   //删除设备


=================================================================================================================================
           完整代码:

  1. #ifndef __HELLOWORLD_C__
  2. #define __HELLOWORLD_C__
  3. #define DEBUGMSG
  4. #include <ntddk.h>
  5. #define DEVICE_HELLO_INDEX 0x860
  6. //2个IOCTL宏
  7. #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
  8. #define STOP_HELLPWORLD  CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
  9. #define NT_DEVICE_NAME L"\Device\HelloWorld"        //设备名称
  10. #define DOS_DEVICE_NAME L"\DosDevices\HelloWorld"   //符号连接
  11. NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);
  12. VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject);
  13. //驱动入口
  14. NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
  15. {
  16.     NTSTATUS ntStatus=STATUS_SUCCESS;
  17.     PDEVICE_OBJECT lpDeviceObject=NULL;       //指向设备对象的指针
  18.     UNICODE_STRING DeviceNameString={0};      //设备名称
  19.     UNICODE_STRING DeviceLinkString={0};      //符号连接
  20.     //调试信息
  21.     #ifdef DEBUGMSG
  22.            DbgPrint("Starting DriverEntry() ");
  23.     #endif
  24.     RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME);  //初始化Unicode字符串
  25.     //创建设备
  26.     ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,0,FALSE,&lpDeviceObject);
  27.     //使用NT_SUCCESS宏检测函数调用是否成功
  28.     if (!NT_SUCCESS(ntStatus))
  29.     {
  30.         #ifdef DEBUGMSG
  31.                DbgPrint("IoCreateDevice() error reports 0x%08X ",ntStatus);
  32.         #endif
  33.         return ntStatus;
  34.     }
  35.     RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
  36.     //创建符号连接
  37.     ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);
  38.     if (!NT_SUCCESS(ntStatus))
  39.     {
  40.         #ifdef DEBUGMSG
  41.                DbgPrint("IoCreateSymbolicLink() error reports 0x%08X ",ntStatus);
  42.         #endif
  43.         if (lpDeviceObject)
  44.             IoDeleteDevice(lpDeviceObject);
  45.         return ntStatus;
  46.     }
  47.     //设置IRP派遣例程和卸载例程
  48.     DriverObject->MajorFunction[IRP_MJ_CREATE]=
  49.     DriverObject->MajorFunction[IRP_MJ_CLOSE]=
  50.     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
  51.     DriverObject->DriverUnload=HelloWorldUnLoad;
  52.     return ntStatus;
  53. }
  54. NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
  55. {
  56.     NTSTATUS ntStatus=STATUS_SUCCESS;
  57.     PIO_STACK_LOCATION IrpStack=NULL;   //IRP堆栈
  58.     ULONG IoControlCodes=0;             //I/O控制代码
  59.     //设置IRP状态
  60.     pIrp->IoStatus.Status=STATUS_SUCCESS;
  61.     pIrp->IoStatus.Information=0;
  62.     #ifdef DEBUGMSG
  63.            DbgPrint("Starting HelloWorldDispatch() ");
  64.     #endif
  65.     IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP
  66.     switch (IrpStack->MajorFunction)
  67.     {
  68.             case IRP_MJ_CREATE:
  69.                  #ifdef DEBUGMSG
  70.                         DbgPrint("IRP_MJ_CREATE ");
  71.                  #endif
  72.                  break;
  73.             case IRP_MJ_CLOSE:
  74.                  #ifdef DEBUGMSG
  75.                         DbgPrint("IRP_MJ_CLOSE ");
  76.                  #endif
  77.                  break;
  78.             case IRP_MJ_DEVICE_CONTROL:
  79.                  #ifdef DEBUGMSG
  80.                         DbgPrint("IRP_MJ_DEVICE_CONTROL ");
  81.                  #endif
  82.                  //取得I/O控制代码
  83.                  IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
  84.                  switch (IoControlCodes)
  85.                  {
  86.                          //启动
  87.                          case START_HELLPWORLD:
  88.                               DbgPrint("Starting "Hello World" ");
  89.                               break;
  90.                          //停止
  91.                          case STOP_HELLPWORLD:
  92.                               DbgPrint("Stoping "Hello World" ");
  93.                               break;
  94.                          default:
  95.                               pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;
  96.                               break;
  97.                  }
  98.                  break;
  99.             default:
  100.                  break;
  101.     }
  102.     ntStatus=pIrp->IoStatus.Status;
  103.     IoCompleteRequest(pIrp,IO_NO_INCREMENT);
  104.     return ntStatus;
  105. }
  106. VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject)
  107. {
  108.      UNICODE_STRING DeviceLinkString={0};
  109.      PDEVICE_OBJECT DeviceObjectTemp1=NULL;
  110.      PDEVICE_OBJECT DeviceObjectTemp2=NULL;
  111.      #ifdef DEBUGMSG
  112.             DbgPrint("Starting HelloWorldUnLoad() ");
  113.      #endif
  114.      RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
  115.      if (DeviceLinkString.Buffer)
  116.          IoDeleteSymbolicLink(&DeviceLinkString);
  117.      if (DriverObject)
  118.      {
  119.          DeviceObjectTemp1=DriverObject->DeviceObject;
  120.          while (DeviceObjectTemp1)
  121.          {
  122.                 DeviceObjectTemp2=DeviceObjectTemp1;
  123.                 DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
  124.                 IoDeleteDevice(DeviceObjectTemp2);
  125.          }
  126.      }
  127. }
  128. #endif

用户态程序:
  1. #define DEBUGMSG
  2. #include <windows.h>
  3. #include <winioctl.h>
  4. #include <stdio.h>
  5. #define DEVICE_FILTER_INDEX 0x860
  6. #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
  7. #define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
  8. #define erron GetLastError()
  9. #define MY_DEVICE_NAME "\\.\HelloWorld"
  10. #define MY_DEVICE_START "-start"
  11. #define MY_DEVICE_STOP "-stop"
  12. BOOL DriverControl (TCHAR *Maik);
  13. void Usage (TCHAR *Paramerter);
  14. int main (int argc,TCHAR *argv[])
  15. {
  16.     if (argc!=2)
  17.     {
  18.         Usage(argv[0]);
  19.         return 0;
  20.     }
  21.     if (strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0)
  22.         DriverControl(argv[1]);
  23.     else
  24.     {
  25.         Usage(argv[0]);
  26.         return 0;
  27.     }
  28.     return 0;
  29. }
  30. BOOL DriverControl (TCHAR *Maik)
  31. {
  32.      HANDLE hDevice=NULL;  //设备句柄
  33.      //获得设备句柄
  34.      hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  35.      if (hDevice==INVALID_HANDLE_VALUE)
  36.      {
  37.          #ifdef DEBUGMSG
  38.                 printf("CreateFile() GetLastError reports %d ",erron);
  39.          #endif
  40.          return FALSE;
  41.      }
  42.      //启动
  43.      if (strcmpi(Maik,MY_DEVICE_START)==0)
  44.      {
  45.          //传递启动的I/O控制代码
  46.          if (!(DeviceIoControl(hDevice,START_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))
  47.          {
  48.              #ifdef DEBUGMSG
  49.                     printf("DeviceIoControl() GetLastError reports %d ",erron);
  50.              #endif
  51.              CloseHandle(hDevice);
  52.              return FALSE;
  53.          }
  54.      }
  55.      //停止
  56.      if (strcmpi(Maik,MY_DEVICE_STOP)==0)
  57.      {
  58.          //传递停止的I/O控制代码
  59.          if (!(DeviceIoControl(hDevice,STOP_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))
  60.          {
  61.              #ifdef DEBUGMSG
  62.                     printf("DeviceIoControl() GetLastError reports %d ",erron);
  63.              #endif
  64.              CloseHandle(hDevice);
  65.              return FALSE;
  66.          }
  67.      }
  68.      if (hDevice)
  69.          CloseHandle(hDevice);  //关闭句柄
  70.      return TRUE;
  71. }
  72. void Usage (TCHAR *Paramerter)
  73. {
  74.      fprintf(stderr,"============================================================================ "
  75.              "      驱动版Hello World "
  76.              "作者:dahubaobao[E.S.T] "
  77.              "主页:www.eviloctal.com "
  78.              "OICQ:382690 "
  79.              "%s -start 启动 "
  80.              "%s -stop 停止 "
  81.              "本程序只是用做代码交流,如有错误,还请多多包含! "
  82.              "============================================================================ "
  83.              ,Paramerter,Paramerter);
  84. }
原文地址:https://www.cnblogs.com/lfls128/p/4975699.html