注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

从C开始

 
 
 

日志

 
 

文件过滤驱动Sfilter学习笔记  

2011-05-15 18:04:33|  分类: 驱动编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

1.DriverEntry例程
(1)创建过滤驱动的控制设备, 以后我们的IO控制码就是发到这个设备上面

//这里的设备名与普通设备有所不同.
//当然, 最简单的可以直接写成 L"\\Device\\Filemontor";(FileMon中是这么写的, 调试过可行)
RtlInitUnicodeString( &nameString, L"\\FileSystem\\Filters\\FileMonitor" );
status = IoCreateDevice( DriverObject,
       0,                     //has no device extension 
            //这是与其他Attach到别的设备上的设备的不同之处
       &nameString,
       FILE_DEVICE_DISK_FILE_SYSTEM,
       FILE_DEVICE_SECURE_OPEN,
       FALSE,
       &gSFilterControlDeviceObject );

if (status == STATUS_OBJECT_PATH_NOT_FOUND) {

 RtlInitUnicodeString( &nameString, L"\\FileSystem\\FileMonitor" );
 status = IoCreateDevice( DriverObject,
        0,                     
        &nameString,
        FILE_DEVICE_DISK_FILE_SYSTEM,
        FILE_DEVICE_SECURE_OPEN,
        FALSE,
        &gSFilterControlDeviceObject );
}

//创建符号链接
RtlInitUnicodeString(&syblnkString, L"\\DosDevices\\FileMonitor");
status = IoCreateSymbolicLink( &syblnkString, &nameString );

if (!NT_SUCCESS(status)) {

 IoDeleteSymbolicLink( &syblnkString );
 status = IoCreateSymbolicLink( &syblnkString, &nameString );
 
 if (!NT_SUCCESS(status)) {

  KdPrint(("创建符号链接失败~\n"));
  IoDeleteDevice(gSFilterControlDeviceObject);
  return status;
 }
}

(2)设置例程
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {

 DriverObject->MajorFunction[i] = SfDispatch;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;

DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SfCleanupClose;

(3)调用IoRegisterFsRegistrationChange函数来通知我们文件系统的加载和卷的mount.


2.SfCreate 例程
(1)sfilter的原版中是这么写的
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
 Irp->IoStatus.Information = 0;
 IoCompleteRequest( Irp, IO_NO_INCREMENT );
 return STATUS_INVALID_DEVICE_REQUEST;
}

这样写的后果是我们用CreateFile函数在R3下打开此控制设备符号链接的时候失败
我刚开始学习文件过滤驱动的时候对此不是很了解, CreateFile老是失败,
起初还以为是符号链接名写错了, 后来参看了FileMon的代码才反应过来

于是修改如下:
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {
  
 Irp->IoStatus.Status = STATUS_SUCCESS;
 Irp->IoStatus.Information = FILE_OPENED;
 IoCompleteRequest( Irp, IO_NO_INCREMENT );
 return STATUS_SUCCESS;
}

(2)根据不同软件的需要, 编写此函数的接下来部分
[1]比如我们要阻止病毒在Windows目录下创建文件, 那么我们就要在此文件还没创建的时候得到此文件的全路径.
要在这个时候得到文件的路径, 楚狂人也说了有点麻烦. 下面提供一个函数给大家, 用于在文件创建前得到路径.
BOOLEAN MzfGetFileFullPathPreCreate(PFILE_OBJECT pFile, PUNICODE_STRING path )
{
  NTSTATUS status;
  POBJECT_NAME_INFORMATION pObjName = NULL;
  WCHAR buf[256] = {0};
  void *obj_ptr = NULL;
  ULONG ulRet = 0;
  BOOLEAN bSplit = FALSE;

  if (pFile == NULL) return FALSE;
  if (pFile->FileName.Buffer == NULL) return FALSE;

  pObjName = (POBJECT_NAME_INFORMATION)buf;

  if (pFile->RelatedFileObject != NULL)
   obj_ptr = (void *)pFile->RelatedFileObject;
  else
   obj_ptr = (void *)pFile->DeviceObject;

  status = ObQueryNameString(obj_ptr, pObjName, 256*sizeof(WCHAR), &ulRet);
  if (status == STATUS_INFO_LENGTH_MISMATCH)
  {
    pObjName = (POBJECT_NAME_INFORMATION)ExAllocatePool(NonPagedPool, ulRet);

    if (pObjName == NULL)  return FALSE;

    RtlZeroMemory(pObjName, ulRet);

    status = ObQueryNameString(obj_ptr, pObjName, ulRet, &ulRet);
    if (!NT_SUCCESS(status)) return FALSE;
  }

 //拼接的时候, 判断是否需要加 '\\'
 if (pFile->FileName.Length > 2 &&
  pFile->FileName.Buffer[0] != L'\\' &&
  pObjName->Name.Buffer[pObjName->Name.Length/sizeof(WCHAR) -1] != L'\\')
  bSplit = TRUE;
 
 ulRet = pObjName->Name.Length + pFile->FileName.Length;

 if (path->MaximumLength < ulRet)  return FALSE;

 RtlCopyUnicodeString(path, &pObjName->Name);
 if (bSplit)
  RtlAppendUnicodeToString(path, L"\\");

 RtlAppendUnicodeStringToString(path, &pFile->FileName);

 if ((void*)pObjName != (void*)buf)
  ExFreePool(pObjName);

 return TRUE;
}

