用LIST_ENTRY双向链表实现文件操作的监控 ,写于08年

十二月 4th, 2009 by shyandsy
1

 这两个星期天天都在搞这个,无数个错误被修正过后,做些笔记以备以后查阅。

        为了实现机密文档的保护,文档管理系统必须对所有打开的文件进行监控,因此我们的过滤驱动需要知道当前运行时刻有几个文件被打开,它们分别被打开了几次(同一个文件可能被多次打开)。

            用户态和内核态之间的关系

                         用户态调用CreateFile函数,子系统将请求发给IO管理器,IO管理器会用该函数的

                 参数生成一个IRP,将此IRP发往底层。过滤驱动截获到IRP,开始对其操作。过滤驱动

                 操作完了之后会继续往下发,就是调用更下层驱动程序。当下层所有操作完成之后,底

                 层的完成信息会向上反。这有点像函数的嵌套调用,从最内层函数开始一级一级的返回,

                 但这只是像。下面是过滤驱动截获返回的CreateFile时的操作。

处理IRP_MJ_CREATE的核心代码():

         {
    //返回的FsContext为空时,不予以处理
    if(FileObject->FsContext == NULL)
    {
     ////////////////////////////////////////DbgPrint("\n\n\n SfCreate: FileObject->FsContext 为 NULL\n\n\n");
     status = Irp->IoStatus.Status;

     DbgPrint("\n SfCreate() : 建立链表的第一个节点时遭遇 FsContext 为 NULL   \n",status);

     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     return status;
    }

      //第一次打开链表时,链表为空,上面for语句无法运行

    if(IsListEmpty(&my_list_head))
    {
     PFILE_CONTEXT FileCtx = (PFILE_CONTEXT)ExAllocatePoolWithTag(PagedPool,sizeof(FILE_CONTEXT),SFLT_POOL_TAG);
     if(FileCtx == NULL)
      return STATUS_INSUFFICIENT_RESOURCES;

     FileCtx->FsContext = FileObject->FsContext;
     FileCtx->RefCount = 1;

     ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);//多线程安全插入方式

     DbgPrint("\n\n SfCreate() : 创建链表的第一个节点:\n ");
     DbgPrint("SfCreate() : 第一个节点中 FileCtx->FsContext: %x , FileCtx->RefCount: %u \n\n\n",FileCtx->FsContext,FileCtx->RefCount);

     status = Irp->IoStatus.Status;
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     return status;
    }

    //搜索链表,查看文件是否已经打开(链表非空时,此搜索才可以进行)
    for(p=my_list_head.Flink; p!=&my_list_head; p=p->Flink)
    {
     PFILE_CONTEXT Elem = CONTAINING_RECORD(p,FILE_CONTEXT,ListEntry);

     //文件已打开,计数器加一
     if(Elem->FsContext == FileObject->FsContext)
     {
      KIRQL irql;
     
      DbgPrint("\n\n\nSfCreate() : 遇到已打开过的文件 , 打印Elem节点信息如下 :\n ListEntry: %s, FsContext: %x, RefCount: %u\n",Elem->ListEntry,Elem->FsContext,Elem->RefCount);

      if( FileObject->FsContext == NULL)
       DbgPrint( "SfCreate() : FileObject->FsContext 为 空");

      KeAcquireSpinLock(&my_list_lock,&irql);          
      Elem->RefCount += 1;    
      KeReleaseSpinLock(&my_list_lock,irql);

      ////////////////////////////////////////DbgPrint("\n文件第 %u 次打开\n",Elem->RefCount);

      status = Irp->IoStatus.Status;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
      return status;
     } 
    }


    //文件未打开,填充节点并插入链表

    { 
     //节点内存分配
     PFILE_CONTEXT FileCtx = (PFILE_CONTEXT)ExAllocatePoolWithTag(PagedPool,sizeof(FILE_CONTEXT),SFLT_POOL_TAG);
     if(FileCtx == NULL)
     {
      DbgPrint("\n\n SfCreate() : 文件第一次打开时,内存分配失败");
      return STATUS_INSUFFICIENT_RESOURCES;
     }

     //节点内容填充
     FileCtx->FsContext = FileObject->FsContext;
     FileCtx->RefCount = 1;

     //插入节点到链表
     ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);//多线程安全插入方式
    }
   }      

