驱动程序进阶篇

           我们学习程序设计,都是从“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);   //删除设备


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

#ifndef __HELLOWORLD_C__ #define __HELLOWORLD_C__ #define DEBUGMSG #include <ntddk.h> #define DEVICE_HELLO_INDEX 0x860 //2个IOCTL宏 #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS) #define STOP_HELLPWORLD  CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS) #define NT_DEVICE_NAME L"\\Device\\HelloWorld"        //设备名称 #define DOS_DEVICE_NAME L"\\DosDevices\\HelloWorld"   //符号连接 NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp); VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject); //驱动入口 NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) {     NTSTATUS ntStatus=STATUS_SUCCESS;     PDEVICE_OBJECT lpDeviceObject=NULL;       //指向设备对象的指针     UNICODE_STRING DeviceNameString={0};      //设备名称     UNICODE_STRING DeviceLinkString={0};      //符号连接     //调试信息     #ifdef DEBUGMSG            DbgPrint("Starting DriverEntry()\n");     #endif     RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME);  //初始化Unicode字符串     //创建设备     ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,0,FALSE,&lpDeviceObject);     //使用NT_SUCCESS宏检测函数调用是否成功     if (!NT_SUCCESS(ntStatus))     {         #ifdef DEBUGMSG                DbgPrint("IoCreateDevice() error reports 0x%08X\n",ntStatus);         #endif         return ntStatus;     }     RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);     //创建符号连接     ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);     if (!NT_SUCCESS(ntStatus))     {         #ifdef DEBUGMSG                DbgPrint("IoCreateSymbolicLink() error reports 0x%08X\n",ntStatus);         #endif         if (lpDeviceObject)             IoDeleteDevice(lpDeviceObject);         return ntStatus;     }     //设置IRP派遣例程和卸载例程     DriverObject->MajorFunction[IRP_MJ_CREATE]=     DriverObject->MajorFunction[IRP_MJ_CLOSE]=     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;     DriverObject->DriverUnload=HelloWorldUnLoad;     return ntStatus; } NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp) {     NTSTATUS ntStatus=STATUS_SUCCESS;     PIO_STACK_LOCATION IrpStack=NULL;   //IRP堆栈     ULONG IoControlCodes=0;             //I/O控制代码     //设置IRP状态     pIrp->IoStatus.Status=STATUS_SUCCESS;     pIrp->IoStatus.Information=0;     #ifdef DEBUGMSG            DbgPrint("Starting HelloWorldDispatch()\n");     #endif     IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP     switch (IrpStack->MajorFunction)     {             case IRP_MJ_CREATE:                  #ifdef DEBUGMSG                         DbgPrint("IRP_MJ_CREATE\n");                  #endif                  break;             case IRP_MJ_CLOSE:                  #ifdef DEBUGMSG                         DbgPrint("IRP_MJ_CLOSE\n");                  #endif                  break;             case IRP_MJ_DEVICE_CONTROL:                  #ifdef DEBUGMSG                         DbgPrint("IRP_MJ_DEVICE_CONTROL\n");                  #endif                  //取得I/O控制代码                  IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;                  switch (IoControlCodes)                  {                          //启动                          case START_HELLPWORLD:                               DbgPrint("Starting \"Hello World\"\n");                               break;                          //停止                          case STOP_HELLPWORLD:                               DbgPrint("Stoping \"Hello World\"\n");                               break;                          default:                               pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;                               break;                  }                  break;             default:                  break;     }     ntStatus=pIrp->IoStatus.Status;     IoCompleteRequest(pIrp,IO_NO_INCREMENT);     return ntStatus; } VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject) {      UNICODE_STRING DeviceLinkString={0};      PDEVICE_OBJECT DeviceObjectTemp1=NULL;      PDEVICE_OBJECT DeviceObjectTemp2=NULL;      #ifdef DEBUGMSG             DbgPrint("Starting HelloWorldUnLoad()\n");      #endif      RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);      if (DeviceLinkString.Buffer)          IoDeleteSymbolicLink(&DeviceLinkString);      if (DriverObject)      {          DeviceObjectTemp1=DriverObject->DeviceObject;          while (DeviceObjectTemp1)          {                 DeviceObjectTemp2=DeviceObjectTemp1;                 DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;                 IoDeleteDevice(DeviceObjectTemp2);          }      } } #endif
用户态程序:
#define DEBUGMSG #include <windows.h> #include <winioctl.h> #include <stdio.h> #define DEVICE_FILTER_INDEX 0x860 #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS) #define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS) #define erron GetLastError() #define MY_DEVICE_NAME "\\\\.\\HelloWorld" #define MY_DEVICE_START "-start" #define MY_DEVICE_STOP "-stop" BOOL DriverControl (TCHAR *Maik); void Usage (TCHAR *Paramerter); int main (int argc,TCHAR *argv[]) {     if (argc!=2)     {         Usage(argv[0]);         return 0;     }     if (strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0)         DriverControl(argv[1]);     else     {         Usage(argv[0]);         return 0;     }     return 0; } BOOL DriverControl (TCHAR *Maik) {      HANDLE hDevice=NULL;  //设备句柄      //获得设备句柄      hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);      if (hDevice==INVALID_HANDLE_VALUE)      {          #ifdef DEBUGMSG                 printf("CreateFile() GetLastError reports %d\n",erron);          #endif          return FALSE;      }      //启动      if (strcmpi(Maik,MY_DEVICE_START)==0)      {          //传递启动的I/O控制代码          if (!(DeviceIoControl(hDevice,START_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))          {              #ifdef DEBUGMSG                     printf("DeviceIoControl() GetLastError reports %d\n",erron);              #endif              CloseHandle(hDevice);              return FALSE;          }      }      //停止      if (strcmpi(Maik,MY_DEVICE_STOP)==0)      {          //传递停止的I/O控制代码          if (!(DeviceIoControl(hDevice,STOP_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))          {              #ifdef DEBUGMSG                     printf("DeviceIoControl() GetLastError reports %d\n",erron);              #endif              CloseHandle(hDevice);              return FALSE;          }      }      if (hDevice)          CloseHandle(hDevice);  //关闭句柄      return TRUE; } void Usage (TCHAR *Paramerter) {      fprintf(stderr,"============================================================================\n"              "      驱动版Hello World\n"              "作者:dahubaobao[E.S.T]\n"              "主页:www.eviloctal.com\n"              "OICQ:382690\n\n"              "%s -start\t启动\n"              "%s -stop \t停止\n\n"              "本程序只是用做代码交流,如有错误,还请多多包含!\n"              "============================================================================\n"              ,Paramerter,Paramerter); }

转载于:https://www.cnblogs.com/lfls128/p/4975699.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值