//This module hooks: // IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_QUERY_INFORMATION, // IRP_MJ_SET_INFORMATION, IRP_MJ_DIRECTORY_CONTROL, // FASTIO_QUERY_STANDARD_INFO FASTIO_QUERY_BASIC_INFO FASTIO_READ(WRITE) //to hide first N bytes of given file extern "C" { #include <ntddk.h> } #pragma hdrstop("InterceptIO.pch") / // Undocumented structures missing in ntddk.h typedef struct _FILE_INTERNAL_INFORMATION { // Information Class 6 LARGE_INTEGER FileId; } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; typedef struct _FILE_EA_INFORMATION { // Information Class 7 ULONG EaInformationLength; } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; typedef struct _FILE_ACCESS_INFORMATION { // Information Class 8 ACCESS_MASK GrantedAccess; } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; typedef struct _FILE_MODE_INFORMATION { // Information Class 16 ULONG Mode; } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; typedef struct _FILE_ALLOCATION_INFORMATION { // Information Class 19 LARGE_INTEGER AllocationSize; } FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; WCHAR FileName[1]; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; typedef struct _FILE_ALL_INFORMATION { // Information Class 18 FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation; FILE_INTERNAL_INFORMATION InternalInformation; FILE_EA_INFORMATION EaInformation; FILE_ACCESS_INFORMATION AccessInformation; FILE_POSITION_INFORMATION PositionInformation; FILE_MODE_INFORMATION ModeInformation; FILE_ALIGNMENT_INFORMATION AlignmentInformation; FILE_NAME_INFORMATION NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; typedef struct tag_QUERY_DIRECTORY { ULONG Length; PUNICODE_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass; ULONG FileIndex; } QUERY_DIRECTORY, *PQUERY_DIRECTORY; #pragma pack(push, 4) typedef struct tag_FQD_SmallCommonBlock { ULONG NextEntryOffset; ULONG FileIndex; } FQD_SmallCommonBlock, *PFQD_SmallCommonBlock; typedef struct tag_FQD_FILE_ATTR { TIME CreationTime; TIME LastAccessTime; TIME LastWriteTime; TIME ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; } FQD_FILE_ATTR, *PFQD_FILE_ATTR; typedef struct tag_FQD_CommonBlock { FQD_SmallCommonBlock SmallCommonBlock; FQD_FILE_ATTR FileAttr; ULONG FileNameLength; } FQD_CommonBlock, *PFQD_CommonBlock; typedef struct _KFILE_DIRECTORY_INFORMATION { FQD_CommonBlock CommonBlock; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_DIRECTORY_INFORMATION, *PKFILE_DIRECTORY_INFORMATION; typedef struct _KFILE_FULL_DIR_INFORMATION { FQD_CommonBlock CommonBlock; ULONG EaSize; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_FULL_DIR_INFORMATION, *PKFILE_FULL_DIR_INFORMATION; typedef struct _KFILE_BOTH_DIR_INFORMATION { FQD_CommonBlock CommonBlock; ULONG EaSize; USHORT ShortFileNameLength; WCHAR ShortFileName[12]; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_BOTH_DIR_INFORMATION, *PKFILE_BOTH_DIR_INFORMATION; #pragma pack(pop) / // Global variables PDRIVER_OBJECT pDriverObject; PDRIVER_DISPATCH OldReadDisp, OldWriteDisp, OldQueryDisp, OldSetInfoDisp, OldDirCtlDisp; PFAST_IO_READ OldFastIoReadDisp; PFAST_IO_WRITE OldFastIoWriteDisp; PFAST_IO_QUERY_STANDARD_INFO OldFastIoQueryStandartInfoDisp; //Size of our file's Invisible Part (in bytes) ULONG InvisiblePartSize = 10; //File, part of which we want to hide wchar_t OurFileName[] = L"testing.fil"; //Size of OurFileName in bytes, excluding null terminator ULONG OurFileNameLen = sizeof(OurFileName) - sizeof(wchar_t); / // Functions //Function returns true if FN matches OurFileName bool ThisIsOurFile(PUNICODE_STRING FN) { return ((FN->Buffer) && (FN->Length >= OurFileNameLen) && _wcsnicmp((wchar_t*)((char*)FN->Buffer + FN->Length - OurFileNameLen), OurFileName, OurFileNameLen/2)==0); } //Structure used to track IRPs which completion must be handled struct s_ComplRtnTrack { PIO_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; //When CompletionRoutine is called, flags corresponds to InvokeOn* UCHAR Control; PIO_STACK_LOCATION CISL; FILE_INFORMATION_CLASS FileInformationClass; PVOID Buffer; }; //Function set new CompletionRoutine, InvokeOnSuccess flag, //and copies original fields to Context void HookIrpCompletion(PIO_STACK_LOCATION CISL, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID Buffer, FILE_INFORMATION_CLASS FileInformationClass) { s_ComplRtnTrack* NewContext = (s_ComplRtnTrack*)ExAllocatePool(NonPagedPool, sizeof(s_ComplRtnTrack)); NewContext->CompletionRoutine = CISL->CompletionRoutine; NewContext->Context = CISL->Context; NewContext->Control = CISL->Control; NewContext->CISL = CISL; //Since CISL.Parameters unavailabile in IrpCompletion handler, //let's save all necessary data in Context structure NewContext->FileInformationClass = FileInformationClass; NewContext->Buffer = Buffer; CISL->CompletionRoutine = CompletionRoutine; CISL->Context = NewContext; CISL->Control |= SL_INVOKE_ON_SUCCESS; } //Function handles IRP completion NTSTATUS NewComplRtn ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, s_ComplRtnTrack* CXT) { //Handle different types of IRP switch (CXT->CISL->MajorFunction) { case IRP_MJ_QUERY_INFORMATION: _asm int 3; //ThisIsOurFile is already tested switch (CXT->FileInformationClass) { //In all cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first InvisiblePartSize bytes case FilePositionInformation: ((PFILE_POSITION_INFORMATION)CXT->Buffer)->CurrentByteOffset.QuadPart -= InvisiblePartSize; break; case FileEndOfFileInformation: ((PFILE_END_OF_FILE_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize; break; case FileStandardInformation: ((PFILE_STANDARD_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize; break; case FileAllocationInformation: ((PFILE_ALLOCATION_INFORMATION)CXT->Buffer)->AllocationSize.QuadPart -= InvisiblePartSize; break; case FileAllInformation: ((PFILE_ALL_INFORMATION)CXT->Buffer)->PositionInformation.CurrentByteOffset.QuadPart -= InvisiblePartSize; ((PFILE_ALL_INFORMATION)CXT->Buffer)->StandardInformation.EndOfFile.QuadPart -= InvisiblePartSize; break; } case IRP_MJ_DIRECTORY_CONTROL: //Get a pointer to first directory entries PFQD_SmallCommonBlock pQueryDirWin32 = (PFQD_SmallCommonBlock)CXT->Buffer; //Cycle through directory entries while (1) { PWCHAR pFileName = 0; ULONG dwFileNameLength = 0; switch (CXT->FileInformationClass) { //In all cases get pointer to FileName and FileNameLength case FileDirectoryInformation: dwFileNameLength = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->FileName; break; case FileFullDirectoryInformation: dwFileNameLength = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->FileName; break; case FileBothDirectoryInformation: dwFileNameLength = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->FileName; break; } //_asm int 3; //Is this file that we want? if ((dwFileNameLength == OurFileNameLen) && _wcsnicmp(pFileName, OurFileName, OurFileNameLen/2)==0) { //_asm int 3; //Hide first InvisiblePartSize bytes ((PFQD_CommonBlock)pQueryDirWin32)->FileAttr.EndOfFile.QuadPart -= InvisiblePartSize; break; } //Quit if no more directory entries if (!pQueryDirWin32->NextEntryOffset) break; //Continue with next directory entry pQueryDirWin32 = (PFQD_SmallCommonBlock)((CHAR*)pQueryDirWin32 + pQueryDirWin32->NextEntryOffset); } } //If appropriate Control flag was set,... if ( ((CXT->Control == SL_INVOKE_ON_SUCCESS)&&(NT_SUCCESS(Irp->IoStatus.Status))) || ((CXT->Control == SL_INVOKE_ON_ERROR)&&(NT_ERROR(Irp->IoStatus.Status))) || ((CXT->Control == SL_INVOKE_ON_CANCEL)&&(Irp->IoStatus.Status == STATUS_CANCELLED)) ) //...call original CompletionRoutine return CXT->CompletionRoutine( DeviceObject, Irp, CXT->Context); else return STATUS_SUCCESS; } //Filename IRP handler deal with #define FName &(CISL->FileObject->FileName) //Function handles IRP_MJ_READ and IRP_MJ_WRITE NTSTATUS NewReadWriteDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if (CISL->FileObject && //Don't mess with swaping !(Irp->Flags & IRP_PAGING_IO) && !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)) { if (ThisIsOurFile(FName)) { //_asm int 3; CISL->Parameters.Write.ByteOffset.QuadPart += InvisiblePartSize; //Write and Read has the same structure, thus handled together } } //Call corresponding original handler switch (CISL->MajorFunction) { case IRP_MJ_READ: return OldReadDisp(DeviceObject, Irp); case IRP_MJ_WRITE: return OldWriteDisp(DeviceObject, Irp); } } //Function handles IRP_MJ_QUERY_INFORMATION NTSTATUS NewQueryDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if ((CISL->MajorFunction == IRP_MJ_QUERY_INFORMATION) && ThisIsOurFile(FName)) { //_asm int 3; switch (CISL->Parameters.QueryFile.FileInformationClass) { //Information types that contains file size or current offset case FilePositionInformation: case FileEndOfFileInformation: case FileStandardInformation: case FileAllocationInformation: case FileAllInformation: //_asm int 3; HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, Irp->AssociatedIrp.SystemBuffer, CISL->Parameters.QueryFile.FileInformationClass); } } //Call original handler return OldQueryDisp(DeviceObject, Irp); } //Function handles IRP_MJ_SET_INFORMATION NTSTATUS NewSetInfoDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if (CISL->FileObject && ThisIsOurFile(FName)) { //_asm int 3; switch (CISL->Parameters.QueryFile.FileInformationClass) { //Information types that contains file size or current offset. //In all cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first InvisiblePartSize bytes case FilePositionInformation: ((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->CurrentByteOffset.QuadPart += InvisiblePartSize; break; case FileEndOfFileInformation: ((PFILE_END_OF_FILE_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize; break; case FileStandardInformation: ((PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize; break; case FileAllocationInformation: //_asm int 3; ((PFILE_ALLOCATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->AllocationSize.QuadPart += InvisiblePartSize; break; case FileAllInformation: ((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->PositionInformation.CurrentByteOffset.QuadPart += InvisiblePartSize; ((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->StandardInformation.EndOfFile.QuadPart += InvisiblePartSize; break; } } //Call original handler return OldSetInfoDisp(DeviceObject, Irp); } //Function handles IRP_MJ_DIRECTORY_CONTROL NTSTATUS NewDirCtlDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { void *pBuffer; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); //_asm int 3; if ((CISL->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && (CISL->MinorFunction == IRP_MN_QUERY_DIRECTORY)) { //Handle both ways of passing user supplied buffer if (Irp->MdlAddress) pBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress); else pBuffer = Irp->UserBuffer; HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, pBuffer, ((PQUERY_DIRECTORY)(&CISL->Parameters))->FileInformationClass); } //Call original handler return OldDirCtlDisp(DeviceObject, Irp); } #undef FName //Function handles FastIoRead BOOLEAN NewFastIoRead( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) { LARGE_INTEGER NewFileOffset; //_asm int 3; if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify FileOffset to hide first InvisiblePartSize bytes NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize; return OldFastIoReadDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Call original handler return OldFastIoReadDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Function handles FastIoWrite BOOLEAN NewFastIoWrite( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) { LARGE_INTEGER NewFileOffset; //_asm int 3; if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify FileOffset to hide first InvisiblePartSize bytes NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize; return OldFastIoWriteDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } return OldFastIoWriteDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Function handles FastIoQueryStandartInfo BOOLEAN NewFastIoQueryStandartInfo( IN struct _FILE_OBJECT *FileObject, IN BOOLEAN Wait, OUT PFILE_STANDARD_INFORMATION Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN struct _DEVICE_OBJECT *DeviceObject ) { //Call original handler BOOLEAN status = OldFastIoQueryStandartInfoDisp(FileObject, Wait, Buffer, IoStatus, DeviceObject); if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify EndOfFile to hide first InvisiblePartSize bytes Buffer->EndOfFile.QuadPart -= InvisiblePartSize; } return status; } extern "C" NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName( IN PUNICODE_STRING ObjectPath, IN ULONG Attributes, IN PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK DesiredAccess OPTIONAL, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext OPTIONAL, OUT PVOID *ObjectPtr ); extern "C" PVOID IoDriverObjectType; //Function hooks given dispatch function (MajorFunction) VOID InterceptFunction(UCHAR MajorFunction, PDRIVER_OBJECT pDriverObject, OPTIONAL PDRIVER_DISPATCH *OldFunctionPtr, OPTIONAL PDRIVER_DISPATCH NewFunctionPtr) { PDRIVER_DISPATCH *TargetFn; TargetFn = &(pDriverObject->MajorFunction[MajorFunction]); //hook only if handler exists if (*TargetFn) { if (OldFunctionPtr) *OldFunctionPtr = *TargetFn; if (NewFunctionPtr) *TargetFn = NewFunctionPtr; } } //Function hooks given driver's dispatch functions NTSTATUS Intercept(PWSTR pwszDeviceName) { UNICODE_STRING DeviceName; NTSTATUS status; KIRQL OldIrql; _asm int 3; pDriverObject = NULL; RtlInitUnicodeString(&DeviceName, pwszDeviceName); status = ObReferenceObjectByName(&DeviceName, OBJ_CASE_INSENSITIVE, NULL, 0, (POBJECT_TYPE)IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDriverObject); if (pDriverObject) { //Raise IRQL to avoid context switch //when some pointer is semi-modified KeRaiseIrql(HIGH_LEVEL, &OldIrql); //hook dispatch functions InterceptFunction(IRP_MJ_READ, pDriverObject, &OldReadDisp, NewReadWriteDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject, &OldWriteDisp, NewReadWriteDisp); InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, &OldQueryDisp, NewQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, &OldSetInfoDisp, NewSetInfoDisp); InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, &OldDirCtlDisp, NewDirCtlDisp); //hook FastIo dispatch functions if FastIo table exists if (pDriverObject->FastIoDispatch) { //It would be better to copy FastIo table to avoid //messing with kernel memory protection, but it works as it is OldFastIoReadDisp = pDriverObject->FastIoDispatch->FastIoRead; pDriverObject->FastIoDispatch->FastIoRead = NewFastIoRead; OldFastIoWriteDisp = pDriverObject->FastIoDispatch->FastIoWrite; pDriverObject->FastIoDispatch->FastIoWrite = NewFastIoWrite; OldFastIoQueryStandartInfoDisp = pDriverObject->FastIoDispatch->FastIoQueryStandardInfo; pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = NewFastIoQueryStandartInfo; } KeLowerIrql(OldIrql); } return status; } //Function cancels hooking VOID UnIntercept() { KIRQL OldIrql; if (pDriverObject) { KeRaiseIrql(HIGH_LEVEL, &OldIrql); InterceptFunction(IRP_MJ_READ, pDriverObject, NULL, OldReadDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject, NULL, OldWriteDisp); InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, NULL, OldQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, NULL, OldSetInfoDisp); InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, NULL, OldDirCtlDisp); if (pDriverObject->FastIoDispatch) { pDriverObject->FastIoDispatch->FastIoRead = OldFastIoReadDisp; pDriverObject->FastIoDispatch->FastIoWrite = OldFastIoWriteDisp; pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = OldFastIoQueryStandartInfoDisp; } KeLowerIrql(OldIrql); ObDereferenceObject(pDriverObject); } }文件加密标识 -隐藏文件头的黑客代码 //This module hooks: // IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_QUERY_INFORMATION, // IRP_MJ_SET_INFORMATION, IRP_MJ_DIRECTORY_CONTROL, // FASTIO_QUERY_STANDARD_INFO FASTIO_QUERY_BASIC_INFO FASTIO_READ(WRITE) //to hide first N bytes of given file extern "C" { #include <ntddk.h> } #pragma hdrstop("InterceptIO.pch") / // Undocumented structures missing in ntddk.h typedef struct _FILE_INTERNAL_INFORMATION { // Information Class 6 LARGE_INTEGER FileId; } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; typedef struct _FILE_EA_INFORMATION { // Information Class 7 ULONG EaInformationLength; } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; typedef struct _FILE_ACCESS_INFORMATION { // Information Class 8 ACCESS_MASK GrantedAccess; } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; typedef struct _FILE_MODE_INFORMATION { // Information Class 16 ULONG Mode; } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; typedef struct _FILE_ALLOCATION_INFORMATION { // Information Class 19 LARGE_INTEGER AllocationSize; } FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; WCHAR FileName[1]; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; typedef struct _FILE_ALL_INFORMATION { // Information Class 18 FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation; FILE_INTERNAL_INFORMATION InternalInformation; FILE_EA_INFORMATION EaInformation; FILE_ACCESS_INFORMATION AccessInformation; FILE_POSITION_INFORMATION PositionInformation; FILE_MODE_INFORMATION ModeInformation; FILE_ALIGNMENT_INFORMATION AlignmentInformation; FILE_NAME_INFORMATION NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; typedef struct tag_QUERY_DIRECTORY { ULONG Length; PUNICODE_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass; ULONG FileIndex; } QUERY_DIRECTORY, *PQUERY_DIRECTORY; #pragma pack(push, 4) typedef struct tag_FQD_SmallCommonBlock { ULONG NextEntryOffset; ULONG FileIndex; } FQD_SmallCommonBlock, *PFQD_SmallCommonBlock; typedef struct tag_FQD_FILE_ATTR { TIME CreationTime; TIME LastAccessTime; TIME LastWriteTime; TIME ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; } FQD_FILE_ATTR, *PFQD_FILE_ATTR; typedef struct tag_FQD_CommonBlock { FQD_SmallCommonBlock SmallCommonBlock; FQD_FILE_ATTR FileAttr; ULONG FileNameLength; } FQD_CommonBlock, *PFQD_CommonBlock; typedef struct _KFILE_DIRECTORY_INFORMATION { FQD_CommonBlock CommonBlock; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_DIRECTORY_INFORMATION, *PKFILE_DIRECTORY_INFORMATION; typedef struct _KFILE_FULL_DIR_INFORMATION { FQD_CommonBlock CommonBlock; ULONG EaSize; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_FULL_DIR_INFORMATION, *PKFILE_FULL_DIR_INFORMATION; typedef struct _KFILE_BOTH_DIR_INFORMATION { FQD_CommonBlock CommonBlock; ULONG EaSize; USHORT ShortFileNameLength; WCHAR ShortFileName[12]; WCHAR FileName[ANYSIZE_ARRAY]; } KFILE_BOTH_DIR_INFORMATION, *PKFILE_BOTH_DIR_INFORMATION; #pragma pack(pop) / // Global variables PDRIVER_OBJECT pDriverObject; PDRIVER_DISPATCH OldReadDisp, OldWriteDisp, OldQueryDisp, OldSetInfoDisp, OldDirCtlDisp; PFAST_IO_READ OldFastIoReadDisp; PFAST_IO_WRITE OldFastIoWriteDisp; PFAST_IO_QUERY_STANDARD_INFO OldFastIoQueryStandartInfoDisp; //Size of our file's Invisible Part (in bytes) ULONG InvisiblePartSize = 10; //File, part of which we want to hide wchar_t OurFileName[] = L"testing.fil"; //Size of OurFileName in bytes, excluding null terminator ULONG OurFileNameLen = sizeof(OurFileName) - sizeof(wchar_t); / // Functions //Function returns true if FN matches OurFileName bool ThisIsOurFile(PUNICODE_STRING FN) { return ((FN->Buffer) && (FN->Length >= OurFileNameLen) && _wcsnicmp((wchar_t*)((char*)FN->Buffer + FN->Length - OurFileNameLen), OurFileName, OurFileNameLen/2)==0); } //Structure used to track IRPs which completion must be handled struct s_ComplRtnTrack { PIO_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; //When CompletionRoutine is called, flags corresponds to InvokeOn* UCHAR Control; PIO_STACK_LOCATION CISL; FILE_INFORMATION_CLASS FileInformationClass; PVOID Buffer; }; //Function set new CompletionRoutine, InvokeOnSuccess flag, //and copies original fields to Context void HookIrpCompletion(PIO_STACK_LOCATION CISL, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID Buffer, FILE_INFORMATION_CLASS FileInformationClass) { s_ComplRtnTrack* NewContext = (s_ComplRtnTrack*)ExAllocatePool(NonPagedPool, sizeof(s_ComplRtnTrack)); NewContext->CompletionRoutine = CISL->CompletionRoutine; NewContext->Context = CISL->Context; NewContext->Control = CISL->Control; NewContext->CISL = CISL; //Since CISL.Parameters unavailabile in IrpCompletion handler, //let's save all necessary data in Context structure NewContext->FileInformationClass = FileInformationClass; NewContext->Buffer = Buffer; CISL->CompletionRoutine = CompletionRoutine; CISL->Context = NewContext; CISL->Control |= SL_INVOKE_ON_SUCCESS; } //Function handles IRP completion NTSTATUS NewComplRtn ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, s_ComplRtnTrack* CXT) { //Handle different types of IRP switch (CXT->CISL->MajorFunction) { case IRP_MJ_QUERY_INFORMATION: _asm int 3; //ThisIsOurFile is already tested switch (CXT->FileInformationClass) { //In all cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first InvisiblePartSize bytes case FilePositionInformation: ((PFILE_POSITION_INFORMATION)CXT->Buffer)->CurrentByteOffset.QuadPart -= InvisiblePartSize; break; case FileEndOfFileInformation: ((PFILE_END_OF_FILE_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize; break; case FileStandardInformation: ((PFILE_STANDARD_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize; break; case FileAllocationInformation: ((PFILE_ALLOCATION_INFORMATION)CXT->Buffer)->AllocationSize.QuadPart -= InvisiblePartSize; break; case FileAllInformation: ((PFILE_ALL_INFORMATION)CXT->Buffer)->PositionInformation.CurrentByteOffset.QuadPart -= InvisiblePartSize; ((PFILE_ALL_INFORMATION)CXT->Buffer)->StandardInformation.EndOfFile.QuadPart -= InvisiblePartSize; break; } case IRP_MJ_DIRECTORY_CONTROL: //Get a pointer to first directory entries PFQD_SmallCommonBlock pQueryDirWin32 = (PFQD_SmallCommonBlock)CXT->Buffer; //Cycle through directory entries while (1) { PWCHAR pFileName = 0; ULONG dwFileNameLength = 0; switch (CXT->FileInformationClass) { //In all cases get pointer to FileName and FileNameLength case FileDirectoryInformation: dwFileNameLength = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->FileName; break; case FileFullDirectoryInformation: dwFileNameLength = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->FileName; break; case FileBothDirectoryInformation: dwFileNameLength = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength; pFileName = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->FileName; break; } //_asm int 3; //Is this file that we want? if ((dwFileNameLength == OurFileNameLen) && _wcsnicmp(pFileName, OurFileName, OurFileNameLen/2)==0) { //_asm int 3; //Hide first InvisiblePartSize bytes ((PFQD_CommonBlock)pQueryDirWin32)->FileAttr.EndOfFile.QuadPart -= InvisiblePartSize; break; } //Quit if no more directory entries if (!pQueryDirWin32->NextEntryOffset) break; //Continue with next directory entry pQueryDirWin32 = (PFQD_SmallCommonBlock)((CHAR*)pQueryDirWin32 + pQueryDirWin32->NextEntryOffset); } } //If appropriate Control flag was set,... if ( ((CXT->Control == SL_INVOKE_ON_SUCCESS)&&(NT_SUCCESS(Irp->IoStatus.Status))) || ((CXT->Control == SL_INVOKE_ON_ERROR)&&(NT_ERROR(Irp->IoStatus.Status))) || ((CXT->Control == SL_INVOKE_ON_CANCEL)&&(Irp->IoStatus.Status == STATUS_CANCELLED)) ) //...call original CompletionRoutine return CXT->CompletionRoutine( DeviceObject, Irp, CXT->Context); else return STATUS_SUCCESS; } //Filename IRP handler deal with #define FName &(CISL->FileObject->FileName) //Function handles IRP_MJ_READ and IRP_MJ_WRITE NTSTATUS NewReadWriteDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if (CISL->FileObject && //Don't mess with swaping !(Irp->Flags & IRP_PAGING_IO) && !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)) { if (ThisIsOurFile(FName)) { //_asm int 3; CISL->Parameters.Write.ByteOffset.QuadPart += InvisiblePartSize; //Write and Read has the same structure, thus handled together } } //Call corresponding original handler switch (CISL->MajorFunction) { case IRP_MJ_READ: return OldReadDisp(DeviceObject, Irp); case IRP_MJ_WRITE: return OldWriteDisp(DeviceObject, Irp); } } //Function handles IRP_MJ_QUERY_INFORMATION NTSTATUS NewQueryDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if ((CISL->MajorFunction == IRP_MJ_QUERY_INFORMATION) && ThisIsOurFile(FName)) { //_asm int 3; switch (CISL->Parameters.QueryFile.FileInformationClass) { //Information types that contains file size or current offset case FilePositionInformation: case FileEndOfFileInformation: case FileStandardInformation: case FileAllocationInformation: case FileAllInformation: //_asm int 3; HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, Irp->AssociatedIrp.SystemBuffer, CISL->Parameters.QueryFile.FileInformationClass); } } //Call original handler return OldQueryDisp(DeviceObject, Irp); } //Function handles IRP_MJ_SET_INFORMATION NTSTATUS NewSetInfoDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if (CISL->FileObject && ThisIsOurFile(FName)) { //_asm int 3; switch (CISL->Parameters.QueryFile.FileInformationClass) { //Information types that contains file size or current offset. //In all cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first InvisiblePartSize bytes case FilePositionInformation: ((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->CurrentByteOffset.QuadPart += InvisiblePartSize; break; case FileEndOfFileInformation: ((PFILE_END_OF_FILE_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize; break; case FileStandardInformation: ((PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize; break; case FileAllocationInformation: //_asm int 3; ((PFILE_ALLOCATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->AllocationSize.QuadPart += InvisiblePartSize; break; case FileAllInformation: ((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->PositionInformation.CurrentByteOffset.QuadPart += InvisiblePartSize; ((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->StandardInformation.EndOfFile.QuadPart += InvisiblePartSize; break; } } //Call original handler return OldSetInfoDisp(DeviceObject, Irp); } //Function handles IRP_MJ_DIRECTORY_CONTROL NTSTATUS NewDirCtlDisp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { void *pBuffer; PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); //_asm int 3; if ((CISL->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && (CISL->MinorFunction == IRP_MN_QUERY_DIRECTORY)) { //Handle both ways of passing user supplied buffer if (Irp->MdlAddress) pBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress); else pBuffer = Irp->UserBuffer; HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, pBuffer, ((PQUERY_DIRECTORY)(&CISL->Parameters))->FileInformationClass); } //Call original handler return OldDirCtlDisp(DeviceObject, Irp); } #undef FName //Function handles FastIoRead BOOLEAN NewFastIoRead( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) { LARGE_INTEGER NewFileOffset; //_asm int 3; if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify FileOffset to hide first InvisiblePartSize bytes NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize; return OldFastIoReadDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Call original handler return OldFastIoReadDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Function handles FastIoWrite BOOLEAN NewFastIoWrite( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) { LARGE_INTEGER NewFileOffset; //_asm int 3; if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify FileOffset to hide first InvisiblePartSize bytes NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize; return OldFastIoWriteDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } return OldFastIoWriteDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); } //Function handles FastIoQueryStandartInfo BOOLEAN NewFastIoQueryStandartInfo( IN struct _FILE_OBJECT *FileObject, IN BOOLEAN Wait, OUT PFILE_STANDARD_INFORMATION Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN struct _DEVICE_OBJECT *DeviceObject ) { //Call original handler BOOLEAN status = OldFastIoQueryStandartInfoDisp(FileObject, Wait, Buffer, IoStatus, DeviceObject); if ((FileObject) && (ThisIsOurFile(&FileObject->FileName))) { //_asm int 3; //Modify EndOfFile to hide first InvisiblePartSize bytes Buffer->EndOfFile.QuadPart -= InvisiblePartSize; } return status; } extern "C" NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName( IN PUNICODE_STRING ObjectPath, IN ULONG Attributes, IN PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK DesiredAccess OPTIONAL, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext OPTIONAL, OUT PVOID *ObjectPtr ); extern "C" PVOID IoDriverObjectType; //Function hooks given dispatch function (MajorFunction) VOID InterceptFunction(UCHAR MajorFunction, PDRIVER_OBJECT pDriverObject, OPTIONAL PDRIVER_DISPATCH *OldFunctionPtr, OPTIONAL PDRIVER_DISPATCH NewFunctionPtr) { PDRIVER_DISPATCH *TargetFn; TargetFn = &(pDriverObject->MajorFunction[MajorFunction]); //hook only if handler exists if (*TargetFn) { if (OldFunctionPtr) *OldFunctionPtr = *TargetFn; if (NewFunctionPtr) *TargetFn = NewFunctionPtr; } } //Function hooks given driver's dispatch functions NTSTATUS Intercept(PWSTR pwszDeviceName) { UNICODE_STRING DeviceName; NTSTATUS status; KIRQL OldIrql; _asm int 3; pDriverObject = NULL; RtlInitUnicodeString(&DeviceName, pwszDeviceName); status = ObReferenceObjectByName(&DeviceName, OBJ_CASE_INSENSITIVE, NULL, 0, (POBJECT_TYPE)IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDriverObject); if (pDriverObject) { //Raise IRQL to avoid context switch //when some pointer is semi-modified KeRaiseIrql(HIGH_LEVEL, &OldIrql); //hook dispatch functions InterceptFunction(IRP_MJ_READ, pDriverObject, &OldReadDisp, NewReadWriteDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject, &OldWriteDisp, NewReadWriteDisp); InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, &OldQueryDisp, NewQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, &OldSetInfoDisp, NewSetInfoDisp); InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, &OldDirCtlDisp, NewDirCtlDisp); //hook FastIo dispatch functions if FastIo table exists if (pDriverObject->FastIoDispatch) { //It would be better to copy FastIo table to avoid //messing with kernel memory protection, but it works as it is OldFastIoReadDisp = pDriverObject->FastIoDispatch->FastIoRead; pDriverObject->FastIoDispatch->FastIoRead = NewFastIoRead; OldFastIoWriteDisp = pDriverObject->FastIoDispatch->FastIoWrite; pDriverObject->FastIoDispatch->FastIoWrite = NewFastIoWrite; OldFastIoQueryStandartInfoDisp = pDriverObject->FastIoDispatch->FastIoQueryStandardInfo; pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = NewFastIoQueryStandartInfo; } KeLowerIrql(OldIrql); } return status; } //Function cancels hooking VOID UnIntercept() { KIRQL OldIrql; if (pDriverObject) { KeRaiseIrql(HIGH_LEVEL, &OldIrql); InterceptFunction(IRP_MJ_READ, pDriverObject, NULL, OldReadDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject, NULL, OldWriteDisp); InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, NULL, OldQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, NULL, OldSetInfoDisp); InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, NULL, OldDirCtlDisp); if (pDriverObject->FastIoDispatch) { pDriverObject->FastIoDispatch->FastIoRead = OldFastIoReadDisp; pDriverObject->FastIoDispatch->FastIoWrite = OldFastIoWriteDisp; pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = OldFastIoQueryStandartInfoDisp; } KeLowerIrql(OldIrql); ObDereferenceObject(pDriverObject); } }