/*--------------------------------------------------------- 函数名称: FileIsEncrypted 函数描述: 判断文件是否是机密文件 输入参数: pfiInstance 过滤器实例 pfoFileObject 文件对象 pfcdCBD 回调参数 ulFlags 标志位 输出参数: pbIsFileEncrypted 文件是否被加密 返回值: STATUS_SUCCESS 成功(未写加密头) STATUS_FILE_NOT_ENCRYPTED 不是机密文件 STATUS_REPARSE_OBJECT 在这个新建的文件 在本函数 中被写入了加密头 其他: 这个函数设计的不是很好,把写文件头和修改IRP参数 也包含在里面了.暂时不修改了. 如果这个文件已经存在在机密表中,函数将返回 STATUS_IMAGE_ALREADY_LOADED 如果查询名称失败(根本无名称)将返回 STATUS_OBJECT_NAME_NOT_FOUND pbAutoWriteEncryptedHeader传入状态为TRUE时将自动 补齐加密头,同时将修改FLT_PARAMETER部分数据,若输出 TRUE说明已经进行,需要外部调用FltSetCallbackDataDirty 更新维护: 2011.4.9 最初版本 2011.4.13 增加了判断是否已经存在在表中 2011.4.19 Bug:未执行FltCreateFile也会 运行FltClose,已修正. 2011.4.30 修改查询不到名称时的返回值. 2011.5.1 Bug:未赋pbIsFileEncrypted 初值会造成判断错误.已修正. 2011.5.2 将打开文件的操作独立出去. 2011.7.8 Bug:修改了FLT_PARAMETER却没有 通知FLTMGR.通知外部是否需 要Dirty.已修正. 2011.7.20 修改了数据结构增加了定义 2012.1.1 Bug:发现此函数导致一些图标加载 异常,FltReadFile造成,使用 自己重新打开的文件对象即可. 已修正. ---------------------------------------------------------*/ NTSTATUS FileIsEncrypted( __in PFLT_INSTANCE pfiInstance, __in PFILE_OBJECT pfoFileObjectOpened, __in PFLT_CALLBACK_DATA pfcdCBD, __in PVOLUME_CONTEXT pvcVolumeContext, __in PFILE_STREAM_CONTEXT pscFileStreamContext, __in ULONG ulFlags ) { //是否是目录 BOOLEAN bDirectory; //文件长度 LARGE_INTEGER nFileSize; //状态 NTSTATUS status; //完成事件 KEVENT keEventComplete; //偏移量 LARGE_INTEGER nOffset; //加密标识 WCHAR wEncryptedLogo[ENCRYPTION_HEAD_LOGO_SIZE] = ENCRYPTION_HEADER; //读取的数据内容 用于同加密标识比较 多申请了一个存放\0的空间 WCHAR wBufferRead[ENCRYPTION_HEAD_LOGO_SIZE+2] = {0}; //文件句柄 HANDLE hFile = NULL; //对象属性,用于打开文件时使用 OBJECT_ATTRIBUTES oaObjectAttributes; //文件路径 UNICODE_STRING usPath; //文件路径存放数据地址 WCHAR wPath[NORMAL_FILE_PATH_LENGTH]; //文件路径指针 PWSTR pwPath = wPath; //文件路径长度 ULONG ulPathLength; //需要的权限 ULONG ulDesiredAccess; //准备返回的返回值 NTSTATUS statusRet = STATUS_FILE_NOT_ENCRYPTED; //函数返回值 BOOLEAN bReturn; //过滤器IO参数 PFLT_PARAMETERS pfpParameters; //用于存放读取的文件开头部分的内存 PWCHAR pwFileHead; // //保存访问权限的值 参数等 // FltDecodeParameters( pfcdCBD, NULL, NULL, NULL, (LOCK_OPERATION *)&ulDesiredAccess ); pfpParameters = &pfcdCBD -> Iopb -> Parameters; do{ // //查询文件基本信息 // status = FileGetStandardInformation( pfiInstance, pfoFileObjectOpened, NULL, &nFileSize, &bDirectory ); if( !NT_SUCCESS(status) ){ statusRet = status; break; } // //如果是目录,直接返回不需要加密 // if( bDirectory ){ statusRet = STATUS_FILE_NOT_ENCRYPTED; break; } // //如果文件大小为0(新建的文件),并且准备写操作,而且是机密进程,那么加密 // if( ( nFileSize.QuadPart == 0 ) &&//文件大小为0 (ulDesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA) )&&//准备写文件 IsCurrentProcessConfidential()){ // //如果要求自动补齐加密头,那么现在写入 写入前重新设置文件大小 // if(!(ulFlags&FILE_IS_ENCRYPTED_DO_NOT_WRITE_LOGO)){ statusRet = FileWriteEncryptionHeader( pfiInstance, pfoFileObjectOpened, pvcVolumeContext, pscFileStreamContext ); if(NT_SUCCESS(statusRet)){ FileClearCache(pfoFileObjectOpened); // //恢复偏移量到0 // DebugTraceFileAndProcess( DEBUG_TRACE_IMPORTANT_INFO|DEBUG_TRACE_CONFIDENTIAL, "FileIsEncrypted", FILE_OBJECT_NAME_BUFFER(pfoFileObjectOpened), ("Header has been written.Set offset. High:%d,Low:%d,Quard:%d", nOffset.HighPart,nOffset.LowPart,nOffset.QuadPart) ); nOffset.QuadPart = 0; status = FileSetOffset(pfiInstance,pfoFileObjectOpened,&nOffset); if( !NT_SUCCESS(status) ) { statusRet = status; break; } statusRet = STATUS_REPARSE_OBJECT; }else{ DebugTraceFileAndProcess( DEBUG_TRACE_ERROR, "FileIsEncrypted", FILE_OBJECT_NAME_BUFFER(pfoFileObjectOpened), ("Cannot write header.") ); } break; } statusRet = STATUS_SUCCESS; break; } // //如果文件大小小于加密头,那么肯定不是加密文件 // if( nFileSize.QuadPart < CONFIDENTIAL_FILE_HEAD_SIZE ){ statusRet = STATUS_FILE_NOT_ENCRYPTED; break; } // //现在读出前几个字符判断是否是加密文件,字符数 //取决于FileFunction.h中ENCRYPTION_HEAD_LOGO_SIZE // // //开始前先初始化事件对象 // KeInitializeEvent( &keEventComplete, SynchronizationEvent,//同步事件 FALSE//事件初始标志为FALSE ); nOffset.QuadPart = 0; // //获取一个内存块用于存放 // // pwFileHead = (PWCHAR)ExAllocateFromNPagedLookasideList( // pvcVolumeContext->pnliReadEncryptedSignLookasideList); __try{ status = FltReadFile( pfiInstance, pfoFileObjectOpened, &nOffset, ENCRYPTION_HEAD_LOGO_SIZE,//pvcVolumeContext->ulSectorSize,//由于非缓存必须一次性读一个读一个SectorSize,所以这里就读一个ENCRYPTION_HEAD_LOGO_SIZE,//,ulLengthToRead,//读出一个标识长度的数据 wBufferRead,//pwFileHead,//保存在pwFileHead FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,//FLTFL_IO_OPERATION_NON_CACHED|FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, NULL, FileCompleteCallback, (PVOID)&keEventComplete ); KeWaitForSingleObject(&keEventComplete, Executive, KernelMode, TRUE, 0); }__except(EXCEPTION_EXECUTE_HANDLER){ // ExFreeToNPagedLookasideList( // pvcVolumeContext->pnliReadEncryptedSignLookasideList,pwFileHead); return STATUS_UNSUCCESSFUL; } if( !NT_SUCCESS(status) ){ statusRet = status; break; } // //恢复偏移量到0 // status = FileSetOffset(pfiInstance,pfoFileObjectOpened,&nOffset); if( !NT_SUCCESS(status) ) { statusRet = status; break; } // //比较标志是否相等 // //DebugPrintFileObject("Read file check",pfoFileObjectOpened,FALSE); //KdPrint(("\t\tRead file %ws\n",wBufferRead)); if( RtlCompareMemory( wBufferRead, wEncryptedLogo, ENCRYPTION_HEAD_LOGO_SIZE) == ENCRYPTION_HEAD_LOGO_SIZE){ statusRet = STATUS_SUCCESS; DebugTraceFileAndProcess( DEBUG_TRACE_IMPORTANT_INFO|DEBUG_TRACE_CONFIDENTIAL, "FileIsEncrypted", FILE_OBJECT_NAME_BUFFER(pfoFileObjectOpened), ("Confidential file detected.") ); // ExFreeToNPagedLookasideList( // pvcVolumeContext->pnliReadEncryptedSignLookasideList,pwFileHead); break; } // ExFreeToNPagedLookasideList( // pvcVolumeContext->pnliReadEncryptedSignLookasideList,pwFileHead); }while(0); // //善后工作 // /* // //要注意: //1.文件的CREATE改为OPEN. //2.文件的OVERWRITE去掉.不管是不是要加密的文件, //都必须这样做.否则的话,本来是试图生成文件的, //结果发现文件已经存在了.本来试图覆盖文件的,再 //覆盖一次会去掉加密头. // // //如果连名字都没有 什么都没做 就不用修改了 // if( !(ulFlags&FILE_IS_ENCRYPTED_DO_NOT_CHANGE_OPEN_WAY) ) { ULONG ulDisp = FILE_OPEN; pfpParameters->Create.Options &= 0x00ffffff; pfpParameters->Create.Options |= (ulDisp << 24); }*/ return statusRet; }
EXTERN_C static NTSTATUS ScvnpReadFile(_In_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Out_ void *Buffer, _In_ ULONG BufferSize) { PAGED_CODE(); // Use an existing file object when it is NOT IRP_MJ_CLEANUP. if (Data->Iopb->MajorFunction != IRP_MJ_CLEANUP) { LARGE_INTEGER byteOffset = {}; auto status = FltReadFile(FltObjects->Instance, FltObjects->FileObject, &byteOffset, BufferSize, Buffer, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, nullptr, nullptr, nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltReadFile failed (%08x)", status); return status; } return status; } PFILE_OBJECT fileObject = nullptr; // Make a new file object since the file is already out of the current IO // path. PFLT_FILE_NAME_INFORMATION fileNameInformation = nullptr; auto status = FltGetFileNameInformationUnsafe( FltObjects->FileObject, FltObjects->Instance, FLT_FILE_NAME_NORMALIZED, &fileNameInformation); if (!NT_SUCCESS(status)) { return status; } OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( &fileNameInformation->Name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); HANDLE fileHandle = nullptr; IO_STATUS_BLOCK ioStatus = {}; status = FltCreateFile( FltObjects->Filter, FltObjects->Instance, &fileHandle, GENERIC_READ, &objAttr, &ioStatus, nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, nullptr, 0, 0); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltCreateFile failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } status = ObReferenceObjectByHandle(fileHandle, 0, nullptr, KernelMode, reinterpret_cast<void **>(&fileObject), nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("ObReferenceObjectByHandle failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } status = FltReadFile(FltObjects->Instance, fileObject, nullptr, BufferSize, Buffer, 0, nullptr, nullptr, nullptr); if (!NT_SUCCESS(status)) { LOG_ERROR_SAFE("FltReadFile failed (%08x) for %wZ", status, &fileNameInformation->Name); goto End; } End: if (fileObject) { ObDereferenceObject(fileObject); } if (fileHandle) { FltClose(fileHandle); } if (fileNameInformation) { FltReleaseFileNameInformation(fileNameInformation); } return status; }
NTSTATUS CopyFile( PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PUNICODE_STRING pCompleteFileName ) { NTSTATUS status; UNICODE_STRING tempDeletedFilePath; OBJECT_ATTRIBUTES tempDeletedObject; IO_STATUS_BLOCK ioStatusTempDeleted; LARGE_INTEGER allocate; FILE_STANDARD_INFORMATION fileStandardInformation; HANDLE tempDeletedHandle; ULONG returnedLength; allocate.QuadPart = 0x10000; InitializeObjectAttributes( &tempDeletedObject, pCompleteFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = FltQueryInformationFile( FltObjects->Instance, Data->Iopb->TargetFileObject, &fileStandardInformation, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation, &returnedLength ); if(NT_SUCCESS(status)) { allocate.QuadPart = fileStandardInformation.AllocationSize.QuadPart; } else { DbgPrint("CaptureFileMonitor: ERROR - Could not get files allocation size\n"); return status; } status = FltCreateFile( FltObjects->Filter, NULL, &tempDeletedHandle, GENERIC_WRITE, &tempDeletedObject, &ioStatusTempDeleted, &allocate, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_NON_DIRECTORY_FILE, NULL, 0, 0 ); if(NT_SUCCESS(status)) { PVOID handleFileObject; PVOID pFileBuffer; LARGE_INTEGER offset; ULONG bytesRead = 0; ULONG bytesWritten = 0; offset.QuadPart = 0; status = ObReferenceObjectByHandle( tempDeletedHandle, 0, NULL, KernelMode, &handleFileObject, NULL); if(!NT_SUCCESS(status)) { DbgPrint("CaptureFileMonitor: ERROR - ObReferenceObjectByHandle - FAILED - %08x\n", status); return status; } pFileBuffer = ExAllocatePoolWithTag(NonPagedPool, 65536, FILE_POOL_TAG); if(pFileBuffer != NULL) { ObReferenceObject(Data->Iopb->TargetFileObject); do { IO_STATUS_BLOCK IoStatusBlock; bytesWritten = 0; status = FltReadFile( FltObjects->Instance, Data->Iopb->TargetFileObject, &offset, 65536, pFileBuffer, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, &bytesRead , NULL, NULL ); if(NT_SUCCESS(status) && bytesRead > 0) { /* You can't use FltWriteFile here */ /* Instance may not be the same instance we want to write to eg a flash drive writing a file to a ntfs partition */ status = ZwWriteFile( tempDeletedHandle, NULL, NULL, NULL, &IoStatusBlock, pFileBuffer, bytesRead, &offset, NULL ); if(NT_SUCCESS(status)) { //DbgPrint("WriteFile: FltReadFile - %08x\n", status); } /* status = FltWriteFile( FltObjects->Instance, handleFileObject, &offset, bytesRead, pFileBuffer, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, &bytesWritten, NULL, NULL ); */ } else { //DbgPrint("CopyFile: FltReadFile - %08x\n", status); break; } offset.QuadPart += bytesRead; } while(bytesRead == 65536); ObDereferenceObject(Data->Iopb->TargetFileObject); ExFreePoolWithTag(pFileBuffer, FILE_POOL_TAG); } ObDereferenceObject(handleFileObject); FltClose(tempDeletedHandle); } else { if(status != STATUS_OBJECT_NAME_COLLISION) { DbgPrint("CaptureFileMonitor: ERROR - FltCreateFile FAILED - %08x\n",status); return status; } } return STATUS_SUCCESS; }
/*--------------------------------------------------------- 函数名称: FileReadEncryptionHeaderAndDeconstruct 函数描述: 非重入读取整个加密头 同时解包 输入参数: pfiInstance 过滤器实例 pfoFileObject 文件对象 pvcVolumeContext 卷上下文 pscFileStreamContext文件流上下文 输出参数: 返回值: STATUS_SUCCESS 成功 否则返回相应状态 其他: 更新维护: 2011.4.9 修改为使用FltXXX版本 ---------------------------------------------------------*/ NTSTATUS FileReadEncryptionHeaderAndDeconstruct( __in PFLT_INSTANCE pfiInstance, __in PFILE_OBJECT pfoFileObject, __in PVOLUME_CONTEXT pvcVolumeContext, __in PFILE_STREAM_CONTEXT pscFileStreamContext ) { //完成事件 KEVENT keEventComplete; //文件大小 LARGE_INTEGER nFileSize; //偏移量 LARGE_INTEGER nOffset; //长度 设置为标准加密头长度 ULONG ulLength = CONFIDENTIAL_FILE_HEAD_SIZE; //各函数返回状态 NTSTATUS status; //本函数要返回的状态 NTSTATUS statusRet; //加密头地址 PVOID pHeader; // //开始前先初始化事件对象 // KeInitializeEvent( &keEventComplete, SynchronizationEvent,//同步事件 FALSE//事件初始标志为FALSE ); // //读取加密标识头 // nOffset.QuadPart = 0; // ulLength = ROUND_TO_SIZE(ulLength,pvcVolumeContext->ulSectorSize); pHeader = ExAllocateFromNPagedLookasideList(&nliNewFileHeaderLookasideList); statusRet = FltReadFile( pfiInstance,//起始实例,用于防止重入 pfoFileObject,//文件对象 &nOffset,//偏移量 从头写起 CONFIDENTIAL_FILE_HEAD_SIZE,//ulLength,//一个头的大小 pHeader, FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET|FLTFL_IO_OPERATION_NON_CACHED,//非缓存写入 NULL,//不需要返回读入的字节数 FileCompleteCallback,//回调,确认执行完毕 &keEventComplete//回调上下文,传递完成事件 ); // //等待完成 // KeWaitForSingleObject(&keEventComplete, Executive, KernelMode, TRUE, 0); // //解包 // FctDeconstructFileHead(pscFileStreamContext,pHeader); ExFreeToNPagedLookasideList(&nliNewFileHeaderLookasideList,pHeader); // //恢复偏移量到0 // status = FileSetOffset(pfiInstance,pfoFileObject,&nOffset); if( !NT_SUCCESS(status) ){ statusRet = status; } return statusRet; }