因为没写过FSD过滤驱动,所以拿来练练手,没有什么技术含量。参考自Win内核安全与驱动开发。

先梳理一下大概的流程,就是怎么去绑定设备栈、怎么去过滤各种请求的。

首先肯定是要绑定设备栈的,来看下怎么绑定的设备栈。

先确定绑定的对象是什么,文件系统的设备是分两个部分的,分别是卷设备和控制设备。其中,卷设备是每有一个卷就会对应有一个卷设备,这么说可能不太清楚我画了一张图。

注意,这里的卷设备并不是磁盘的卷设备。而是文件系统生成的,用来对应每一个卷的卷设备。一定要区分开这两者。绑定这些个卷设备也不能够直接进行,因为这些由文件系统生成的卷设备是无名的设备,根本没法直接绑定。这里使用的办法是先绑定FSD的控制设备,当控制设备接收到新建卷设备的请求这时就是可以绑定到卷设备上了。但是,FSD的控制设备也不是直接就可以绑定上的,所以也要想办法去获取下,这里用的是注册文件系统变动回调函数的方法。IoRegisterFsRegistrationChange()函数就是用来注册FSD过滤驱动的,这个回调在文件系统发生变动时会被调用,比如说文件系统被挂载时。回调函数和其他的系统事件通知类似都是有固定格式的,函数原型如下。

 VOID NotifyFunction(PDEVICE_OBJECT DeviceObject,BOOLEAN Type)
{
//其中DeviceObject就可以是FSD加载时的控制设备指针,Type参数表示是卸载还是加载
}

注册了这个回调函数后,在里面对DeviceObject进行一下判断看一下是不是自己想要的文件系统,因为一个电脑上有很多的文件系统。

小结一下,目前为止我们知道了要进行这么一个流程:

分三步才能实现最终绑定。而我们的目的只是为了去绑定FSD卷设备,因为这个卷设备才是真正去接受和处理读写请求的设备。

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegPath)
{
UNICODE_STRING DeviceName;
DEVICE_OBJECT DeviceObject;
NTSTATUS Status;
FilterDriver=DriverObject;//全局变量
RtlInitUnicodeString(&DeviceName,L"\\FileSystem\\Filters\\DemoDriver");
Status=IoCreateDevice(DriverObject,
,
&DeviceName,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&DeviceObject);
if(!NT_STATUS(Status))
{
//设备创建失败
return Status;
}
for(i=;i<IRP_MJ_MAXIMUM_FUNCTION)
{
DriverObject->MajorFunction[i]=CallNextIrpLocation;
}
//DriverObject->MajorFunction[]
//当前设备绑定到文件系统的控制设备(CDO)上
//注册一个文件系统变动回调函数
Status=IoRegisterFsRegistrationChange(DriverObject,FsChangeCallBack);
if(!NT_STATUS(Status))
{
IoDeleteDevice(DeviceObject);
return Status;
} }

我这里把MajorFunctionp[]的分发函数一些给省略了,后面会写出来。再来看一下FsChangeCallBack也就文件系统变动回调函数的内容,这个函数的目的是要绑定FSD控制设备。

 VOID FsChangeCallBack(PDEVICE_OBJECT DeviceObject,BOOLEAN Create)
{
NTSTATUS Status;
UNICODE_STRING CompareName;
UNICODE_STRING DriverName;
DEVICE_OBJECT FilterDevice;
RtlInitUnicodeString(&CompareName,L"\\FileSystem\\Fs_Rec");
if(Create)
{
//附加当前过滤驱动到CDO上去
//附加前要注意两点
//1.是不是自己想要的CDO类型
//2.是不是文件识别器
if(IS_WANTED_DEVICE_TYPE(DeviceObject->DriverObject->type))
{
//
GetDriverNameFromDeviceObject(&DriverName,DeviceObject);
if(!RtlCompareUnicodeString(&DriverName,&CompareName))
{
Status=IoCreateDevice(FilterDriver,
sizeof(DEVICE_EXTENSION),
NULL,
DeviceObject->DeviceType,
,
FALSE,
&FilterDevice);
if(!NT_SUCCESS(Status))
{
return Status;
}
FilterDevice->Flags=DeviceObject->Flags;
Status=(PVOID)IoAttachDeviceToDeviceStack(FilterDevice,DeviceObject);
if(!Status)
{
IoDeleteDevice(FilterDevice);
return Status;
}
DeviceObject->DeviceExtension->NextDevice=(PDRIVER_OBJECT)Status; } }
}
else
{
return ;
}
return ;
}

