《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

时间:2023-03-09 15:55:04
《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置。

2)DRIVER_OBJECT下的派遣函数(分发函数)指针的个数为:IRP_MJ_MAXINUM_FUNCTION,保存在一个数组中,编写驱动程序,实质上就是自己编写处理I/O请求IRP包的派遣函数,可以使用for循环将所有的派遣函数注册到同一个派遣函数。分发函数类似MFC消息机制中的回调函数,它的名字也是程序员自定义的,但是参数确定。卸载函数通过地址在驱动对象的DriverUnload域,在驱动卸载中会自动调用该函数。由于DriverUnload函数没有返回值,所以不能决定是否真正被卸载,只能做一些善后处理。

3)驱动程序和系统其它组件之间的交互是通过给设备对象(DEVICE_OBJECT)发送或者接受发送给设备对象的请求(IRP包)来交互的(设备对象通过符号链接使其在运用层可见,如果不创建符号链接,则只在内核可见),一个驱动程序不创建设备,就不会受到任何IRP,分发函数也就失去了意义。但在一些只要HOOK等不需要交互的驱动,可以不创建设备。运用层使用符号链接可通过CreateFile像打开文件一样打开设备,只不过路径格式为“\\\\.\\设备符号链接”。

4)IoCreateDevice生成的设备有权限限制,使用IoCreateDeviceSecure(在wdmsec.lib中)可以对所有用户开放权限,函数原型为:

 NTSTATUS IoCreateDeviceSecure(
                IN PDRIVER_OBJECT  DriverObject,
                IN ULONG  DeviceExtensionSize,
                IN PUNICODE_STRING  DeviceName  OPTIONAL,
                IN DEVICE_TYPE  DeviceType,
                IN ULONG  DeviceCharacteristics,
                IN BOOLEAN  Exclusive,
                IN PCUNICODE_STRING  DefaultSDDLString,
                IN LPCGUID  DeviceClassGuid,
                OUT PDEVICE_OBJECT  *DeviceObject
 );

  这个函数增加了两个内容(注:亲测不通过,不知道还需要导入什么lib文件而作者没提到):

    a)DefaultSDDLString:描述权限的字符串,使用“D:P(A;;GA;;;WD)”即可对所有用户开放权限,如:UNICODE_STRING         sddl = RTL_CONSTANT_STRING(L” D:P(A;;GA;;;WD)”)

    b)DeviceClassGuid:随便填写GUID,不与其他设备冲突即可。如:const        GUID DECLSPEC_SELECTANY  MYGUID_CLASS_MYCDO = {0x26e0d1e0L,0x8189,0x12e0,{0x99,0x14,0x88,0x00,0x22,0x30,0x19,0x13}}

5)在\DosDevices\(注:由《驱动开发技术详解》可知,WDM驱动为\DosDevice\,而ddk驱动为\??\,我测试使用的是ddk,作者并未说明,而如下代码暂时未测试)下的简单符号链接并不是对所有用户可见,但是在DriverEnter中生成符号链接则对所有用户可见,因为入口函数总是在进程“System”中,但是注销后可能消失。解决办法:生成全局符号链接“\DosDevice\Global\符号链接”即可,在不支持符号链接用户相关性的系统上,使用如下代码:

 UNICODE_STRING  device_name;
 UNICODE_STRING  symbl_name;
 , 0x10) )
 {

          //如果支持符号链接用户相关,用全局符号链接
          RtlInitUnicodeString(&symbl_name,L” \\DosDevice\\Global\\符号链接”);
 }
 else
 {
          //如果不支持,则不用全局的
          RtlInitUnicodeString(&symbl_name,L” \\DosDevice\\符号链接”);
 }
 IoCreateSymbolicLink(&symbl_name,&device_name);

6)IRP类型取决于主功能号,也就是DRIVER_OBJECT中分发函数指针数组中的索引(如IRP_MJ_CREATE等)。IRP主功能号在IRP的当前栈空间。打开设备IRP可简单返回成功,这需要3个步骤(返回失败也一样):

  a)设置pIrp->IoStatus.Information=0

  b)设置pIrp->IoStatus.Status=STATUS_SUCCESS(如果要让处理失败,则返回错误代码)

  c)调用IoCompleteRequest(pIrp,IO_NO_INCREMENT)这个函数指示完成IRP处理

  完成以上3步以后可直接返回pIrp->IoStatus.Status。

