阅读:2141回复:0
U盘监控创建删除重命名文件 简单例子
很简单的一个小玩意
就是监控U盘插上后 记录创建 改名字 删除操作。 这个是MINIFILTER架构 并且没有通知用户层 直接在C盘目录下创建文件 记录操作的。 后来改了改 用于记录某些EXE安装过程中 出现了哪些SYS文件 方便分析 同我的《关于隐藏文件夹 附代码 》 一样 否是从MINIFILTER的PASSTHROUGH框架改出的 在PASSTHROUGH框架上逐步添加的一些代码 1.监控USB PASSTHROUGH本身就监控 卷的加载和卸载 我只是加了少许代码 区分哪些是USB // Create IRP Get The BusType // 创建IRP 查询设备的BUSTYPE类型 为7 则说明是USB设备 // status = FltGetDiskDeviceObject(FltObjects->Volume, &DiskDeviceObject); if (status == STATUS_FLT_NO_DEVICE_OBJECT ) { return STATUS_SUCCESS; }else if(!NT_SUCCESS(status)) { return status; } KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE); Query.PropertyId = StorageDeviceProperty;//StorageDeviceType;// Query.QueryType = PropertyStandardQuery; NewIrp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_QUERY_PROPERTY, DiskDeviceObject, (PVOID)&Query, sizeof(STORAGE_DEVICE_DESCRIPTOR), (PVOID)pBuffer, sizeof(STORAGE_DEVICE_DESCRIPTOR) * 4, FALSE, &WaitEvent, &IoStatus); if (NULL == NewIrp) // can't create new irp { DbgPrint(" BusTypeUnknown \n"); return STATUS_SUCCESS; } status = IoCallDriver(DiskDeviceObject, NewIrp); if (status == STATUS_PENDING) { status = KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, NULL); status = IoStatus.Status; } if (!NT_SUCCESS(status)) { DbgPrint(" BusTypeUnknown \n"); return STATUS_SUCCESS; } Descriptor = (PSTORAGE_DEVICE_DESCRIPTOR)pBuffer; if(Descriptor->BusType == 7) { DbgPrint("GetStorageDeviceBusType SUCCEED: %d DevType:%x \n ", Descriptor->BusType, DiskDeviceObject->DeviceType); }else if (Descriptor->BusType == 3) { DbgPrint("GetStorageDeviceBusType SUCCEED: %d DevType:%x \n ", Descriptor->BusType, DiskDeviceObject->DeviceType); 我们设置一个实例上下文 记录它是USB类型 //将卷情况写入实例上下文(对于一个卷只有一个实例的情况 实例上下文即卷上下文且更有效率) // status = FltAllocateContext( FltObjects->Filter, FLT_INSTANCE_CONTEXT, CTX_INSTANCE_CONTEXT_SIZE, NonPagedPool, &instanceContext ); if (!NT_SUCCESS( status )) { DbgPrint("Instance Setup Allocate Context Failed %08x\n",status); return status; } instanceContext->Instance = FltObjects->Instance; instanceContext->Volume = FltObjects->Volume; instanceContext->BusType = Descriptor->BusType; status = FltSetInstanceContext( FltObjects->Instance, FLT_SET_CONTEXT_REPLACE_IF_EXISTS , instanceContext, NULL ); if( !NT_SUCCESS( status )) { DbgPrint("Instance Setup Set Context Failed %08x\n",status); if ( instanceContext != NULL ) { FltReleaseContext( instanceContext ); } return status; } if ( instanceContext != NULL ) { FltReleaseContext( instanceContext ); } return STATUS_SUCCESS; 然后是CREATE中进行操作 注意是CREATE后操作 CREATE预操作中 文件尚未建立 对于文件的确认判断是不确认的 FLT_POSTOP_CALLBACK_STATUS PtPostOperationCreate( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags ) { // 确认文件创建成功 if ( !NT_SUCCESS( Data->IoStatus.Status ) || ( STATUS_REPARSE == Data->IoStatus.Status ) ) { return FLT_POSTOP_FINISHED_PROCESSING; } if((create_option != FILE_CREATE) && (create_option != FILE_OVERWRITE_IF) ) //确认为新建文件操作 { return FLT_POSTOP_FINISHED_PROCESSING; } status = FltQueryInformationFile(FltObjects->Instance, FltObjects->FileObject, &FileInformation, sizeof(FileInformation), FileStandardInformation,NULL); if(!NT_SUCCESS(status)) { DbgPrint("Createpost QueryFile Failed %08x\n",status); return FLT_POSTOP_FINISHED_PROCESSING; } //确认不是创建文件夹 是文件夹则直接返回 // 其实这里MINIFILTER已经有API可以调用 不必这么麻烦 if(FileInformation.Directory) { return FLT_POSTOP_FINISHED_PROCESSING; } //尝试获得实例上下文 从而获得BUSTYPE // status = FltGetInstanceContext( FltObjects->Instance, &PInstanceContext ); if(NT_SUCCESS(status)) { BusType = PInstanceContext->BusType; FltReleaseContext( PInstanceContext ); } // 如果BUFTYPE不是等于7 说明不是U盘文件 我们不必管他 // 后面则是确认文件名字等一系列操作了 // 这个很简单 MINIFLTER简化了很多 // FltGetFileNameInformation FltParseFileNameInformation 两个函数搞定 // 如果想转化为类似"d:\ test.file"之类的 使用RtlVolumeDeviceToDosName } 重命名 删除操作在哪里?在SETINFORMATION操作中 FLT_POSTOP_CALLBACK_STATUS PtPostOperationSetInfor( __inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags) 其实删除也是一种重命名路径的操作 被删除文件转移到垃圾箱里的重命名 //我们只关心删除和重命名操作 if((Data->Iopb->Parameters.SetFileInformation.FileInformationClass != FileDispositionInformation) && (Data->Iopb->Parameters.SetFileInformation.FileInformationClass != FileRenameInformation)) { return FLT_POSTOP_FINISHED_PROCESSING; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // //尝试获得实例上下文 从而获得BUSTYPE //确认是否是U盘上的文件操作 status = FltGetInstanceContext( FltObjects->Instance, &PInstanceContext ); if(NT_SUCCESS(status)) { BusType = PInstanceContext->BusType; FltReleaseContext( PInstanceContext ); } 到这里 需要监控的操作 创建 删除 重命名 已经全部监控完成 操作在手 天下我有啊 那么怎么记录他们呢 每次截获都OPENFILE WRITEFIEL么 ? NO 那样很没效率 而且文件过滤驱动中这么做 很容易重入的。(截获写操作的例程中又进行写操作,自己截获自己,为重入。 子子孙孙无穷匮也,也就是传说中的死循环) 不过minifilter使用FLT系列函数已经解决了重入这个问题,但是这里我们还是不使用没效率的截获到就写入的办法。 这里参考了FILEDISK的做法 开启一个线程。 每当创建 重命名或者删除的操作被截获后,我们将要写入的内容丢入一个队列。 而开启的线程就无限循环,将队列的内容依次导出写入指定文件。 // driverentry的驱动入口函数中创建一个队列 和开启一个线程 InitializeListHead(&MyGlovalVar.ProcessListHead); //初始化链表头 KeInitializeSpinLock(&MyGlovalVar.ProcessListLock); //初始化链表锁 KeInitializeEvent( &MyGlovalVar.request_event, SynchronizationEvent, FALSE ); MyGlovalVar.terminate_thread = FALSE; // //创建进程 // status = PsCreateSystemThread( &thread_handle, (ACCESS_MASK) 0L, NULL, NULL, NULL, FilePathThread, &MyGlovalVar); //FilePathThread函数 VOID FilePathThread (IN PVOID Context) { PMYGLOBALVAR GolVar; PLIST_ENTRY request; PMYFILEDATA PMyFileData; ASSERT(Context != NULL); GolVar = (PMYGLOBALVAR)Context; KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); // 不断循环 for( ; ; ) { // 队列里的操作全部被线程完成后 线程在这里等待 //等待EVENT时间触发,当有创建 重命名与删除操作截获入队列后 EVENT时间被触发 KeWaitForSingleObject( &MyGlovalVar.request_event, Executive, KernelMode, FALSE, NULL ); // 一个全局变量 当需要关闭线程的时候 这个变量被置为true if (MyGlovalVar.terminate_thread) { DbgPrint("FilePathThread :Thread Exit\n"); PsTerminateSystemThread(STATUS_SUCCESS); } while (request = ExInterlockedRemoveHeadList( &MyGlovalVar.ProcessListHead, &MyGlovalVar.ProcessListLock)) { PMyFileData = CONTAINING_RECORD(request, MYFILEDATA, myListEntry); DbgPrint("Thread CHAR OPER is %s\n",PMyFileData->Oper); DbgPrint("Thread UNICODE Path is %wZ\n",&PMyFileData->PathName); DbgPrint("Thread WCHAR ParName is %S\n",PMyFileData->ParName); WriteLogFile(PMyFileData); if(PMyFileData->PathName.Buffer != NULL) ExFreePool(PMyFileData->PathName.Buffer); if(PMyFileData != NULL) ExFreePool(PMyFileData); } 然后就是一些细节问题 比如截获到的路径名都是unicode但是写入TXT文件的时候 如果出现中文反而不显示 要转化成ASCII代码才行 这里的处理代码很挫。。。而且我没深入调查 大家有兴趣或者知道答案 请告知一声 谢谢。 另外创建IRP查询BUSTYPE的代码 是从驱动开发网的ZNSOFT的帖子里获取 如果没这个帖子 我区分不出U盘与一般磁盘 在这里表示感谢 |
|