首页
社区
课程
招聘
[讨论]监控U盘文件创建删除命名操作 附代码
发表于: 2011-12-22 13:08 14938

[讨论]监控U盘文件创建删除命名操作 附代码

2011-12-22 13:08
14938
很简单的一个小玩意
就是监控U盘插上后 记录创建 改名字 删除操作。
我搜索了下论坛 和 莫灰灰的《【原创】文件创建监控小工具-MzfFileMonitor(开源) 》
基本一样
不过我这个是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设备后
我们设置一个实例上下文 记录它是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盘与一般磁盘 在这里表示感谢

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (10)
雪    币: 12
活跃值: (773)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错。。楼主你直接把你的代码打个包上传吧
2011-12-22 13:47
0
雪    币: 49
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
连续两篇  似乎都没人感兴趣
这篇本来不写的  只是找个位置做个文章备份
2011-12-22 16:29
0
雪    币: 12
活跃值: (773)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
看雪的众神都冬眠了--
2011-12-22 16:37
0
雪    币: 49
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
没算法 没深入底层 没逆向
大牛还是路过吧
2011-12-22 18:04
0
雪    币: 84
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
catface 没算法 没深入底层 没逆向 大牛还是路过吧
求大大手把手教学,没怎么看懂,自己想弄个日志存入远程数据库的
2017-8-30 09:51
0
雪    币: 84
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
catface 没算法 没深入底层 没逆向 大牛还是路过吧
求个连接数据库版本打包
2017-8-30 09:57
0
雪    币: 199
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
记号
2017-9-6 10:09
0
雪    币: 307
活跃值: (60)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
能发一份代码吗?  icedxu@163.com
2018-5-13 11:10
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
想问一下,上下文在 PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; 中设置,但是我实际测试发现只有驱动启动的时候才会进入这个函数设置 上下文,之后每一次对U盘进行操作都不会再进入这个函数了,这样的话怎么监控到U盘并且获取到BusType呢?
2021-5-25 18:56
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
DriverEntry 想问一下,上下文在 PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; 中设置,但是我实际测试发现只有驱动启动的时候才会进入这个函数设置 上下文,之 ...
弄懂了,这个是用来绑定卷实例的,Minifilter 启动或者新增卷的时候就会绑定一次,可以通过设置上下文来共享 BusType。感谢分享。
2021-7-12 10:32
0
游客
登录 | 注册 方可回帖
返回
//