至此, 得到了文件的路径以后, 我们就可以做出判断了, 如果要阻止文件创建,
那么直接用IoCompleteRequest函数结束此IRP即可, 否则下发
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

[2]要是像FileMon那样只是记录系统中创建了哪些文件的话, 我们可以设置此函数的完成例程.
然后等文件创建完成了之后, 只要调用 IoQueryFileDosDeviceName 函数即可知道文件的全路径了.

设置完成例程如下:
{
 KEVENT waitEvent;
 KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );

 IoCopyCurrentIrpStackLocationToNext( Irp );
 IoSetCompletionRoutine(Irp, SfCreateCompletion, &waitEvent, TRUE, TRUE, TRUE );
 status = IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp );

 if (STATUS_PENDING == status) {

  NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);
  ASSERT(STATUS_SUCCESS == localStatus);
 }
 //此处文件已经创建完成了, 我们可以调用IoQueryFileDosDeviceName函数得到文件的全路径

 //最后结束此IRP
 status = Irp->IoStatus.Status;
 IoCompleteRequest( Irp, IO_NO_INCREMENT );
 return status;
}

3.SfDispatch例程
在此例程中要判断是不是我们的控制设备, 如果使我们的控制设备, 则要处理相应的IO控制码.
否则, 下发此IRP
NTSTATUS SfDispatch ( IN PDEVICE_OBJECT DeviceObject,  IN PIRP Irp )
{
 NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION irpStack;
   
    if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {

        Irp->IoStatus.Information = 0;
        irpStack = IoGetCurrentIrpStackLocation( Irp );
  
        switch (irpStack->MajorFunction) {
   
  case IRP_MJ_DEVICE_CONTROL:
   // 此函数用来执行相应的控制码
   status = SpyCommonDeviceIoControl( Irp->AssociatedIrp.SystemBuffer,
                                                   irpStack->Parameters.DeviceIoControl.InputBufferLength,
                                                   Irp->AssociatedIrp.SystemBuffer,
                                                   irpStack->Parameters.DeviceIoControl.OutputBufferLength,
                                                   irpStack->Parameters.DeviceIoControl.IoControlCode,
                                                   &Irp->IoStatus );
   break;
   
  case IRP_MJ_CLEANUP
   status = STATUS_SUCCESS;
   break;
   
  default:
   status = STATUS_INVALID_DEVICE_REQUEST;
        }
  
        Irp->IoStatus.Status = status;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        return status;
    }
 
 //不是我们的控制设备则下发此IRP
    return SfPassThrough( DeviceObject, Irp );
}


IO控制码也可以在FASTIO例程的SfFastIoDeviceControl函数中处理,如下:
当然, 最简单的还是在FASTIO例程中返回FALSE. 这样, 系统便会调用我们上面的SfDispatch函数.
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {
  
        SpyCommonDeviceIoControl( InputBuffer,
   InputBufferLength,
   OutputBuffer,
   OutputBufferLength,
   IoControlCode,
   IoStatus );
  
        return TRUE;
}

  评论这张
 
阅读(2592)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018