7)运用层消息传入:可使用WriteFile,也可以使用DeviceIoControl(设备控制接口),后者是双向的。DeviceIoControl的IRP携带了控制码(必须预先宏定义)、输入缓冲区位置和长度,以及输出缓冲区和位置长度,使用示例(注:DDk驱动代码,实现的功能是在R3层MFC运用程序点击一个按钮,将发送一个字符串到R0内核程序,内核程序接收字符串并打印它):

 //最后修改13-10-15
 #include "xde.h"
 #include <ntddk.h>

 #define INITCODE code_seg("INIT") /*指的代码运行后就从内存释放掉*/
 #define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/

 #define DEVNAME L"\\Device\\testDDk_Device"
 #define SYMNAME L"\\??\\TestLinkName"

 //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序写,内核读数据时的控制码宏定义
 #define MY_DVC_IN_CODE (ULONG)CTL_CODE(\
         FILE_DEVICE_UNKNOWN,/*设备类型*/\
         0xa01,/*用户自定义功能号*/\
         METHOD_BUFFERED,/*传输方式:带缓冲区*/\
         FILE_WRITE_DATA/*调用者需求权限:文件写*/)

 typedef unsigned long ULONG;

 //函数声明
 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject);//创建设备对象与符号链接
 NTSTATUS ddk_DispatchRoutine_CONTROL(IN    PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数
 DRIVER_INITIALIZE DriverEntry;//入口,添加设备,启动HOOk
 DRIVER_UNLOAD DriverUnload;//卸载,删除设备,停止HOOk

 /////////////////////////////////////////////////////////////////
 #pragma  PAGECODE
 //DriverEntry,入口函数。相当于main
 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING sRegPath)
 {
         //创建设备对象与符号链接
         CreateMyDevice (pDriverObject);
         //派遣函数
         pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
         pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数

         pDriverObject->DriverUnload = DriverUnload;
         return STATUS_SUCCESS;
 }

 ///////////////////////////////////////////////////////////////////
 #pragma  PAGECODE
 //提供一个Unload函数值是为了让这个程序能动态卸载,方便调试
 VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
 {
         PDEVICE_OBJECT pDev;//用来取得要删除设备对象
         UNICODE_STRING symLinkName; //
         //打印一句
         KdPrint(("Our driver is unloading...\n"));
         pDev=pDriverObject->DeviceObject;//从驱动对象取得设备对象,所有设备对象连成一条链,这里假定只有一个设备
         IoDeleteDevice(pDev); //删除设备

         RtlInitUnicodeString(&symLinkName,SYMNAME);
         //删除符号链接
         IoDeleteSymbolicLink(&symLinkName);
         KdPrint(("驱动成功被卸载... ")); //sprintf,printf
         //取得要删除设备对象
         //删掉所有设备
 }

 //读取IRP_MJ_DEVICE_CONTROL类型IRP包中的数据信息:驱动读
 NTSTATUS  MyDeviceIoCtrlIn(PIRP  pIrp)
 {
         PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
         //得到输入缓存长度
         ULONG in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
         //输入输出缓冲是公用内存的
         PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;

         //输出缓冲区信息
         KdPrint(("The Messege from R3 is : %s \n",buffer));

         pIrp->IoStatus.Information = in_len;//设置IRP读取的字节数
         pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态
         IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理
         return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致
 }

 //分类处理
 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp)
 {
         //IoGetCurrentIrpStackLocation得到调用者堆栈的指针
         PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);

         //判断IRP请求包的类型
         switch(irpsp->MajorFunction)
         {
         case  IRP_MJ_CREATE://处理打开请求(CreateFile)
             KdPrint(("IRP_MJ_CREATE\n"));
             break;
         case  IRP_MJ_CLOSE://处理关闭请求(CloseHandle)
             KdPrint(("IRP_MJ_CLOSE\n"));
             break;
         case  IRP_MJ_READ://处理读请求(ReadFile)
             KdPrint(("IRP_MJ_READ\n"));
             break;
         case  IRP_MJ_WRITE://处理写请求(WriteFile)
             KdPrint(("IRP_MJ_WRITE\n"));
             break;
         case  IRP_MJ_DEVICE_CONTROL://处理设备控制信息(DeviceIoControl)
             {
                 //首先得到功能号
                 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
                 KdPrint(("IRP_MJ_DEVICE_CONTROL\n"));
                 if ( code == MY_DVC_IN_CODE )//内核读取数据
                 {
                     KdPrint(("MY_DVC_IN_CODE\n"));
                     return MyDeviceIoCtrlIn(pIrp);
                 }
                 break;
             }
         default:
             KdPrint(("其它处理\n"));
         }

         pIrp->IoStatus.Information=;//设置IRP操作的字节数为,这里无实际意义
         pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态
         IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理
         return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致
 }

 #pragma INITCODE
 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject)
 {
         NTSTATUS status;
         PDEVICE_OBJECT pDevObj;/*用来返回创建设备结构体的指针*/
         //设备对象(DEVICE_OBJECT)由驱动创建。一个驱动可以创建多个设备对象。
         //通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象。
         //一个驱动创建的所有设备对象链成一条链。该驱动的驱动对象可以找到这个链,
         //一个设备对象也可以找到创建它的驱动的驱动对象。DEVICE_OBJECT是设备对象存在的形式

         //创建设备名称
         UNICODE_STRING devName;
         UNICODE_STRING symLinkName; // 结构体,包含了宽字节字符缓冲区与其长度
         RtlInitUnicodeString(&devName,DEVNAME);//对devName初始化字串为"\\Device\\testDDK_Device"
         //这个宽字节的路径“\\Device\\ ”部分不能改变,后面是设备名称

         //创建设备对象
         status = IoCreateDevice ( pDriverObject,    //驱动程序对象指针。在入库函数DriverEntry过程里接收
             ,//指定驱动程序为设备扩展对象定义的结构体大小
             &devName,//设备名称,必须是完整的设备路径名,设置为NULL则是无名设备
             FILE_DEVICE_UNKNOWN,//设备类型
             , TRUE,//驱动程序的其它信息以及指定设备是否是独占的,TRUE则是
             &pDevObj);//输出,用来保存PDEVICE_OBJECT结构体指针,这个指针指向设备对象自身
         if (!NT_SUCCESS(status))
         {
             if (status==STATUS_INSUFFICIENT_RESOURCES)
             {
                 KdPrint(("资源不足STATUS_INSUFFICIENT_RESOURCES"));
             }
             if (status==STATUS_OBJECT_NAME_EXISTS )
             {
                 KdPrint(("指定对象名存在"));
             }
             if (status==STATUS_OBJECT_NAME_COLLISION)
             {
                 KdPrint(("//对象名有冲突"));
             }
             KdPrint(("设备创建失败...++++++++"));
             return status;
         }
         KdPrint(("设备创建成功...++++++++"));
         // IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中
         pDevObj->Flags |= DO_BUFFERED_IO;
         //创建符号链接
         RtlInitUnicodeString(&symLinkName,SYMNAME);
         status = IoCreateSymbolicLink( &symLinkName,// Unicode字符串指针,是一个用户态可见的名称
                     &devName );// Unicode字符串指针,是驱动程序创建的设备对象名称。
         if (!NT_SUCCESS(status)) /*status等于*/
         {
             IoDeleteDevice( pDevObj );//删除驱动设备
             return status;
         }
         return STATUS_SUCCESS;
 }

  在R3运用程序上,进行DeviceIoControl代码(写MFC程序,添加按钮点击事件):

 //在头文件添加
 #include <winioctl.h>
 #define SYMNAME "\\\\.\\TestLinkName"
 //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序写,内核读数据时的控制码宏定义
 #define MY_DVC_IN_CODE (ULONG)CTL_CODE(\
         FILE_DEVICE_UNKNOWN,/*设备类型*/\
         0xa01,/*用户自定义功能号*/\
         METHOD_BUFFERED,/*传输方式:带缓冲区*/\
     FILE_WRITE_DATA/*调用者需求权限:文件写*/)

 //添加按钮点击事件
 // TODO: 在此添加控件通知处理程序代码
     DWORD length = ;//返回的长度

     HANDLE device = CreateFile(SYMNAME,
         GENERIC_READ|GENERIC_WRITE,,,
         OPEN_EXISTING,
         FILE_ATTRIBUTE_SYSTEM,);

     if ( device == INVALID_HANDLE_VALUE )
     {
         //打开失败
         AfxMessageBox(_T("打开符号链接失败"));
         return;
     }

     CString message("Hello R0");
     LPVOID in_buffer = message.GetBuffer();
     int in_buffer_len = message.GetLength();

     BOOL ret = DeviceIoControl(device,
         MY_DVC_IN_CODE,            //功能号
         in_buffer,                //输入缓冲,要传递的信息,预先填好
         in_buffer_len,            //输入缓冲长度
         NULL,                    //没有输出缓冲
         ,                        //输出缓冲为长度
         &length,                    //返回的长度
         NULL);
     if (!ret)
     {
         // DeviceIoControl失败
         AfxMessageBox(_T("DeviceIoControl失败"));
     }

     if ( length != in_buffer_len)
     {
         AfxMessageBox(_T("读取字节数不一致"));
     }
     

  输出效果:

