//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);
}
}
// 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);
}
}