文件监控技术分为hook,过滤,回调。
文件监控系统就是绑定和过滤,进程的各种操作被封装成一个IRP,再发给驱动的设备对象,驱动的设备对象通过各种分发函数处理操作,过滤就是在设备对象上面再绑定一个新的设备对象,先经过我们的设备对象。在去原来的设备对象。
分层驱动框架与过滤
irp从上往下经过不同设备栈,先经过文件过滤驱动设备(由文件过滤驱动创建),文件卷设备(盘符C盘D盘这些),磁盘设备。栈层n有个默认值,每个卷文件设备对象就有一个文件过滤驱动设备。
API流程
sfilter内部实现
控制设备(接受我们自己的客服端)----过滤设备(接受别的进程的IRP) IRP栈每层对应的设备不同
过滤
分层驱动中再加一层而不影响它的上下层,以过滤它们之间的数据,对数据或行为进行安全控制。过滤是通过设备绑定实现的。
绑定
设备栈绑定的形式。驱动自己生成一个设备(过滤设备),调用系统提供的绑定API,绑定到目标设备上。并返回一个在未绑定之前目标设备所在设备栈的最顶层设备(TargetDevice,为了继续下发)。这样发往下层的IRP或者发往上层的数据都会被过滤设备截获。
设备对象有个DriverObject指针,指向谁创建点的,nextDevice是个由这个驱动创建的设备对象链表,还有一个AttachDevice,任何设备对象有一个设备栈,层层下发。
AttachedDevice需要记录在DEVICE_EXTENSION中,以便调用IoCallDriver()继续下发IRP
设备对象中有两个设备指针:
驱动对象生成的设备对象通过NextDevice指针组织在一个链表里
ATTachedDevice:绑定的设备对象。
!devobj 地址 查看设备对象信息
!drvobj查看驱动对象信息
!devstack查看设备栈
!devnode 0 1系统设备树
控制设备
DriverEntry中创建,接收自己客户端的IRP
过滤设备
绑定时候创建,在设备栈上接收其他R3程序的IRP。
分发函数将接收各进程IRP
如何区分自己和别的进程发的IRP?(FilterCreate(PDEVICE_OBJECT DeviceObject,PIRP pIrp))
自己进程IRP发给控制设备,在DriverEntry创建的对象,由一个全局指针保存起来,以后跟DeviceObject比较,相等就是发给控制设备,然后如果不同DeviceObject的DriverObject看看和过滤设备对象的DriverObject一样不一样。
#define IS_MY_DEVICE_OBJECT(_devObj) (((_devObj)!=NULL)&&((_devObj)->DriverObject==gSFilterDriverObject)&&((_devObj)->DeviceExtension!=NULL)//DeviceExtension是原来最上面的设备对象
#define IS_MY_CONTROL_DEVICE_OBJCET(_devObj)(((_devObj)==gSFilterControlDriverObject)?(ASSERT(((_debObj)->DriverObject==gSFilterDriverObject)&&((_devObj)->DeviceExtension==NULL)),TRUE:FALSE)
文件系统过滤框架
Filemon //不常见 不能动态监控移动设备
Sfilter //走在被淘汰的路上
Minifilter //未来主流
Filespy //不常见
Sfilter总体流程
创建控制设备
创建控制设备符号链接
过滤分发函数
Fastio
过滤与绑定
生成一个过滤设备
IoRegisterFsRegistrationChange( DriverObject,SfFsNotification ); (文件系统设备绑定)
SfFsControl (卷设备绑定)一个驱动,看见几个文件系统设备,看见几个卷设备,对应每一个设备就生成相应设备附载上去,然后进行相应处理。
绑定文件系统是在回调中(也就是上面的SfFsNotification),绑定卷设备在分发函数中 在绑定文件系统后 在FILR_SYSTEM_COMTEL中就会收到卷设备创建的信息 就可以绑定卷设备了 在分发函数中创建过滤设备对象
比如一个U盘插入电脑 ,它第一步需要创建一个文件对象,然后再创建一个卷设备
我们要动态Attach到这上面,我们需要Hook这个过程,而这个HOOK过程,我们用回调的方法
这个回调的作用是监视创建文件设备行为和绑定文件设备,绑定文件设备后,当这个U盘要创建卷设备的时候就会被我拦截到,这个时候我们就可以生成过滤设备绑定上去了
动态监控卷的挂载(VolumeMounting)概述: 一个新的存储介质被系统发现并在文件系统中创建一个新的卷Volume的过程被称为Mounting。其过程是: 文件系统的控制设备对象(FSCDO)将得到一个IRP,其主功能码(MajorFunctionCode)为IRP_MJ_FILE_SYSTEM_CONTRO。副功能码(MinorFunctionCode)为IRP_MN_MOUNT,呵呵,仔细想一下,不要死板,如果我们创建一个设备对象,并将其绑定到文件系统控制设备对象的设备对象栈上,那么我们的设备对象不就能接受到这个IRP了吗!这样,我们就能时刻的知道新卷的产生了。解决了上面的问题。
这里说说FileMon过滤
FileMon里的方法:
枚举26个盘符,打开文件,获得FileObjectDeviceObject.
然后通过自己驱动生成一个过滤设备,Attach过滤设备到DeviceObject上
无法监控类似U盘等动态加载上去的
而IoRegisterFsRegistrationChange是动态获取的
Fastio
文件系统除了处理正常的IRP之外,还要处理所谓的FastIo.
FastIo是Cache Manager调用所引发的一种没有irp的请求。换句话说,除了正常的Dispatch Functions之外,你还得为DriverObject撰写另一组Fast Io Functions.
这组函数的指针在driver->FastIoDispatch
Sfilter代码分析
把DriverEntry保存在一个全局指针里面,将驱动对象保存起来,绑定文件会用到。
gSFilterDriverObject = DriverObject;//用来判断我们的控制和过滤设备对象是不是我们的驱动的
//
// Initialize the lookaside list for name buffering. This is used in
// several places to avoid having a large name buffer on the stack. It is
// also needed by the name lookup routines (NLxxx).
//
ExInitializePagedLookasideList( &gSfNameBufferLookasideList,
NULL,
NULL,
0,
SFILTER_LOOKASIDE_SIZE,
SFLT_POOL_TAG_NAME_BUFFER,
0 );
创建控制设备对象, 为了区分是自己应用还是其他发下来的IRP,所以要把控制设备对象放到一个全局指针,
status = IoCreateDevice( DriverObject,
0, //has no device extension
&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
接下来就是创建设备对象
首先在
"\\FileSystem\\Filters\\SFilterDrv"
中创建,如果创建失败就在
"\\FileSystem\\SFilterDrv"
与NTMod不同的是 NTmodel中我们创建对象的时候对象是UNKNOW_DEVICE 这里我们创建的是磁盘文件系统设备 FILE_DEVICE_DISK_FILE_SYSTEM
status = IoCreateDevice( DriverObject,
0, //has no device extension
&nameString,
FILE_DEVICE_DISK_FILE_SYSTEM,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&gSFilterControlDeviceObject );
设置通信方式,控制设备对象bufferio,过滤跟被绑定的设备对象一致
gSFilterControlDeviceObject->Flags |= DO_BUFFERED_IO;//控制设备对象是buffer_io,过滤设备对象通信方式要和被绑定的设备相同
初始化分发函数
FilterCreate(创建)
FilterRead(一般不拦,加解密处理)
FilterWrite(修改,加解密处理)
FilterSetInfo(删,重命名)
FilterClose(一般不拦)
FilterClean(写关闭等)
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i] = SfPassThrough;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;
//DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = SfCreate;
//DriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = SfCreate;
DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SfFsControl;//移动(U盘)卷设备绑定
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SfCleanupClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SfCleanupClose;
NTSTATUS
SfPassThrough (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION pIrp = IoGetCurrentIrpStackLocation( Irp );
ASSERT(!IS_MY_CONTROL_DEVICE_OBJECT( DeviceObject ));//不能是我们的控制设备对象
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
ASSERT(pIrp->MajorFunction != IRP_MJ_POWER);
IoSkipCurrentIrpStackLocation( Irp );//忽略这层过滤
直接向下发给设备栈
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->NLExtHeader.AttachedToDeviceObject,
Irp );
}
然后处理fastio
DriverObject->FastIoDispatch = fastIoDispatch;//fastIO,不走IRP直接从缓存中读取数据的
然后为驱动注册一个回调,用来监控移动设备,绑定文件系统设备对象,为绑定卷设备对象提供条件,
status = IoRegisterFsRegistrationChange( DriverObject, SfFsNotification );//文件系统设备绑定,是卷设备绑定的前提,监控移动设备,比如插入U盘,先生成一个文件设备对象,根据文件类型,然后系统发送一个MountIRP创建属于U盘的卷设备对象,固定设备也会去绑定
那么驱动怎么拿到这个卷设备对象?监控Mon这个IRP,发给文件设备对象,可以先生成一个过滤设备,绑定到这个文件系统设备对象,然后就监控这个irp,能拿到mon这个irp,拿到这个动态生成的卷设备对象,再生成一个过滤设备对象,然后绑定到这个卷设备对象上,然后才能监控这个移动设备,
IoGetDeviceObjectPointer函数的功能是:
它从下层的设备对象名称来获得下层设备指针。该函数造成了对下层设备对象以及下层设备对象所对应的文件对象的引用。
如果本层驱动在卸载之前对下层的设备对象的引用还没有消除,则下层驱动的卸载会被停止。因此必须要消除对下层设备对象的引用。
但是程序一般不会直接对下层设备对象的引用减少。因此只要减少对文件对象的引用就可以减少文件对象和设备对象两个对象的引用。
事实上,IoGetDeviceObjectPointer返回的并不是下层设备对象的指针,而是该设备堆栈中顶层的设备对象的指针。
IoGetDeviceObjectPointer函数的调用必须在 IRQL=PASSIVE_LEVEL的级别上运行。
*/
status = IoGetDeviceObjectPointer( &nameString, FILE_READ_ATTRIBUTES, &fileObject, &rawDeviceObject );
if (NT_SUCCESS( status ))
{
SfFsNotification( rawDeviceObject, TRUE );
ObDereferenceObject( fileObject ); //这里减少对文件对象的引用
}
RtlInitUnicodeString( &nameString, L"\\Device\\RawCdRom" );
status = IoGetDeviceObjectPointer( &nameString, FILE_READ_ATTRIBUTES, &fileObject, &rawDeviceObject );
if (NT_SUCCESS( status ))
{
SfFsNotification( rawDeviceObject, TRUE );
ObDereferenceObject( fileObject );//这里减少对文件对象的引用
}
}
ClearFlag( gSFilterControlDeviceObject->Flags, DO_DEVICE_INITIALIZING );
return STATUS_SUCCESS;
}
去看看SfFsNotification
它创建一个设备对象,并将它附加到指定的文件系统控制设备对象(File System CDO)的对象栈上。这就允许这个设备对象过滤所有发送给文件系统的请求。
这样,我们就能够获得一个挂载卷的请求,就可以附加到这个新的卷设备对象的设备对象栈上。
当SfFsNotification函数调用完毕以后,我们的过滤驱动设备对象就能够接收到发送到文件系统CDO的请求,即接收到IRP_MJ_FILE_SYSTEM_CONTROL,或者说,文件系统控制设备已经被绑定,可以动态监控卷的挂载了。那么以后的工作就是要完成对卷的监控绑定了。
//回调例程,当文件系统被激活或者撤销时调用//在该例程中,完成对文件系统控制设备对象的绑定.
绑定文件系统: 首先,我们需要知道当前系统中都有那些文件系统,例如:NTFS,FAT32,CDFS。因为,卷设备对象是由文件系统创建的。 其次,我们要知道什么时候去绑定文件系统。当一个卷设备对象动态产生的时候,其对应的文件系统就被激活。例如,如果一个FAT32的U盘被插入到电脑上,则对应的FAT32文件系统就会被激活,并创建一个“J:”的卷设备对象。 IoRegisterFsRegistrationChange是一个非常有用的系统调用。这个调用注册一个回调函数,当系统中有文件系统被激活或者撤销时,该回调函数就被调用。OhGood,那么我们就可以在这个回调函数中去绑定文件系统的控制设备对象。
这里要注意:文件系统的加载和卷的挂载是两码事,卷的挂载是建立在文件系统被激活的基础上。当一个文件系统被激活后,才能创建卷设备对象
SfFsNotification是我们要注册的回调函数,它调用SfAttachToFileSystemDevice完成真正的设备绑定。当然,它还有其他功能,代码说明问题。 SfAttachToFileSystemDevice创建过滤设备对象,并调用我们设备绑定外包函数:SfAttachDeviceToDeviceStack来将过滤设备对象绑定到文件系统控制设备对象的设备栈上。 这样,我们的过滤设备对象就能接受到发送到FSCDO的IRP_MJ_FILE_SYSTEM_CONTRO的请求,动态监控卷的挂载。那么以后的工作就是完成对卷的监控绑定了。
回调例程,当文件系统被激活或者撤销时调用
//在该例程中,完成对文件系统控制设备对象的绑定.//
//例程描述:
这个例程在文件系统激活或者销毁的时被调用
这个例程创建一个设备对象将它附加到指定的文件系统控制设备对象
//的对象栈上,这就允许这个设备对象过滤所有发送给文件系统的请求.
//这样,我们就能获得一个挂载卷的请求,就可以附加到这个新的卷设备对象
//的设备对象栈上//
VOID
SfFsNotification (
IN PDEVICE_OBJECT DeviceObject,//文件系统对象,它指向文件系统的控制设备对象(CDO)。即 被激活或则撤消的File System CDO
IN BOOLEAN FsActive//true插入时候,false卸载时候 )
{
PNAME_CONTROL devName;
PAGED_CODE();
devName = NLGetAndAllocateObjectName( DeviceObject,
&gSfNameBufferLookasideList );
if (devName == NULL) {
SF_LOG_PRINT( SFDEBUG_DISPLAY_ATTACHMENT_NAMES,
("SFilter!SfFsNotification: Not attaching to %p, insufficient resources.\n",
DeviceObject) );
return;
}
SF_LOG_PRINT( SFDEBUG_DISPLAY_ATTACHMENT_NAMES,
("SFilter!SfFsNotification: %s %p \"%wZ\" (%s)\n",
(FsActive) ? "Activating file system " : "Deactivating file system",
DeviceObject,
&devName->Name,
GET_DEVICE_TYPE_NAME(DeviceObject->DeviceType)) );
if (FsActive) {
SfAttachToFileSystemDevice( DeviceObject, devName );
} else {
SfDetachFromFileSystemDevice( DeviceObject );
}
NLFreeNameControl( devName, &gSfNameBufferLookasideList );
}
要记得正在初始化清理标志去掉(DriverEntry系统来做)
ClearFlag( newDeviceObject->Flags, DO_DEVICE_INITIALIZING );
绑定时通过这个函数绑定到文件设备对象上。
status = SfAttachDeviceToDeviceStack( newDeviceObject,
DeviceObject,
&devExt->NLExtHeader.AttachedToDeviceObject );//同这个函数绑定到文件设备对象上
这个绑定了文件设备对象,这时我们就可以收到IRP_MJ_FILE_SYSTEM_CONTROL IRP 在这个例程里我们就可以实现对卷的绑定,mon是这个DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] 的子功能号。
(注意 这里必须绑定了文件设备后才能收到哦)
看下IRP_MJ_FILE_SYSTEM_CONTROL的例程
NTSTATUS
SfFsControl (
IN PDEVICE_OBJECT DeviceObject,//文件系统设备对象
IN PIRP Irp
)
/*++
Routine Description:
This routine is invoked whenever an I/O Request Packet (IRP) w/a major
function code of IRP_MJ_FILE_SYSTEM_CONTROL is encountered. For most
IRPs of this type, the packet is simply passed through. However, for
some requests, special processing is required.
Arguments:
DeviceObject - Pointer to the device object for this driver.
Irp - Pointer to the request packet representing the I/O request.
Return Value:
The function value is the status of the operation.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
PAGED_CODE();
ASSERT(!IS_MY_CONTROL_DEVICE_OBJECT( DeviceObject ));
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
switch (irpSp->MinorFunction) {
case IRP_MN_MOUNT_VOLUME:
return SfFsControlMountVolume( DeviceObject, Irp );//就这个函数绑定卷设备
case IRP_MN_LOAD_FILE_SYSTEM:
return SfFsControlLoadFileSystem( DeviceObject, Irp );
case IRP_MN_USER_FS_REQUEST:
{
switch (irpSp->Parameters.FileSystemControl.FsControlCode) {
case FSCTL_DISMOUNT_VOLUME:
{
PSFILTER_DEVICE_EXTENSION devExt = DeviceObject->DeviceExtension;
SF_LOG_PRINT( SFDEBUG_DISPLAY_ATTACHMENT_NAMES,
("SFilter!SfFsControl: Dismounting volume %p \"%wZ\"\n",
devExt->NLExtHeader.AttachedToDeviceObject,
&devExt->NLExtHeader.DeviceName) );
break;
}
}
break;
}
}
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->NLExtHeader.AttachedToDeviceObject,
Irp );
}
NTSTATUS SfFsControlMountVolume( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
/*
参数说明:
DeviceObject: 它是我们创建的设备对象。它被绑定到文件系统CDO的设备栈上。
Irp: 它是发送给文件系统CDO的挂载请求。它是一个新卷的挂载请求。
*/
PSFILTER_DEVICE_EXTENSION devExt = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
PDEVICE_OBJECT storageStackDeviceObject;
// newDeviceObject是将要绑定到文件系统的卷设备对象上。或者说,这个newDeviceObject要被绑定到新挂载卷的设备卷上。
PDEVICE_OBJECT newDeviceObject;
PSFILTER_DEVICE_EXTENSION newDevExt;
NTSTATUS status;
BOOLEAN isShadowCopyVolume;
PFSCTRL_COMPLETION_CONTEXT completionContext;
PAGED_CODE();
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
ASSERT(IS_DESIRED_DEVICE_TYPE(DeviceObject->DeviceType));
/*
在把IRP发送到文件系统之前,当挂载请求到来时,Vpb->RealDevice保存的是:将要被挂载的磁盘设备对象。
storageStackDeviceObject事先保存了VPB的值,这是因为:当IRP下发给底层驱动后,可能会改变。
*/
storageStackDeviceObject = irpSp->Parameters.MountVolume.Vpb->RealDevice;
status = SfIsShadowCopyVolume ( storageStackDeviceObject, &isShadowCopyVolume );
if (NT_SUCCESS(status) &&
isShadowCopyVolume &&
!FlagOn(SfDebug,SFDEBUG_ATTACH_TO_SHADOW_COPIES))
{
UNICODE_STRING shadowDeviceName;
WCHAR shadowNameBuffer[MAX_DEVNAME_LENGTH];
RtlInitEmptyUnicodeString( &shadowDeviceName, shadowNameBuffer, sizeof(shadowNameBuffer) );
SfGetObjectName( storageStackDeviceObject, &shadowDeviceName );
SF_LOG_PRINT( SFDEBUG_DISPLAY_ATTACHMENT_NAMES,
("SFilter!SfFsControlMountVolume Not attaching to Volume %p \"%wZ\", shadow copy volume\n",
storageStackDeviceObject,
&shadowDeviceName) );
//如果不打算绑定卷影则跳到下一层驱动
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( devExt->AttachedToDeviceObject, Irp );
}
status = IoCreateDevice( gSFilterDriverObject,
sizeof( SFILTER_DEVICE_EXTENSION ),
NULL,
DeviceObject->DeviceType,
0,
FALSE,
&newDeviceObject );
/*如果不把IRP发送到文件系统中,那么文件系统就不会收到这个卷的挂载请求。*/
if (!NT_SUCCESS( status ))
{
KdPrint(( "SFilter!SfFsControlMountVolume: Error creating volume device object, status=%08x\n", status ));
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
status = IoCreateDevice( gSFilterDriverObject,
sizeof( SFILTER_DEVICE_EXTENSION ),
NULL,
DeviceObject->DeviceType,
0,
FALSE,
&newDeviceObject );//又新生成了一个过滤设备对象,要把它绑定到将要生成的卷设备对象上
这时irp还没下发,卷设备对象,还没创建出来
if (IS_WINDOWSXP_OR_LATER()) {
KEVENT waitEvent;
KeInitializeEvent( &waitEvent,//设置一个事件
NotificationEvent,
FALSE );
IoCopyCurrentIrpStackLocationToNext ( Irp );//先把IRP往下发,因为这个时候卷设备对象还没创建出来
IoSetCompletionRoutine( Irp,
SfFsControlCompletion,//为这个IRP设置一个完成例程,就是通知下已经完成
&waitEvent, //context parameter
TRUE,
TRUE,
TRUE );
status = IoCallDriver( devExt->NLExtHeader.AttachedToDeviceObject, Irp );//通过这个把IRP往下发
//
// Wait for the operation to complete
//
if (STATUS_PENDING == status) {
status = KeWaitForSingleObject( &waitEvent,//等待IRP结束
Executive,
KernelMode,
FALSE,
NULL );
status = SfFsControlMountVolumeComplete( DeviceObject,//完成之后创建出来的卷设备对象
Irp,
newDeviceObject );
在上面最后函数里attch
status = SfAttachToMountedDevice( vpb->DeviceObject,//把我们过滤设备对象绑定到新的卷设备对象
NewDeviceObject );
win2000,irql比较高要一个工作者线程,先分配一个结构体。
ExInitializeWorkItem( &completionContext->WorkItem,
SfFsControlMountVolumeCompleteWorker,
completionContext );
completionContext->DeviceObject = DeviceObject;
completionContext->Irp = Irp;
completionContext->NewDeviceObject = newDeviceObject;
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine( Irp,
SfFsControlCompletion,
&completionContext->WorkItem,
TRUE,
TRUE,
TRUE );
//
// Call the driver
//
status = IoCallDriver( devExt->NLExtHeader.AttachedToDeviceObject, Irp );
然后看完成例程里面,如果xp直接设置事件,win2000就会在完成例程里执行工作者线程。绑定申请卷设备对象。
#if WINVER >= 0x0501
if (IS_WINDOWSXP_OR_LATER()) {
//
// On Windows XP or later, the context passed in will be an event
// to signal.
//
KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
} else {
#endif
//
// For Windows 2000, if we are not at passive level, we should
// queue this work to a worker thread using the workitem that is in
// Context.
//
if (KeGetCurrentIrql() > PASSIVE_LEVEL) {
//
// We are not at passive level, but we need to be to do our work,
// so queue off to the worker thread.
//
ExQueueWorkItem( (PWORK_QUEUE_ITEM) Context,
DelayedWorkQueue );
} else {
PWORK_QUEUE_ITEM workItem = Context;
//
// We are already at passive level, so we will just call our
// worker routine directly.
//
(workItem->WorkerRoutine)(workItem->Parameter);
}
#if WINVER >= 0x0501
}
#endif
return STATUS_MORE_PROCESSING_REQUIRED;
}
然后在看我们的分发函数SFcreate
NTSTATUS
SfCreate (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function filters create/open operations. It simply establishes an
I/O completion routine to be invoked if the operation was successful.
Arguments:
DeviceObject - Pointer to the target device object of the create/open.
Irp - Pointer to the I/O Request Packet that represents the operation.
Return Value:
The function value is the status of the call to the file system's entry
point.
--*/
{
NTSTATUS status;
PNAME_CONTROL fileName = NULL;
PSFILTER_DEVICE_EXTENSION devExt = (PSFILTER_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
BOOLEAN cacheName;
PAGED_CODE();
//
// If this is for our control device object, don't allow it to be opened.
//
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) {
//
// Sfilter doesn't allow for any communication through its control
// device object, therefore it fails all requests to open a handle
// to its control device object.
//
// See the FileSpy sample for an example of how to allow creates to
// the filter's control device object and manage communication via
// that handle.
//
//Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
//return STATUS_INVALID_DEVICE_REQUEST;
return STATUS_SUCCESS;
}
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
//
// If debugging is enabled, do the processing required to see the packet
// upon its completion. Otherwise, let the request go with no further
// processing.
//
if (!FlagOn( SfDebug, SFDEBUG_DO_CREATE_COMPLETION |//正常情况下sfDebug从注册表里读配置,这里硬编码改名字
SFDEBUG_GET_CREATE_NAMES|
SFDEBUG_DISPLAY_CREATE_NAMES )) {
//
// We don't want to get filenames, display filenames, or
// call our completion routine. Don't put us on the stack
// and call the next driver.
//
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->NLExtHeader.AttachedToDeviceObject,
Irp );
}
if (FlagOn( SfDebug, SFDEBUG_GET_CREATE_NAMES |
SFDEBUG_DISPLAY_CREATE_NAMES ) &&
!FlagOn(devExt->Flags,SFDEVFL_DISABLE_VOLUME)) {
//
// Debugging specifies that we need to get the filename
//
NAME_LOOKUP_FLAGS LookupFlags = 0x00000000;
//
// If DosName has been set, indicate via flags that we
// want to use it when getting the full file name.
//
if (devExt->NLExtHeader.DosName.Length != 0) {
SetFlag( LookupFlags, NLFL_USE_DOS_DEVICE_NAME );
}
//
// Indicate we are in pre-create
//
SetFlag( LookupFlags, NLFL_IN_CREATE );
if (FlagOn( irpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) {
//
// The file is being opened by ID, not file name.
//
SetFlag( LookupFlags, NLFL_OPEN_BY_ID );
}
if (FlagOn( irpSp->Flags, SL_OPEN_TARGET_DIRECTORY )) {
//
// The file's parent directory should be opened
//
SetFlag( LookupFlags, NLFL_OPEN_TARGET_DIR );
}
//
// Retrieve the file name. Note that in SFilter we don't do any name
// caching.
//
status = NLAllocateNameControl( &fileName, &gSfNameBufferLookasideList );//拿到文件名字,先分配一个结构
if (NT_SUCCESS( status )) {
//
// We are okay not checking the return value here because
// the GetFullPathName function will set the Unicode String
// length to 0. So either way, in an error it will print an empty string
//
status = NLGetFullPathName( irpSp->FileObject,//拿到文件全路径
fileName,
&devExt->NLExtHeader,
LookupFlags,
&gSfNameBufferLookasideList,
&cacheName );
}
}
if (FlagOn( SfDebug, SFDEBUG_DISPLAY_CREATE_NAMES |
SFDEBUG_DO_CREATE_COMPLETION ) &&
!FlagOn(devExt->Flags,SFDEVFL_DISABLE_VOLUME)) {
//
// Debugging flags indicate we must do completion.
// Note that to display file names we must do completion
// because we don't know IoStatus.Status and IoStatus.Information
// until post-create.
//
KEVENT waitEvent;
//
// Initialize an event to wait for the completion routine to occur
//
KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );
//
// Copy the stack and set our Completion routine
//
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine(
Irp,
SfCreateCompletion,
&waitEvent,
TRUE,
TRUE,
TRUE );
//
// Call the next driver in the stack.
//
status = IoCallDriver( devExt->NLExtHeader.AttachedToDeviceObject, Irp );
//
// Wait for the completion routine to be called
//
if (STATUS_PENDING == status) {
NTSTATUS localStatus = KeWaitForSingleObject( &waitEvent,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT(STATUS_SUCCESS == localStatus);
}
//
// Verify the IoCompleteRequest was called
//
ASSERT(KeReadStateEvent(&waitEvent) ||
!NT_SUCCESS(Irp->IoStatus.Status));
//
// If debugging indicates we should display file names, do it.
//
if (irpSp->Parameters.Create.Options & FILE_OPEN_BY_FILE_ID) {
SF_LOG_PRINT( SFDEBUG_DISPLAY_CREATE_NAMES,
("SFilter!SfCreate: OPENED fo=%p %08x:%08x %wZ (FID)\n",
irpSp->FileObject,
Irp->IoStatus.Status,
Irp->IoStatus.Information,
&fileName->Name) );
} else {
SF_LOG_PRINT( SFDEBUG_DISPLAY_CREATE_NAMES,
("SFilter!SfCreate: OPENED fo=%p st=%08x:%08x %wZ\n",
irpSp->FileObject,
Irp->IoStatus.Status,
Irp->IoStatus.Information,
&fileName->Name) );
}
//
// Release the name control structure if we have
//
if (fileName != NULL) {
NLFreeNameControl( fileName, &gSfNameBufferLookasideList );
}
//
// Save the status and continue processing the IRP
//
status = Irp->IoStatus.Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
} else {
//
// Free the name control if we have one
//
if (fileName != NULL) {
NLFreeNameControl( fileName, &gSfNameBufferLookasideList );
}
//
// Debugging flags indicate we did not want to display the file name
// or call completion routine.
// (ie SFDEBUG_GET_CREATE_NAMES && !SFDEBUG_DO_CREATE_COMPLETION)
//
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->NLExtHeader.AttachedToDeviceObject,
Irp );
}
}
sfilter流程
创建控制设备对象
创建控制设备对象符号链接
注册过滤分发函数
Fastio(上面修禁用了Fastio,如果查看fastio相关代码分析,参考资料文章里有)
过滤绑定,1生成一个过滤设备,绑定到目标设备对象上。2注册回调IoRegisterFsRegistrationChange(DriverObject,SfFsNotification)监控文件设备对象,绑定到文件系统设备上,3发送IRP请求就会被文件过滤设备监控到,然后监控到MountIRP就能拿到卷设备对象,生成过滤设备对象,监控到卷设备对象。
参考资料
https://blog.youkuaiyun.com/cosmoslife/article/details/7727688
https://blog.youkuaiyun.com/zhuhuibeishadiao/article/details/51178901
https://bbs.pediy.com/thread-152338.htm