zoukankan      html  css  js  c++  java
  • 驱动程序进阶篇

               我们学习程序设计,都是从“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. }
  • 相关阅读:
    谷歌的 I/O 2019,究竟推出了什么新特性?
    Flutter交互实战-即刻App探索页下拉&拖拽效果
    5G到来,App的未来,是JavaScript,Flutter还是Native ?
    python爬虫-房天下-登录
    python爬虫-有道翻译-js加密破解
    虾米音乐爬虫
    Golang 读写文件
    Golang-使用md5对字符串进行加密
    Golang-使用mysql
    Golang 传递任意类型的切片
  • 原文地址:https://www.cnblogs.com/lfls128/p/4975699.html
Copyright © 2011-2022 走看看