大体思路说明:

        获取文件对象,确认它是对一个文件的操作(不是就就忽略,直接返回)。然后获取其FsContext,这是文件的全局唯一标识,一个文件不管打开多少次,它只有一个FsContext,不同文件的FsContext也不一样。如果链表为空,直接用这个FsContext填充数据结构,创立一个节点,然后插入链表,之后返回。如果链表不为空,则遍历搜索链表,如果查找到FsContext值一样的节点则说明该文件打开过,只需将该节点数据结构中的RefCount++。如果链表中没有搜索到相应节点,那么就要新建一个节点插入链表。

         既然监控就不能只监控打开的,自然也要监控关闭的。

         当关闭一个文件窗口的时候,IO管理器会发送一个IRP_MJ_CLEANUP。在处理这个消息的时候,我们需要做的仅仅是从链表中找到那个文件,将其RefCount–。当RefCount值为0的时候,IO管理器就会发送一个IRP_MJ_CLOSE,截获到这个消息,我们就可以将这个文件对应的节点从链表中删除了。

        OK!That’s all.

关键用法

    A.链表的遍历搜索

                   if(IsListEmpty(&my_list_head))       {    插入第一个节点 返回     }

                   for(p=my_list_head.Flink; p!=&my_list_head; p=p->Flink)    

                  从循环体内推出说明没找到,这就需要我们新建节点了

 

                  关键是要优先处理空链表的情况

    B.链表的插入删除操作

                      ExInterlockedInsertTailList(&my_list_head,&FileCtx->ListEntry,&my_list_lock);

                      删除LST_ENTRY p所指向的节点,第一个参数需要用前一个节点的LIST_ENTRY

                       ExInterlockedRemoveHeadList(p->Blink,&my_list_lock);

    C.IRQL锁与互斥体

                     KeAcquireSpinLock(&my_list_lock,&Irql);
                     Elem->RefCount -= 1;
                     KeReleaseSpinLock(&my_list_lock,Irql);

                     它通过提高IRQL来使其他线程无法执行,解决同步问题

                       ExAcquireFastMutex( &gSfilterAttachLock );                                       

                      ExReleaseFastMutex( &gSfilterAttachLock );

                      这个要相对轻量级

Posted in 文件过滤驱动

One Response to “用LIST_ENTRY双向链表实现文件操作的监控 ,写于08年”

  1. gnnw33R 说:

    Great article very important information i found here 1F35NeSAbpQrrL

Leave a Reply

վ:Ħ Ϻgay ŰĦ Ϻ տ ɽ ɽ ŰĦ Ϻ˹˾ ŰĦ ϺŰĦ ˹˾ ݰĦ ŰĦ Ħ ϺĦ ͬ־ Ѽ˹˾ ŰĦ ϺѼ ɳ᳡ shenzhen massage ŰĦ ͬ־ Ϸʿյά ϲŰĦ 人˿ఴĦ ɽ˹˾ Ϻ Ѽ˿ Ħ ֣ŰĦ ˹˾ ݼѼ Ϻ ǿ Ϻ ڰ᳡۸ °˾
ӣǵɽǵݸǵ麣ǵɽǵɳǵɽǵǵǵǵϺǵǵǵǵݸǵɽǵ麣ǵǵɽǵɽǵǵǵǵǵǵǵǵϺǵŰĦϺǵɽanĦൺǵӰװϷϺǵϾŰĦϾŰĦϾŰĦ㶫huλ㽭huңhuң
ӣҸĻȾ,ɾӹȫ.
ugg boots cheap UGG Boots UGG Boots Sale UGG Bailey Button Triplet ugg australia uk mens ugg boots new ghd hair straighteners coach handbags outlet
ӣվĶ٣ÿ쿴ɾ, ұ˫Ϊ˴Ǯ;ãӲҪɾҾɾվɾipҲ㡣
ɹͷ Ҹ θ Ƽʪõķ ҸƷ ƾõҽԺ 򾲶ʮζ ƹɹͷҩ θҩ ҽԺ ɹͷô ƹɹͷõҽԺ θҽԺ ̿۸ lovegreen californianews
ӣǵάɽǵάݸǵά麣ǵάɽǵάɳǵάɽǵάǵάǵάǵάϺǵάǵάǵάǵάݸǵάɽǵά麣ǵάǵάɽǵάɽǵάǵάǵάǵάǵάǵάǵάǵάϺװ˿㰴ĦϺǵάɽСlaohujiϷǵӰװϾСϾСϾС㽭huϷɽǵά˿㰴Ħ