《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

8)驱动层消息传出:运用程序需要开启一个线程调用DeviceIoControl(或者ReadFile)读取数据,而驱动在没有消息的时候阻塞这个IRP,等待有消息时返回(使用事件也行,但还要传递事件句柄,较繁琐)。驱动内部制作一个链表,当有消息的要通知运用程序时,把消息放入链表,并设置同步事件,在DeviceIoControl的处理中等待事件。这里测试一下,避免麻烦,传送一个ULONG类型,传输10次,R3运用程序开线程接收,不停的弹窗,这里不考虑数据覆盖问题。

 //内核程序
 #include "xde.h"
 #include <ntddk.h>

 #define INITCODE code_seg("INIT") /*指的代码运行后就从内存释放掉*/
 #define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/

 #define DELAY_ONE_MICROSECOND (-10)
 #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

 #define DEVNAME L"\\Device\\testDDk_Device"
 #define SYMNAME L"\\??\\TestLinkName"

 //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序读,内核写数据时的控制码宏定义
 #define MY_DVC_OUT_CODE (ULONG)CTL_CODE(\
     FILE_DEVICE_UNKNOWN,/*设备类型*/\
     0xa02,/*用户自定义功能号*/\
     METHOD_BUFFERED,/*传输方式:带缓冲区*/\
     FILE_READ_DATA/*调用者需求权限:文件读*/)

 typedef unsigned long ULONG;
 typedef ULONG* PULONG;

 //要发送的数据
 tatic ULONG data;
 //全局事件变量
 static KEVENT g_my_notify_event;

 //函数声明
 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject);//创建设备对象与符号链接
 NTSTATUS ddk_DispatchRoutine_CONTROL(IN    PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数
 void MySleep(LONG msec);//延时函数
 void MyThreadProc(PVOID context);//测试线程
 DRIVER_INITIALIZE DriverEntry;//入口,添加设备,启动HOOk
 DRIVER_UNLOAD DriverUnload;//卸载,删除设备,停止HOOk

 /////////////////////////////////////////////////////////////////
 #pragma  PAGECODE
 //DriverEntry,入口函数。相当于main
 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING sRegPath)
 {
     HANDLE hThread = NULL;
     NTSTATUS status;
     //对ZwCreateSection进行HOOK
     RtlInitUnicodeString(&apiFuncName,L"ZwCreateSection");
     if (InlineHookIofCallDriverXp(&apiFuncName,(ULONG)MyIofCallDriver,TRUE) == FALSE)
     {
         KdPrint(("Inline Hook failed!\n"));
     }

     //创建设备对象与符号链接
     CreateMyDevice (pDriverObject);
     //派遣函数
     pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
     pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
     pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
     pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数
     pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数

     //创建线程:
     status = PsCreateSystemThread(
         &hThread,//新线程句柄
         0L,NULL,NULL,NULL,
         MyThreadProc,//函数地址
         NULL//传递参数
         );
     if ( !NT_SUCCESS(status) )
     {
         KdPrint(("CreateSystemThread failed!"));
     }

     ZwClose(hThread);
     pDriverObject->DriverUnload = DriverUnload;
     return STATUS_SUCCESS;
 }

 void MyThreadProc(PVOID context)
 {
     //测试
     KeInitializeEvent(&g_my_notify_event,
         SynchronizationEvent,//自动复位
         FALSE//初始为无信号状态
         );
      ; data <=  +  ; data ++)
     {
         //Sleep(3000)
         MySleep();
         //设置有信号
         KeSetEvent(&g_my_notify_event,
             IO_NO_INCREMENT,//预备给被唤醒线程临时提升优先级的增量
             FALSE//之后是否跟KeWaitForXXX等待(其它或者自身)事件
             );
         KdPrint(("The data is : %u \n",data));
     }
 }

 void MySleep(LONG msec)
 {
     LARGE_INTEGER my_interval;
     my_interval.QuadPart = DELAY_ONE_MILLISECOND;
     my_interval.QuadPart *= msec;
     KeDelayExecutionThread(KernelMode,,&my_interval);
 }

 //提供一个Unload函数值是为了让这个程序能动态卸载,方便调试
 VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
 {
     PDEVICE_OBJECT pDev;//用来取得要删除设备对象
     UNICODE_STRING symLinkName; //
     //打印一句
     KdPrint(("Our driver is unloading...\n"));
     //停止HOOk
     if(InlineHookIofCallDriverXp(&apiFuncName,(ULONG)MyIofCallDriver,FALSE) == FALSE)
     {
         KdPrint(("UnHook failed!\n"));
     }
     KdPrint(("UnHook succeed!\n"));

     pDev=pDriverObject->DeviceObject;//从驱动对象取得设备对象,所有设备对象连成一条链,这里假定只有一个设备
     IoDeleteDevice(pDev); //删除设备
     RtlInitUnicodeString(&symLinkName,SYMNAME);

     //删除符号链接
     IoDeleteSymbolicLink(&symLinkName);
     KdPrint(("驱动成功被卸载... ")); //sprintf,printf
     //取得要删除设备对象
     //删掉所有设备
 }

 //读取IRP_MJ_DEVICE_CONTROL类型IRP包中的数据信息:驱动写
 NTSTATUS  MyDeviceIoCtrlOut(PIRP  pIrp)
 {
     PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
     //获得输出缓冲区
     PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;
     //获取输出缓冲区长度
     ULONG out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;

     //等待有事件信号
     KeWaitForSingleObject(&g_my_notify_event,
         Executive,//等待原因,驱动程序设置为Executive
         KernelMode,//内核模式
         FALSE,//不允许警告
         NULL//等待超时时间:无限等待
         );

     //有请求了
     if (out_len < sizeof(ULONG) )//缓冲长度不够
     {
         pIrp->IoStatus.Information = sizeof(ULONG);//写需要的长度
         pIrp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
         IoCompleteRequest(pIrp,IO_NO_INCREMENT);
         return pIrp->IoStatus.Status;
     }

     //长度足够,填写输出缓冲区
     *((PULONG)buffer) = data;

     //返回成功
     pIrp->IoStatus.Information = sizeof(ULONG);//写入的长度
     pIrp->IoStatus.Status = STATUS_SUCCESS;
     IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理
     return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致
 }

 //分类处理
 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp)
 {
     //IoGetCurrentIrpStackLocation得到调用者堆栈的指针
     PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);

     //判断IRP请求包的类型
     switch(irpsp->MajorFunction)
     {
     case  IRP_MJ_CREATE://处理打开请求(CreateFile)
         KdPrint(("IRP_MJ_CREATE\n"));
         break;
     case  IRP_MJ_CLOSE://处理关闭请求(CloseHandle)
         KdPrint(("IRP_MJ_CLOSE\n"));
         break;
     case  IRP_MJ_READ://处理读请求(ReadFile)
         KdPrint(("IRP_MJ_READ\n"));
         break;
     case  IRP_MJ_WRITE://处理写请求(WriteFile)
         KdPrint(("IRP_MJ_WRITE\n"));
         break;
     case  IRP_MJ_DEVICE_CONTROL://处理设备控制信息(DeviceIoControl)
         {
             //首先得到功能号
             ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
             KdPrint(("IRP_MJ_DEVICE_CONTROL\n"));
             if ( code == MY_DVC_OUT_CODE)//内核传出数据
             {
                 KdPrint(("MY_DVC_OUT_CODE\n"));
                 return MyDeviceIoCtrlOut(pIrp);
             }
             break;
         }
     default:
         KdPrint(("其它处理\n"));
     }

     //判断是输入还是输出操作

     pIrp->IoStatus.Information=;//设置IRP操作的字节数为,这里无实际意义
     pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态
     IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理
     return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致
 }

 /////////////////////////////////////////////////////////////////
 #pragma INITCODE
 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject)
 {
     NTSTATUS status;
     PDEVICE_OBJECT pDevObj; 

     //创建设备名称
     UNICODE_STRING devName;
     UNICODE_STRING symLinkName;

     RtlInitUnicodeString(&devName,DEVNAME); 

     //创建设备对象
     status = IoCreateDevice/*Secure*/( pDriverObject,    //驱动程序对象指针。在入库函数DriverEntry过程里接收
         ,//指定驱动程序为设备扩展对象定义的结构体大小
         &devName,//设备名称,必须是完整的设备路径名,设置为NULL则是无名设备
         FILE_DEVICE_UNKNOWN,//设备类型
         , TRUE,//驱动程序的其它信息以及指定设备是否是独占的,TRUE则是
         //&sddl,(LPCGUID)&MYGUID_CLASS_MYCDO,//sddl,guid
         &pDevObj);//输出,用来保存PDEVICE_OBJECT结构体指针,这个指针指向设备对象自身
     if (!NT_SUCCESS(status))
     {
         if (status==STATUS_INSUFFICIENT_RESOURCES)
         {
             KdPrint(("资源不足STATUS_INSUFFICIENT_RESOURCES"));
         }
         if (status==STATUS_OBJECT_NAME_EXISTS )
         {
             KdPrint(("指定对象名存在"));
         }
         if (status==STATUS_OBJECT_NAME_COLLISION)
         {
             KdPrint(("//对象名有冲突"));
         }
         KdPrint(("设备创建失败...++++++++"));
         return status;
     }
     KdPrint(("设备创建成功...++++++++"));
     // IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中
     pDevObj->Flags |= DO_BUFFERED_IO;
     //创建符号链接

     RtlInitUnicodeString(&symLinkName,SYMNAME);

     status = IoCreateSymbolicLink( &symLinkName,// Unicode字符串指针,是一个用户态可见的名称
                 &devName );// Unicode字符串指针,是驱动程序创建的设备对象名称。
     if (!NT_SUCCESS(status)) /*status等于*/
     {
         IoDeleteDevice( pDevObj );//删除驱动设备
         return status;
     }
     return STATUS_SUCCESS;
 }

  R3接收程序:

 #include <winioctl.h>
 #define SYMNAME "\\\\.\\TestLinkName"

 //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序读,内核写数据时的控制码宏定义
 #define MY_DVC_OUT_CODE (ULONG)CTL_CODE(\
     FILE_DEVICE_UNKNOWN,/*设备类型*/\
     0xa02,/*用户自定义功能号*/\
     METHOD_BUFFERED,/*传输方式:带缓冲区*/\
     FILE_READ_DATA/*调用者需求权限:文件读*/)

 DWORD MyThreadProc(void);

 //按钮事件

 void CMFCTestDlg::OnBnClickedButton1()
 {
     CreateThread(NULL,,(LPTHREAD_START_ROUTINE)MyThreadProc,NULL,NULL,NULL);
 }

 DWORD MyThreadProc(void)
 {
     // TODO: 在此添加控件通知处理程序代码
     ULONG data = ;//返回的数据
     DWORD length = ;//返回的长度

     HANDLE device = CreateFile(SYMNAME,
         GENERIC_READ|GENERIC_WRITE,,,
         OPEN_EXISTING,
         FILE_ATTRIBUTE_SYSTEM,);

     if ( device == INVALID_HANDLE_VALUE )
     {
         //打开失败
         AfxMessageBox(_T("打开符号链接失败"));
         return FALSE;
     }

     ;
      )
     {
         BOOL ret = DeviceIoControl(device,
             MY_DVC_OUT_CODE,        //功能号
             NULL,                    //没有输入缓冲,要传递的信息,预先填好
             NULL,                    //输入缓冲长度为
             &data,                    //输出缓冲
             sizeof(ULONG),            //输出缓冲长度
             &length,                //返回的长度
             NULL);
         if (!ret)
         {
             // DeviceIoControl失败
             AfxMessageBox(_T("DeviceIoControl失败"));
         }

         CString a;
         a.Format("%u",data);
         AfxMessageBox(a);
     }
     CloseHandle(device);
     return TRUE;
 }

  效果图:

《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求