做了简单的判断:

  1. 判断是挂载文件系统的通知
  2. 判断是想要的文件系统类型
  3. 判断不是文件识别器

这里FSD的控制设备就被绑定上了,然后再去过滤FSD控制设备识别器接受的卷挂载请求。过滤请求这里有一点要注意的,因为当请求发下来时卷设备还没真正创建,所以不能去绑定,要等Irp返回后才能去绑定。这一点跟处理过滤驱动过滤读请求时的处理是很像的,因为读请求下发下来时,还没有正在的读动作,只有等到Irp返回后才能进行过滤。具体的做法就是用完成函数,但是毕竟特殊的一点是书上是用完成函数去激活事件然后在分发例程中等待事件来进行的处理,等待事件后进行绑定。书中的解释是完成函数处于DPC的IRQL,其实我觉得放在完成函数里应该不会有问题吧,又使用了完成函数又使用了等待事件感觉好重复。这种写法以前好像没写过。

 NTSTATUS FilterAddToMount(PDEVICE_OBJECT DeviceObject,PIRP Irp)
{
//从DeviceObject中获取VPB,再从VPB中获取卷的信息
PIO_STACK_LOCATION irpSP;
PDEVICE_OBJECT RealDevice;
PDEVICE_OBJECT FilterDevice;
KEVENT NotifyEvent;
NTSTATUS Status;
PVPB vpb;
irpSP=IoGetCurrentIrpStackLocation(Irp);
RealDevice=irpSP->Parameters.MountVolume.Vpb->RealDevice;
IoCreateDevice(FilterDriver,
sizeof(DEVICE_EXTENSION),
NULL,
DeviceObject->DeviceType,
,
FALSE,
&FilterDevice);
KeInitializeEvent(&NotifyEvent,
NotificationEvent,
FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
SetEventRoutine,
&NotifyEvent,
TRUE,
TRUE,
TRUE);
IoCallDriver(Device,Irp); KeWaitForSingleObject(&NotifyEvent,
Executive,
KernelMode,
FALSE,
NULL);
if(NT_SUCCESS(irpSP->IoStatus.Status))
{
vpb=RealDevice->Vpb;
Status=IoAttachDeviceToDeviceStack(vpb->DeviceObject,FilterDevice);
}
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS; } NTSTATUS SetEventRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context)
{
KeSetEvent((PKEVENT)Context,IO_NO_INCREMENT,FALSE);
return STATUS_MORE_PROCESSING_REQUIRED; }

到这里已经绑定了卷设备,这时只要设置好分发函数就可以完成过滤了。

接下来要说的就是具体的针对每种信息的处理了,要分类来说了。

0x0 过滤文件的打开

文件打开请求的主功能号是IRP_MJ_CREATE

首先,在Irp栈中可以获得文件对象指针。FileObject=irpsp->FileObject;目录和文件都是用文件对象表示的,直接是看不出来两者的分别的。irpsp->Parameters.Create的结构如下

struct{
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT FileAttributes;
USHORT ShareAccess;
ULONG EaLength;
}

注意,打开请求是一种尝试性动作,就是说打开只有当返回成功时才有过滤的意义,获取结果要用完成函数才行。

0x1 过滤文件的删除

文件删除是分三步请求的,所以不是过滤一次就行的。删除的三步如图

打开文件的请求上面已经说过了。设置删除标志是主功能号为IRP_MJ_SET_INFORMATION的设置请求中的一种,其中irpsp->Parameters.SetFile.FileInformationClass为FileDispositionInformation。然后irp->AssociatedIrp.SystemBuffer指向如下结构

