驱动小牛
|
阅读:2601回复:2
今天闲来无事,研究了下SFilter,写了点总结,请指正.
我以前是从FileSpy入手的.总体感觉其实差不多,SFilter比较纯净采用的是自动加载模式,没有提供多于的控制指令.FileSpy采用手动加载,提供了日志输出和一些控制指令.
今天我先发表一下对Sfilter的分析,方便初学者.以后也可能发表一下FileSpy,WDM的过滤驱动分析,不足之处请大家指正.我的邮箱是YanDong_8212@163.com 同时,在这里对初学者说一句,学习驱动最重要是去跟踪调试,多动手. 4.1 数据结构分析 1) typedef struct _SFILTER_DEVICE_EXTENSION { // // Pointer to the file system device object we are attached to // PDEVICE_OBJECT AttachedToDeviceObject; // // Pointer to the real (disk) device object that is associated with // the file system device object we are attached to // PDEVICE_OBJECT StorageStackDeviceObject; // // Name for this device. If attached to a Volume Device Object it is the // name of the physical disk drive. If attached to a Control Device // Object it is the name of the Control Device Object. // UNICODE_STRING DeviceName; // // Buffer used to hold the above unicode strings // WCHAR DeviceNameBuffer[MAX_DEVNAME_LENGTH]; } SFILTER_DEVICE_EXTENSION, *PSFILTER_DEVICE_EXTENSION; 该结构是放在新建的DeviceObject->DeviceExtension里面的,其中AttachedToDeviceObject保存的是新建的DeviceObject关联的文件系统设备对象, StorageStackDeviceObject保存的是磁盘设备,DeviceName保存设备的名称 举例: 当U盘插入时,各结构的值如下, StorageStackDeviceObject是\Driver\Disk的一个设备, DeviceName是\Device\Harddisk2\DP(1)0-0+b, AttachedToDeviceObject是\FileSystem\FastFat的一个设备; 2) typedef struct _FSCTRL_COMPLETION_CONTEXT { // // The workitem that will be initialized with our context and // worker routine if this completion processing needs to be completed // in a worker thread. // WORK_QUEUE_ITEM WorkItem; // // The device object to which this device is currently directed. // PDEVICE_OBJECT DeviceObject; // // The IRP for this FSCTRL operation. // PIRP Irp; // // For mount operations, the new device object that we have allocated // and partially initialized that we will attach to the mounted volume // if the mount is successful. // PDEVICE_OBJECT NewDeviceObject; } FSCTRL_COMPLETION_CONTEXT, *PFSCTRL_COMPLETION_CONTEXT; 该结构用WINVER < 0x0501时的MoundVolume和LoadFileSystem处理,主要就是保存之前的信息,在完成例程里面使用. 4.2 基本函数,变量及宏 1) IS_MY_DEVICE_OBJECT(_devObj) 判断当前DeviceObject是否属于本驱动 2) IS_MY_CONTROL_DEVICE_OBJECT(_devObj) 判断当前DeviceObject是不是用于DeviceIoctrl的对象,该对象在DriverEntry中初始化,上层应用程序可以根据该设备和驱动通讯.通讯时需先建立符号连接例,具体方法参照Filespy的DriverEntry. 3) VALID_FAST_IO_DISPATCH_HANDLER 判断驱动是否支持对应的FastIo 4) SfGetFileName 获取文件名,该函数提供操作成功方式和操作失败两种方式. 5) SfGetFileNameCleanup清除上面函数中产生的文件名Buffer. 6) DELAY_ONE_MICROSECOND 这一系列宏用于Sleep(KeDelayExecutionThread)使用,驱动中的Sleep都为负数. 7) PDRIVER_OBJECT gSFilterDriverObject = NULL;驱动对象 8) PDEVICE_OBJECT gSFilterControlDeviceObject = NULL;驱动的DeviceIoctrl设备对象. 9) FAST_MUTEX gSfilterAttachLock; 关联设备时的锁 10) SFDEBUG_* 用于日志的分级控制; 11) GET_DEVICE_TYPE_NAME 获取设备的类型,需要区分设备类型时,可以使用该宏; 4.1.3 DriverEntry分析 1) 该函数是驱动初始化的入口,是驱动最先执行的函数,类似于应用层的main,WinMain; 2) SfLoadDynamicFunctions: XP及之后版本,需要动态加载一些函数,具体函数可以查看IFS帮助,其中比较关键的是RegisterFileSystemFilterCallbacks,在内存映射等操作时会在会调用SfPreFsFilterPassThrough和SfPostFsFilterPassThrough 3) SfReadDriverParameters:该函数用于读取Services\ DebugFlags,并将值赋给SfDebug,如果不设置该值就不进行调试和关联.所以,编译后需要修改此值才能调试和跟踪; 4) 一系列的初始化操作:指定DriverUnload回调,包括Attach时的锁,创建DeviceIoctrl设备对象,IRP Dispatch函数回调,FastIo Dispatch回调, RegisterFileSystemFilterCallbacks, RawDisk,RawCdrom回调(系统启动时,自动关联,系统关闭时,自动去除关联;关联之后,新mount的卷都会被截获到IRP_MJ_FILESYSTEM_CTRL,因此初始化时最重要的就是注册这一系列的回调,这些在之后的操作中会用到.) 5) ClearFlag( gSFilterControlDeviceObject->Flags, DO_DEVICE_INITIALIZING ):专门把这一句提出来是为了引起足够的重视,每个DeviceObject在初始化完毕后,都必须去掉这个标记,标识设备已经完成初始化. 4.3 DriverUnload分析 1) 该函数类似于析构函数,值在XP及之后版本存在,和DriverEntry功能刚好相反. 2) IoUnregisterFsRegistrationChange:取消注册的回调; 3)枚举本驱动的所有DevicObject,并反初始化; 4) 枚举本驱动的所有DeviceObject并删除 5) 释放FastIoDispatch内存; 4.4 SfFsNotification分析 该函数用于卷设备发生变化时的通知调用,功能很简单.内部调用SfAttachToFileSystemDevice和SfDetachToFileSystemDevice,作用对象是文件系统设备.当Attch上去后,之后的IRP_MJ_FILESYSTEM_CTRL才会通过本层驱动. 4.5流程及结构分析 整体结构: 1) 如上图所示,那么流程是(不知道能发图片不,FSDO-FileSystemDO,VDO-VolumeDO,AFDO-attach FS DO, AVDO-attch VDO,总体意思就是文件系统包含多个卷设备,Sfilter会同时Attach到文件系统和卷设备上.): FSDO->VDO->AFDO->AVDO STEP 1:文件系统创建 STEP2:文件系统自行Mount卷 STEP3:Sfilter在DriverEntry中注册的SfFsNotification 关联到文件系统对象上形成AFDO,并且枚举已有的卷设备VDO,形成AVDO 2) 当有类似U盘的设备插入,流程是: IRP_MJ_FILESYSTEM_CTRL/IRP_MN_MOUNT_VOLUME->ATTACHE到新的VDO上,形成AVDO 3) 值得注意的是,设备Mount之后默认是没有Unmount的操作的,所以一个设备对象建立以后就不会删除,直到系统重启,除非是通过DeviceIoctrl由上层主动发起. 4.6 自身数据结构的存放问题 1) 如果是整个驱动都需要的数据结构,直接作为全局或保存在DeviceExtension中; 2) 如果是跟卷相关的数据结构,最好是在Attach到卷设备时,放在卷设备的DeviceExtension中; 3) 补充说明,如果在DeviceExtension中申请了ERESOURCE,那么必须在DeleteDevice时释放ERESOURCE,因为ERESOURCE保存在系统全局列表中,否则下次调用ERESOURCE相关的函数时会报奇怪的错误. 4.7 常用手法 1) 文件名处理: 通常需要在DeviceExtension中维护一个列表,保存已打开的文件列表.当create成功时,将以FileObject或FCB为索引,进行保存;当Close时,删除对应的Entry;当其他时候需要访问时,通过FileObject或FCB进行查询获得,其他时候FileObject->FileName字段是不可信的,仅做调试参考. 2) 下发IO操作,不做任何处理: PassThrough() { IoSkipCurrentIrpStackLocation( Irp ); Status = IoCallDriver(AttachedDevice, Irp ); return status; } 3) 以某种错误直接结束IRP DenyThrough() { Irp->IoStatus.Status = status = STATUS_ACCESS_DENY; Irp->IoStatus.Information = 0; IoCompleteIrp(Irp, IO_NO_INCREMENT); Return status; } 4) 需要判断下以后的状态并做修改 KeInitializeEvent( &waitEvent, NotificationEvent, FALSE ); IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine( Irp, CreateCompletion, &waitEvent, TRUE, TRUE, TRUE ); status = IoCallDriver(AttachedToDeviceObject, Irp ); if (STATUS_PENDING == status) { NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL); ASSERT(STATUS_SUCCESS == localStatus); } //此处判断 IoCompleteIrp(Irp, IO_NO_INCREMENT); Return status; 5) 重定向操作 ExFreePool(FileObject->FileName.Buffer); FileObject->FileName.Length = 0; FileObject->FileName.MaximumLength = MAX_PATH; FileObject->DeviceObject = NULL; RtlInitUnicodeString(&FileObject->FileName, temp_buf); status = Irp->IoStatus.Status = STATUS_REPARSE; Irp->IoStatus.Information = IO_REPARSE; Return IO_REPARSE; 6) 隐藏文件 处理IRP_MJ_DIR_CTRL/ IRP_MN_QUERY_DIRECTORY中的FileBothDirectoryInformation; 7) ... |
|
沙发#
发布于:2009-11-06 15:40
支持原创。。
|
|
板凳#
发布于:2009-11-06 23:51
太好了。。。
喜欢这样的东西。。 |
|