zoukankan      html  css  js  c++  java
  • 《windows内核安全与驱动开发》ctrl2cap中的ObReferenceObjectByName疑问

      国内有关于windows内核驱动这块的书籍实在是甚少,不过好在《windows内核安全与驱动开发》这本书还算不错(内容方面),但是不得不说这本书在许多地方存在着一些细节上的问题。比如我今天要谈的这个话题。

      在这本书的键盘过滤这个章节,作者对ObReferenceObjectByName个函数的用法做了介绍,并指明这是一个非文档化函数(可以用,但是在MSDN的文档中以及SDK的头文件中没有公开出来)。这个函数的具体结构如下:

     1 NTSTATUS
     2 ObReferenceObjectByName(  
     3     IN PUNICODE_STRING ObjectName,  
     4     IN ULONG Attributes,  
     5     IN PACCESS_STATE PassedAccessState OPTIONAL,  
     6     IN ACCESS_MASK DesiredAccess OPTIONAL,  
     7     IN POBJECT_TYPE ObjectType,  
     8     IN KPROCESSOR_MODE AccessMode,  
     9     IN OUT PVOID ParseContext OPTIONAL,  
    10     OUT PVOID *Object  
    11     );

    这个函数的作用是利用对象的名字获得对象的指针,在这本书的这个章节,作者用它来以“键盘驱动名”获取一个键盘驱动对象(这个对象通过函数的最后一个参数返回),再从这个键盘驱动对象中获得这个键盘设备。

      由于每次调用这个函数成功的话,系统会给这个对象多增加一个引用,除了属性为OBJ_PERMANENT的对象,一般对象的生命是由引用数控制的,当某个对象的引用数为零了,windows就会删除这个对象,这有点类似于java的垃圾回收机制。所以为了成功调用这个函数并且保持这个对象的引用数不变,就有必要使用ObDereferenceObject这个函数来解除这个引用。

      这本书给出的源码如下:

      1 ///
      2 /// @file            ctrl2cap.c
      3 /// @author    wowocock
      4 /// @date        2009-1-27
      5 /// 
      6 
      7 #include <wdm.h>
      8 
      9 // Kbdclass驱动的名字
     10 #define KBD_DRIVER_NAME  L"\Driver\Kbdclass"
     11 
     12 typedef struct _C2P_DEV_EXT 
     13 { 
     14     // 这个结构的大小
     15     ULONG NodeSize; 
     16     // 过滤设备对象
     17     PDEVICE_OBJECT pFilterDeviceObject;
     18     // 同时调用时的保护锁
     19     KSPIN_LOCK IoRequestsSpinLock;
     20     // 进程间同步处理  
     21     KEVENT IoInProgressEvent; 
     22     // 绑定的设备对象
     23     PDEVICE_OBJECT TargetDeviceObject; 
     24     // 绑定前底层设备对象
     25     PDEVICE_OBJECT LowerDeviceObject; 
     26 } C2P_DEV_EXT, *PC2P_DEV_EXT;
     27 
     28 NTSTATUS 
     29 c2pDevExtInit( 
     30     IN PC2P_DEV_EXT devExt, 
     31     IN PDEVICE_OBJECT pFilterDeviceObject, 
     32     IN PDEVICE_OBJECT pTargetDeviceObject, 
     33     IN PDEVICE_OBJECT pLowerDeviceObject ) 
     34 { 
     35     memset(devExt, 0, sizeof(C2P_DEV_EXT)); 
     36     devExt->NodeSize = sizeof(C2P_DEV_EXT); 
     37     devExt->pFilterDeviceObject = pFilterDeviceObject; 
     38     KeInitializeSpinLock(&(devExt->IoRequestsSpinLock)); 
     39     KeInitializeEvent(&(devExt->IoInProgressEvent), NotificationEvent, FALSE); 
     40     devExt->TargetDeviceObject = pTargetDeviceObject; 
     41     devExt->LowerDeviceObject = pLowerDeviceObject; 
     42     return( STATUS_SUCCESS ); 
     43 }
     44 
     45 // 这个函数是事实存在的,只是文档中没有公开。声明一下
     46 // 就可以直接使用了。
     47 NTSTATUS
     48 ObReferenceObjectByName(
     49                         PUNICODE_STRING ObjectName,
     50                         ULONG Attributes,
     51                         PACCESS_STATE AccessState,
     52                         ACCESS_MASK DesiredAccess,
     53                         POBJECT_TYPE ObjectType,
     54                         KPROCESSOR_MODE AccessMode,
     55                         PVOID ParseContext,
     56                         PVOID *Object
     57                         );
     58 
     59 extern POBJECT_TYPE IoDriverObjectType;
     60 ULONG gC2pKeyCount = 0;
     61 PDRIVER_OBJECT gDriverObject = NULL;
     62 
     63 // 这个函数经过改造。能打开驱动对象Kbdclass,然后绑定
     64 // 它下面的所有的设备:
     65 NTSTATUS 
     66 c2pAttachDevices( 
     67                   IN PDRIVER_OBJECT DriverObject, 
     68                   IN PUNICODE_STRING RegistryPath 
     69                   ) 
     70 { 
     71     NTSTATUS status = 0; 
     72     UNICODE_STRING uniNtNameString; 
     73     PC2P_DEV_EXT devExt; 
     74     PDEVICE_OBJECT pFilterDeviceObject = NULL; 
     75     PDEVICE_OBJECT pTargetDeviceObject = NULL; 
     76     PDEVICE_OBJECT pLowerDeviceObject = NULL; 
     77 
     78     PDRIVER_OBJECT KbdDriverObject = NULL; 
     79 
     80     KdPrint(("MyAttach
    ")); 
     81 
     82     // 初始化一个字符串,就是Kdbclass驱动的名字。
     83     RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME); 
     84     // 请参照前面打开设备对象的例子。只是这里打开的是驱动对象。
     85     status = ObReferenceObjectByName ( 
     86         &uniNtNameString, 
     87         OBJ_CASE_INSENSITIVE, 
     88         NULL, 
     89         0, 
     90         IoDriverObjectType, 
     91         KernelMode, 
     92         NULL, 
     93         &KbdDriverObject 
     94         ); 
     95     // 如果失败了就直接返回
     96     if(!NT_SUCCESS(status)) 
     97     { 
     98         KdPrint(("MyAttach: Couldn't get the MyTest Device Object
    ")); 
     99         return( status ); 
    100     }
    101     else
    102     {
    103         // 这个打开需要解应用。早点解除了免得之后忘记。
    104         ObDereferenceObject(DriverObject);
    105     }
    106 
    107     // 这是设备链中的第一个设备    
    108     pTargetDeviceObject = KbdDriverObject->DeviceObject;
    109     // 现在开始遍历这个设备链
    110     while (pTargetDeviceObject) 
    111     {
    112         // 生成一个过滤设备,这是前面读者学习过的。这里的IN宏和OUT宏都是
    113         // 空宏,只有标志性意义,表明这个参数是一个输入或者输出参数。
    114         status = IoCreateDevice( 
    115             IN DriverObject, 
    116             IN sizeof(C2P_DEV_EXT), 
    117             IN NULL, 
    118             IN pTargetDeviceObject->DeviceType, 
    119             IN pTargetDeviceObject->Characteristics, 
    120             IN FALSE, 
    121             OUT &pFilterDeviceObject 
    122             ); 
    123 
    124         // 如果失败了就直接退出。
    125         if (!NT_SUCCESS(status)) 
    126         { 
    127             KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object
    ")); 
    128             return (status); 
    129         } 
    130 
    131         // 绑定。pLowerDeviceObject是绑定之后得到的下一个设备。也就是
    132         // 前面常常说的所谓真实设备。
    133         pLowerDeviceObject = 
    134             IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject); 
    135         // 如果绑定失败了,放弃之前的操作,退出。
    136         if(!pLowerDeviceObject) 
    137         { 
    138             KdPrint(("MyAttach: Couldn't attach to MyTest Device Object
    ")); 
    139             IoDeleteDevice(pFilterDeviceObject); 
    140             pFilterDeviceObject = NULL; 
    141             return( status ); 
    142         } 
    143 
    144         // 设备扩展!下面要详细讲述设备扩展的应用。
    145         devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension); 
    146         c2pDevExtInit( 
    147             devExt, 
    148             pFilterDeviceObject, 
    149             pTargetDeviceObject, 
    150             pLowerDeviceObject ); 
    151 
    152         // 下面的操作和前面过滤串口的操作基本一致。这里不再解释了。
    153         pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType; 
    154         pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics; 
    155         pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1; 
    156         pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ; 
    157         //next device 
    158         pTargetDeviceObject = pTargetDeviceObject->NextDevice;
    159     }
    160     return status; 
    161 } 
    162 
    163 VOID 
    164 c2pDetach(IN PDEVICE_OBJECT pDeviceObject) 
    165 { 
    166     PC2P_DEV_EXT devExt; 
    167     BOOLEAN NoRequestsOutstanding = FALSE; 
    168     devExt = (PC2P_DEV_EXT)pDeviceObject->DeviceExtension; 
    169     __try 
    170     { 
    171         __try 
    172         { 
    173             IoDetachDevice(devExt->TargetDeviceObject);
    174             devExt->TargetDeviceObject = NULL; 
    175             IoDeleteDevice(pDeviceObject); 
    176             devExt->pFilterDeviceObject = NULL; 
    177             DbgPrint(("Detach Finished
    ")); 
    178         } 
    179         __except (EXCEPTION_EXECUTE_HANDLER){} 
    180     } 
    181     __finally{} 
    182     return; 
    183 }
    184 
    185 
    186 #define  DELAY_ONE_MICROSECOND  (-10)
    187 #define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
    188 #define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
    189 
    190 VOID 
    191 c2pUnload(IN PDRIVER_OBJECT DriverObject) 
    192 { 
    193     PDEVICE_OBJECT DeviceObject; 
    194     PDEVICE_OBJECT OldDeviceObject; 
    195     PC2P_DEV_EXT devExt; 
    196 
    197     LARGE_INTEGER    lDelay;
    198     PRKTHREAD CurrentThread;
    199     //delay some time 
    200     lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);
    201     CurrentThread = KeGetCurrentThread();
    202     // 把当前线程设置为低实时模式,以便让它的运行尽量少影响其他程序。
    203     KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY);
    204 
    205     UNREFERENCED_PARAMETER(DriverObject); 
    206     KdPrint(("DriverEntry unLoading...
    ")); 
    207 
    208     // 遍历所有设备并一律解除绑定
    209     DeviceObject = DriverObject->DeviceObject;
    210     while (DeviceObject)
    211     {
    212         // 解除绑定并删除所有的设备
    213         c2pDetach(DeviceObject);
    214         DeviceObject = DeviceObject->NextDevice;
    215     } 
    216     ASSERT(NULL == DriverObject->DeviceObject);
    217 
    218     while (gC2pKeyCount)
    219     {
    220         KeDelayExecutionThread(KernelMode, FALSE, &lDelay);
    221     }
    222     KdPrint(("DriverEntry unLoad OK!
    ")); 
    223     return; 
    224 } 
    225 
    226 NTSTATUS c2pDispatchGeneral( 
    227                                  IN PDEVICE_OBJECT DeviceObject, 
    228                                  IN PIRP Irp 
    229                                  ) 
    230 { 
    231     // 其他的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备
    232     // 的设备对象。 
    233     KdPrint(("Other Diapatch!")); 
    234     IoSkipCurrentIrpStackLocation(Irp); 
    235     return IoCallDriver(((PC2P_DEV_EXT)
    236         DeviceObject->DeviceExtension)->LowerDeviceObject, Irp); 
    237 } 
    238 
    239 NTSTATUS c2pPower( 
    240                        IN PDEVICE_OBJECT DeviceObject, 
    241                        IN PIRP Irp 
    242                        ) 
    243 { 
    244     PC2P_DEV_EXT devExt;
    245     devExt =
    246         (PC2P_DEV_EXT)DeviceObject->DeviceExtension; 
    247 
    248     PoStartNextPowerIrp( Irp ); 
    249     IoSkipCurrentIrpStackLocation( Irp ); 
    250     return PoCallDriver(devExt->LowerDeviceObject, Irp ); 
    251 } 
    252 
    253 NTSTATUS c2pPnP( 
    254                      IN PDEVICE_OBJECT DeviceObject, 
    255                      IN PIRP Irp 
    256                      ) 
    257 { 
    258     PC2P_DEV_EXT devExt; 
    259     PIO_STACK_LOCATION irpStack; 
    260     NTSTATUS status = STATUS_SUCCESS; 
    261     KIRQL oldIrql; 
    262     KEVENT event; 
    263 
    264     // 获得真实设备。
    265     devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension); 
    266     irpStack = IoGetCurrentIrpStackLocation(Irp); 
    267 
    268     switch (irpStack->MinorFunction) 
    269     { 
    270     case IRP_MN_REMOVE_DEVICE: 
    271         KdPrint(("IRP_MN_REMOVE_DEVICE
    ")); 
    272 
    273         // 首先把请求发下去
    274         IoSkipCurrentIrpStackLocation(Irp); 
    275         IoCallDriver(devExt->LowerDeviceObject, Irp); 
    276         // 然后解除绑定。
    277         IoDetachDevice(devExt->LowerDeviceObject); 
    278         // 删除我们自己生成的虚拟设备。
    279         IoDeleteDevice(DeviceObject); 
    280         status = STATUS_SUCCESS; 
    281         break; 
    282 
    283     default: 
    284         // 对于其他类型的IRP,全部都直接下发即可。 
    285         IoSkipCurrentIrpStackLocation(Irp); 
    286         status = IoCallDriver(devExt->LowerDeviceObject, Irp); 
    287     } 
    288     return status; 
    289 }
    290 
    291 // 这是一个IRP完成回调函数的原型
    292 NTSTATUS c2pReadComplete( 
    293                               IN PDEVICE_OBJECT DeviceObject, 
    294                               IN PIRP Irp, 
    295                               IN PVOID Context 
    296                               ) 
    297 {
    298      PIO_STACK_LOCATION IrpSp;
    299      ULONG buf_len = 0;
    300      PUCHAR buf = NULL;
    301      size_t i;
    302 
    303      IrpSp = IoGetCurrentIrpStackLocation( Irp );
    304 
    305      //  如果这个请求是成功的。很显然,如果请求失败了,这么获取
    306      //   进一步的信息是没意义的。
    307      if( NT_SUCCESS( Irp->IoStatus.Status ) ) 
    308      {
    309         // 获得读请求完成后输出的缓冲区
    310         buf = Irp->AssociatedIrp.SystemBuffer;
    311         // 获得这个缓冲区的长度。一般的说返回值有多长都保存在
    312         // Information中。
    313         buf_len = Irp->IoStatus.Information;
    314 
    315         //… 这里可以做进一步的处理。我这里很简单的打印出所有的扫
    316         // 描码。
    317         for(i=0;i<buf_len;++i)
    318         {
    319             DbgPrint("ctrl2cap: %2x
    ", buf[i]);
    320         }
    321     }
    322     gC2pKeyCount--;
    323 
    324     if( Irp->PendingReturned )
    325     { 
    326         IoMarkIrpPending( Irp ); 
    327     } 
    328     return Irp->IoStatus.Status;
    329 }
    330 
    331 
    332 NTSTATUS c2pDispatchRead( 
    333                               IN PDEVICE_OBJECT DeviceObject, 
    334                               IN PIRP Irp ) 
    335 { 
    336     NTSTATUS status = STATUS_SUCCESS; 
    337     PC2P_DEV_EXT devExt; 
    338     PIO_STACK_LOCATION currentIrpStack; 
    339     KEVENT waitEvent;
    340     KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );
    341 
    342     if (Irp->CurrentLocation == 1) 
    343     { 
    344         ULONG ReturnedInformation = 0; 
    345         KdPrint(("Dispatch encountered bogus current location
    ")); 
    346         status = STATUS_INVALID_DEVICE_REQUEST; 
    347         Irp->IoStatus.Status = status; 
    348         Irp->IoStatus.Information = ReturnedInformation; 
    349         IoCompleteRequest(Irp, IO_NO_INCREMENT); 
    350         return(status); 
    351     } 
    352 
    353     // 全局变量键计数器加1
    354     gC2pKeyCount++;
    355 
    356     // 得到设备扩展。目的是之后为了获得下一个设备的指针。
    357     devExt =
    358         (PC2P_DEV_EXT)DeviceObject->DeviceExtension;
    359 
    360     // 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。
    361     // 剩下的任务是要等待读请求完成。
    362     currentIrpStack = IoGetCurrentIrpStackLocation(Irp); 
    363     IoCopyCurrentIrpStackLocationToNext(Irp);
    364     IoSetCompletionRoutine( Irp, c2pReadComplete, 
    365         DeviceObject, TRUE, TRUE, TRUE ); 
    366     return  IoCallDriver( devExt->LowerDeviceObject, Irp );     
    367 }
    368 
    369 NTSTATUS DriverEntry( 
    370                      IN PDRIVER_OBJECT DriverObject, 
    371                      IN PUNICODE_STRING RegistryPath 
    372                      ) 
    373 { 
    374     ULONG i; 
    375     NTSTATUS status; 
    376     KdPrint (("c2p.SYS: entering DriverEntry
    ")); 
    377 
    378     // 填写所有的分发函数的指针
    379     for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) 
    380     { 
    381         DriverObject->MajorFunction[i] = c2pDispatchGeneral; 
    382     } 
    383 
    384     // 单独的填写一个Read分发函数。因为要的过滤就是读取来的按键信息
    385     // 其他的都不重要。这个分发函数单独写。
    386     DriverObject->MajorFunction[IRP_MJ_READ] = c2pDispatchRead; 
    387 
    388     // 单独的填写一个IRP_MJ_POWER函数。这是因为这类请求中间要调用
    389     // 一个PoCallDriver和一个PoStartNextPowerIrp,比较特殊。
    390     DriverObject->MajorFunction [IRP_MJ_POWER] = c2pPower; 
    391 
    392     // 我们想知道什么时候一个我们绑定过的设备被卸载了(比如从机器上
    393     // 被拔掉了?)所以专门写一个PNP(即插即用)分发函数
    394     DriverObject->MajorFunction [IRP_MJ_PNP] = c2pPnP; 
    395 
    396     // 卸载函数。
    397     DriverObject->DriverUnload = c2pUnload; 
    398     gDriverObject = DriverObject;
    399     // 绑定所有键盘设备
    400     status =c2pAttachDevices(DriverObject, RegistryPath);
    401 
    402     return status; 
    403 }

       问题的关键在于ObDereferenceObject函数的参数,应该要和ObReferenceObjectByName这个函数的最后一个参数相同,这样才能减少引用,而这里并没有。我当时看到这里的时候百思不得其解,一直觉得有问题,然后我就去百度搜这个东西,结果真的搜到了“《寒江独钓》_键盘过滤_ctrl2cap_卸载_蓝屏”这类标题的文章,当时异常高兴,可是进去以后发现内容和我想要的根本不一样,如下:

    我大概花了2个小时从百度搜到了404搜索引擎,结果都是说蓝屏的原因出现在unload函数中。这样一来我上面那个问题还是没有解决,然而我并没有放弃,果然在搜了大概三个小时之后终于搜到了下面这篇文章

    小结:经过我三个小时的努力搜索,终于皇天不负有心人,得到了我想要的结果,解决了我心中的疑惑。因为现在已经星期四晚上了,星期天下午还要考概率论,必须留点时间来预习(上了大学的同学应该都能体会“预习”这个词吧。。。),不然就真的gg了。所以没有时间来验证这个问题,总之就目前的结果来说unload那边是不是真的有问题还不知道(写这篇文章的时候还没看到unload部分),但是ObReferenceObject这部分应该是有问题无疑了。

  • 相关阅读:
    Android文字上下滚动
    Java怎么去除字符串中的多个空白【split(" ")与split("\s+")】
    Java:Scanner.nextLine()和Scnner.next()的区别,以及多条nextLine()输入问题的解决方法
    SQL server报错18456(又名SQL server怎么用sa登入)
    Android Studio快速自动生成findViewById
    java.sql.Date和java.util.Date的联系与区别【转载】
    Linux Crontab 不执行
    PostgreSQL 全量 增量 归档 备份工具 pg_rman介绍与使用(转载)
    Rman备份的保留策略(retention policy) (转载)
    Oracle RMAN block_change_tracking(块更改追踪)
  • 原文地址:https://www.cnblogs.com/flyor/p/8887738.html
Copyright © 2011-2022 走看看