typdef struct _FILE_DISPOSITION_INFORMATION{
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION;

0x2 过滤文件的路径

我们使用文件系统过滤驱动很多时候都是为了根据路径来进行操作,要不就使用磁盘过滤驱动就可以了完全没必要使用FSD过滤驱动。

(未完待续)

一个文件系统过滤驱动的demo的更多相关文章

  1. 基于Minifilter框架的文件过滤驱动理解

    概述 Minifilter即File System Minifilter Drivers,是Windows为了简化第三方开发人员开发文件过滤驱动而提供的一套框架,这个框架依赖于一个称之为Filter ...

  2. File System Minifilter Drivers(文件系统微型过滤驱动)入门

    问题: 公司之前有一套文件过滤驱动,但是在实施过程中经常出现问题,现在交由我维护.于是在边看代码的过程中,一边查看官方资料,进行整理. 这套文件过滤驱动的目的只要是根据应用层下发的策略来控制对某些特定 ...

  3. [转载]文件过滤驱动 文件系统激活通知 IoRegisterFsRegistrationChange函数实现

    IoRegisterFsRegistrationChange 注册一个文件系统变动回调函数,用来被通知文件系统的激活和注销,激活是指第一次加载文件系统,当一个文件系统已经加载后,当加载一个同种文件系统 ...

  4. 文件过滤驱动实现目录重定向(一)good

    文件过滤驱动拦截的IRP主要包括以下几个:IRP_MJ_CREATE,文件创建操作,文件的任何操作,都是从这里开始的.IRP_MJ_CLEANUP,文件的HANDLE句柄全部关闭会触发这个消息IRP_ ...

  5. Windows 文件过滤驱动经验总结

    Windows 文件过滤驱动经验总结作者:sinister 本文转载自驱动开发网 看了 ChuKuangRen 的第二版<文件过滤驱动开发教程>后,颇有感触.我想,交流都是建立在平等的基础 ...

  6. Windbg对过滤驱动DriverEntry函数下断点技巧

    方法1: 1> 先用DeviceTree.exe查看指定的过滤驱动的Load Address(加载地址) 2> 再用LordPE.EXE查看指定过滤驱动文件的入口点地址 3> 计算过 ...

  7. 写一个EF的CodeFirst的Demo

    写一个EF的CodeFirst的Demo 今天打算写一个关于EF的CodeFirs的一个小Demo.先略说一个EF的三种与数据库,怎么说,叫映射么,好吧,那就这么叫吧,就是一个是ModelFirst就 ...

  8. ------- 软件调试——注销 QQ 过滤驱动设置的事件通知 CallBack (完)-------

    ---------------------------------------------------------------------------------- 本系列的最后一篇演示如何通过调试手 ...

  9. (转)支持 PS/2 与 USB 的键盘过滤驱动(可卸载)

    Author:  sinisterEmail:   sinister@whitecell.orgHomepage:http://www.whitecell.org Date:    2007-02-2 ...

随机推荐

  1. [opencv] 图像几何变换:旋转,缩放,斜切

    几何变换 几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动. 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定 ...

  2. hibernate的懒加载

    WHY? WHAT? HOW? 所谓懒加载(lazy)就是延时加载,延迟加载.即不是不加载,而是在需要的时候才加载. 什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载. 至于为什么要用懒加载呢 ...

  3. poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数

    Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11469   Accepted: 379 ...

  4. STM32 ------ HardFault_Hander 中断函数

    1.数组越界(对数组赋值) 正常情况,数组越界会进入 HardFault_Hander 中断函数的无线循环. 避免数组越界的一个方法是:每次使用数组前,检查要放入数据的数据长度是否大于数组长度,大于则 ...

  5. SMO详解

    转自:简书https://www.jianshu.com/p/55458caf0814 SVM通常用对偶问题来求解,这样的好处有两个:1.变量只有N个(N为训练集中的样本个数),原始问题中的变量数量与 ...

  6. Java基础-字符串(String)常用方法

    Java基础-字符串(String)常用方法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.java的API概念 Java的API(API:Application(应用) Pr ...

  7. 即时通信系统Openfire分析之三:ConnectionManager 连接管理

    Openfire是怎么实现连接请求的? XMPPServer.start()方法,完成Openfire的启动.但是,XMPPServer.start()方法中,并没有提及如何监听端口,那么Openfi ...

  8. 流媒体技术学习笔记之(十六)H264编码profile & level控制

    H.264有四种画质级别,分别是baseline, extended, main, high: 1.Baseline Profile:基本画质.支持I/P 帧,只支持无交错(Progressive)和 ...

  9. ZeroMQ API(二) 上下文

    1.创建上下文 1.1 zmq_ctx_new(3) 1.1.1 名称 zmq_ctx_new - 创建新的ZMQ上下文 1.1.2 概要 void * zmq_ctx_new(); 1.1.3 描述 ...

  10. python3使用stmplib发送邮件

    代码如下: import smtplib from email.mime.text import MIMEText from email.header import Header from email ...