check file content & redirect filehandle to a dummy file

hello all. Im new in driver programming so i am very with any help you guys can give. Basically what i want to do is develop a simple scanner driver which :

  1. looks at the contents of a file &,
  2. redirect the file handle to a dummy file (if the files contains a certain header)

both works fine by itself.

i can look the file using fltreadfile or zwreadfile in post create.

i can redirect any file by changing targetfileobject name and reparse it in precreate.

however things doesnt work as i combine it. fltreadfile doesnt work in precreate according to the documentation. so i use zwcreatefile() to get the filehandle and read it using zwreadfile(). but i got BSOD once i called zwcreatefile.

so i move them to postcreate. However the redirection doesnt seems to work in postcreate.

this is my code for file redirection

//////////////////////////
////FILE REDIRECTION
//////////////////////////
{

UNICODE_STRING nf;
PWCHAR pBuffer;
PFILE_OBJECT FileObject;
NTSTATUS status;

PUNICODE_STRING FileName;
PVOID FileNameBuffer;

FileObject = Data->Iopb->TargetFileObject;
FileName = &(Data->Iopb->TargetFileObject->FileName);

RtlInitUnicodeString( &nf, L"\.dummyfile\esafe_block.txt");
FileNameBuffer = ExAllocatePool(NonPagedPool,nf.MaximumLength);

if ( !FileNameBuffer )
{
DbgPrint(“insufficient resources\n”);
return STATUS_INSUFFICIENT_RESOURCES;
}

RtlCopyUnicodeString( FileName, &nf);

if ( FileName ) // Discard old name
{
ExFreePool(FileObject->FileName.Buffer );
}

FileName->Buffer = FileNameBuffer;
FileName->MaximumLength = nf.MaximumLength;

RtlCopyUnicodeString( FileName, &nf );

FltObjects->FileObject->FileName.Buffer = FileNameBuffer;
FltObjects->FileObject->FileName.MaximumLength = nf.MaximumLength;

RtlCopyUnicodeString(&FltObjects->FileObject->FileName,&nf);

Data->Iopb->TargetFileObject->RelatedFileObject = NULL;

Data->IoStatus.Information = IO_REPARSE;
Data->IoStatus.Status = STATUS_REPARSE;
FltSetCallbackDataDirty( Data );

}

any good advice?

I don’t see the call to ZwCreateFile in this code fragment. Also, as you may
have noticed when searching the archive, when you need help with a bugcheck
it is usually asked to paste the results of !analyze -v to the email.

Thanks,
Alex.

change ZwCreateFile to FltCreateFile. works on vista/7 but getting BSOD with bugcheck NTFS_FILE_SYSTEM on XP.

sorry i didnt include the complete code before.

here is the code

/////////////////////////////////////////////////////////////////////

FLT_PREOP_CALLBACK_STATUS
ScannerPreCreate (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext
)

{
int i;
BOOLEAN USBOn = FALSE;
NTSTATUS status;
BOOLEAN isDir;

UNREFERENCED_PARAMETER( FltObjects );
UNREFERENCED_PARAMETER( CompletionContext );

PAGED_CODE();

// see if its in usb list
for (i=0;i<max_usb> {
if (USBplist[i] == (ULONG) FltObjects->Volume) USBOn=TRUE;
}

if (USBOn==TRUE)
{

WCHAR dummyPath[200]=L"“;
UNICODE_STRING USdummyPath;
UNICODE_STRING dummyFile;
OBJECT_ATTRIBUTES myObjectAttributes;
IO_STATUS_BLOCK myIoStatusBlock;
HANDLE hFile = NULL;

WCHAR devicePath[200]=L”“;
FILE_STANDARD_INFORMATION fi;

RtlInitUnicodeString(&dummyFile,L”\esafe_block.txt");

if(!NT_SUCCESS( Data->IoStatus.Status ) ||
Data->IoStatus.Status == STATUS_REPARSE)
{
//DbgPrint(“skip reparse\n”);
return FLT_PREOP_COMPLETE;
}

///////////////////////////
///// get volume realName
///////////////////////////
{
UCHAR VPBuffer[sizeof(FLT_VOLUME_PROPERTIES)+512];
PFLT_VOLUME_PROPERTIES VolumeProperties = (PFLT_VOLUME_PROPERTIES)VPBuffer;
ULONG ReturnedLength;
int a;

FltGetVolumeProperties( FltObjects->Volume,
VolumeProperties,
sizeof( VPBuffer ),
&ReturnedLength );

wcsncpy(devicePath,VolumeProperties->RealDeviceName.Buffer,VolumeProperties->RealDeviceName.MaximumLength/2);
}

if(FltObjects->FileObject->FileName.Length > 2)
{
PFLT_FILE_NAME_INFORMATION nameInfo;
CHAR ext[100]=“”;
int i;

PVOID buffer;
LARGE_INTEGER ofset;
ULONG bytesRead;
PEPROCESS curproc;
CHAR CallerProcessName[NT_PROCNAMELEN];
UNICODE_STRING str;
int ftype;
int disposeFlag=0;

WCHAR fname[260]=L"“;
UNICODE_STRING usfname;
HANDLE fhandle=NULL;
BOOLEAN safeToOpen = FALSE;

disposeFlag = (Data->Iopb->Parameters.Create.Options & 0xff000000) >> 24;

DbgPrint(“Disposition Flag:%x\n”,(Data->Iopb->Parameters.Create.Options & 0xff000000) >> 24);
if(disposeFlag == 2)
return FLT_PREOP_SUCCESS_WITH_CALLBACK;

if(IsSystem() == TRUE)
return FLT_PREOP_SUCCESS_WITH_CALLBACK;

status = FltGetFileNameInformation(Data,
FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
&nameInfo);
if(!NT_SUCCESS(status))
return FLT_PREOP_SUCCESS_NO_CALLBACK;

FltParseFileNameInformation( nameInfo );

wcscpy(fname,FltObjects->FileObject->FileName.Buffer);

//DbgPrint(“fnameagain:%S\n”,fname);

//skip the dummyfile
if(wcscmp(fname,L”\.dummyfile\esafe_block.txt") == 0)
{
//pass to post routine without checking
DbgPrint(“its parsing handle to dummy skip\n”);
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

///////////////////////////////////////
///// READ THE FILE CONTENT
///////////////////////////////////////

/// getting the fullpath name
wcscpy(fname,devicePath);
wcscat(fname,FltObjects->FileObject->FileName.Buffer);

/// open file handle
DbgPrint(“fname:%S\n”,fname);
RtlInitUnicodeString(&usfname,fname);
InitializeObjectAttributes( &myObjectAttributes, &usfname,OBJ_CASE_INSENSITIVE, NULL, NULL );
status = FltCreateFile(FltObjects->Filter,FltObjects->Instance,&fhandle,GENERIC_READ,&myObjectAttributes,&myIoStatusBlock,
NULL,FILE_ATTRIBUTE_NORMAL,0,FILE_OPEN ,FILE_NON_DIRECTORY_FILE|FILE_SEQUENTIAL_ONLY,0,0,NULL);

if(!NT_SUCCESS(status))
{
DbgPrint(“FltCreateFile FAILED\n”);
if(status == STATUS_FILE_IS_A_DIRECTORY)
DbgPrint(“File is DIR\n”);
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

/// allocate buffer
buffer = FltAllocatePoolAlignedWithTag( FltObjects->Instance,
NonPagedPool,
PAGE_SIZE * 8,
‘nacS’ );

ofset.HighPart =0;
ofset.LowPart =0;

/// read file
status = ZwReadFile(fhandle,NULL,NULL,NULL,&myIoStatusBlock,buffer,PAGE_SIZE * 8,&ofset,NULL);

if(NT_SUCCESS(status))
{
PUCHAR a;
int ftype=0;

a = (PUCHAR) buffer;
DbgPrint(“SUCCESS zwreadfile\n”);

for(i=0;i<10;i++)
DbgPrint(“%d: %c, %d\n”,i,a[i],a[i]);

strcpy(ext,“”);
if(nameInfo->Extension.Length > 0)
{
for(i=0;iExtension.MaximumLength/2;i++)
{
ext[i] = (CHAR)nameInfo->Extension.Buffer[i];
}
ext[i]=0;
}

ftype=checkHeaderSUKI(buffer,strlen(a),nameInfo);
DbgPrint(“ftype:%d\n”,ftype);

safeToOpen = checkSafeType(blockMode,ftype,ext);
DbgPrint(“safeToOpen:%d\n”,safeToOpen);

}

// cleanup
ZwClose(fhandle);

DbgPrint(“free buffer\n”);
FltFreePoolAlignedWithTag(
FltObjects->Instance,// in PFLT_INSTANCE Instance,
buffer,//
in PVOID Buffer,
‘nacS’//__in ULONG Tag
);

FltReleaseFileNameInformation(nameInfo);

//////////////////////////////////////

if (safeToOpen == FALSE)
{

DbgPrint(“blocking the file\n”); // not blocking but redirect it to dummy file

//////////////////////////
////FILE REDIRECTION
//////////////////////////
{

UNICODE_STRING nf;
PWCHAR pBuffer;
PFILE_OBJECT FileObject;
NTSTATUS status;

PUNICODE_STRING FileName;
PVOID FileNameBuffer;

FileObject = Data->Iopb->TargetFileObject;
FileName = &(Data->Iopb->TargetFileObject->FileName);

RtlInitUnicodeString( &nf, L"\.dummyfile\esafe_block.txt");
FileNameBuffer = ExAllocatePool(NonPagedPool,nf.MaximumLength);

if ( !FileNameBuffer )
{
DbgPrint(“insufficient resources\n”);
return STATUS_INSUFFICIENT_RESOURCES;
}

RtlCopyUnicodeString( FileName, &nf);

if ( FileName ) // Discard old name
{
ExFreePool(FileObject->FileName.Buffer );
}

FileName->Buffer = FileNameBuffer;
FileName->MaximumLength = nf.MaximumLength;

RtlCopyUnicodeString( FileName, &nf );

FltObjects->FileObject->FileName.Buffer = FileNameBuffer;
FltObjects->FileObject->FileName.MaximumLength = nf.MaximumLength;

RtlCopyUnicodeString(&FltObjects->FileObject->FileName,&nf);

Data->Iopb->TargetFileObject->RelatedFileObject = NULL;

//Data->IoStatus.Information = IO_REPARSE;
//Data->IoStatus.Status = STATUS_REPARSE;
FltSetCallbackDataDirty( Data );

//DbgPrint(“targetfile:%S\n”,FileObject->FileName.Buffer);
//DbgPrint(“filename:%S\n”,FltObjects->FileObject->FileName.Buffer);

DbgPrint(“File redirection reparse\n”);
//RtlFreeUnicodeString(&nf);
//return FLT_PREOP_COMPLETE;
}

}
}
else
{
///////////////////////////
///// get volume realName
///////////////////////////
{
wcscpy(dummyPath,devicePath);
wcscat(dummyPath,L"\.dummyfile\0");

RtlInitUnicodeString(&USdummyPath,dummyPath);

///////////////////////////////////////////
//// create folder for dummy file to reside
///////////////////////////////////////////
InitializeObjectAttributes( &myObjectAttributes, &USdummyPath,OBJ_CASE_INSENSITIVE, NULL, NULL );
status = ZwCreateFile(&hFile,FILE_READ_ACCESS,&myObjectAttributes,&myIoStatusBlock,NULL,FILE_ATTRIBUTE_HIDDEN,0,FILE_OPEN,FILE_DIRECTORY_FILE,0,0);

if(!NT_SUCCESS(status)) // the folder is not there
ZwCreateFile(&hFile,FILE_READ_ACCESS,&myObjectAttributes,&myIoStatusBlock,NULL,FILE_ATTRIBUTE_HIDDEN,0,FILE_OPEN_IF,FILE_DIRECTORY_FILE,0,0);

ZwClose(hFile);

//-----------------------------------------
}
////////////////////
//create dummy file
////////////////////
{

NTSTATUS status =0;
CHAR cBuf[100] = “THIS IS A BLOCKED FILE”;

wcscat(dummyPath,dummyFile.Buffer);
RtlInitUnicodeString(&USdummyPath,dummyPath);

InitializeObjectAttributes( &myObjectAttributes, &USdummyPath,OBJ_CASE_INSENSITIVE, NULL, NULL );
status = ZwCreateFile(&hFile,GENERIC_ALL,&myObjectAttributes,&myIoStatusBlock,NULL,0,0,FILE_OPEN,FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE|FILE_WRITE_THROUGH,NULL, 0);

if(!NT_SUCCESS(status))
{
status = ZwCreateFile(&hFile,GENERIC_ALL,&myObjectAttributes,&myIoStatusBlock,NULL,0,0,FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE|FILE_WRITE_THROUGH,NULL, 0);
}
else //if file is there skip
{
ZwClose(hFile);
//RtlFreeUnicodeString(&USdummyPath);
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

if(NT_SUCCESS(status))
{
if( NT_SUCCESS(status))
{
ZwWriteFile(hFile,NULL,NULL,NULL,&myIoStatusBlock,cBuf,strlen(cBuf),NULL,NULL);
}
}

ZwClose(hFile);

//RtlFreeUnicodeString(&USdummyPath);

}
}
}

//{
// NTSTATUS status;

// if (IoThreadToProcess( Data->Thread ) == ScannerData.UserProcess)
// {
// //DbgPrint( “!!! scanner.sys – allowing create for trusted process \n” );
// return FLT_PREOP_SUCCESS_NO_CALLBACK;
// }
//}

DbgPrint(“END OF PRECREATE\n”);
//passing to POST create
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
//////////////////[END OF CODE]/////////////////////

This is the !analyze -v output



Bugcheck Analysis



NTFS_FILE_SYSTEM (24)
If you see NtfsExceptionFilter on the stack then the 2nd and 3rd
parameters are the exception record and context record. Do a .cxr
on the 3rd parameter and then kb to obtain a more informative stack
trace.
Arguments:
Arg1: 001902fe
Arg2: b8207704
Arg3: b8207400
Arg4: b7d96de2

Debugging Details:
------------------

EXCEPTION_RECORD: b8207704 – (.exr 0xffffffffb8207704)
ExceptionAddress: b7d96de2 (Ntfs!NtfsCommonCreate+0x0000065c)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000126
Attempt to read from address 00000126

CONTEXT: b8207400 – (.cxr 0xffffffffb8207400)
eax=88d6b2a0 ebx=b8207960 ecx=00000094 edx=00000000 esi=88d2dc68 edi=00000000
eip=b7d96de2 esp=b82077cc ebp=b8207908 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010202
Ntfs!NtfsCommonCreate+0x65c:
b7d96de2 66837c4afe5c cmp word ptr [edx+ecx2-2],5Ch ds:0023:00000126=???
Resetting default scope

CUSTOMER_CRASH_COUNT: 3

PROCESS_NAME: explorer.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at “0x%08lx” referenced memory at “0x%08lx”. The memory could not be “%s”.

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at “0x%08lx” referenced memory at “0x%08lx”. The memory could not be “%s”.

EXCEPTION_PARAMETER1: 00000000

EXCEPTION_PARAMETER2: 00000126

READ_ADDRESS: 00000126

FOLLOWUP_IP:
Ntfs!NtfsCommonCreate+65c
b7d96de2 66837c4afe5c cmp word ptr [edx+ecx
2-2],5Ch

FAULTING_IP:
Ntfs!NtfsCommonCreate+65c
b7d96de2 66837c4afe5c cmp word ptr [edx+ecx*2-2],5Ch

BUGCHECK_STR: 0x24

DEFAULT_BUCKET_ID: NULL_CLASS_PTR_DEREFERENCE

LAST_CONTROL_TRANSFER: from b7d98f2d to b7d96de2

STACK_TEXT:
b8207908 b7d98f2d 890cf008 88d2dad8 b8207960 Ntfs!NtfsCommonCreate+0x65c
b82079ec 804ef19f 892791a0 88d2dad8 88d2dad8 Ntfs!NtfsFsdCreate+0x1dc
b82079fc b7e2ce9b 00000000 88d2dad8 88d2dc8c nt!IopfCallDriver+0x31
b8207a20 b7e39754 b8207a40 88e78ee8 00000000 fltMgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x20b
b8207a5c 804ef19f 88e78ee8 88d2dad8 88d2dad8 fltMgr!FltpCreate+0x26a
b8207a6c 80583220 89430ab8 88d761cc b8207c04 nt!IopfCallDriver+0x31
b8207b4c 805bf488 89430ad0 00000000 88d76128 nt!IopParseDevice+0xa12
b8207bc4 805bba14 00000000 b8207c04 00000040 nt!ObpLookupObjectName+0x53c
b8207c18 80576057 00000000 00000000 00000001 nt!ObOpenObjectByName+0xea
b8207c94 805769ce 039ffdcc 80100080 039ffd6c nt!IopCreateFile+0x407
b8207cf0 805790d8 039ffdcc 80100080 039ffd6c nt!IoCreateFile+0x8e
b8207d30 8054167c 039ffdcc 80100080 039ffd6c nt!NtCreateFile+0x30
b8207d30 7c90e514 039ffdcc 80100080 039ffd6c nt!KiFastCallEntry+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
039ffdc4 00000000 00000000 00000000 00000000 0x7c90e514

SYMBOL_STACK_INDEX: 0

SYMBOL_NAME: Ntfs!NtfsCommonCreate+65c

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: Ntfs

IMAGE_NAME: Ntfs.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 48025be5

STACK_COMMAND: .cxr 0xffffffffb8207400 ; kb

FAILURE_BUCKET_ID: 0x24_Ntfs!NtfsCommonCreate+65c

BUCKET_ID: 0x24_Ntfs!NtfsCommonCreate+65c

Followup: MachineOwner
---------</max_usb>

There are quite a few things wrong in here. I’ll mention some of them but the list is far from complete.
please don’t allocate buffers on the stack in the kernel (like for WCHAR fname[260]). Also, the maximum path length in the kernel is not MAX_PATH from user mode so the 260 makes no sense. You probably don’t want to use the “CHAR” type at all. Also, this path “\.dummyfile\0” doesn’t make sense. Strings in the kernel don’t need to be null terminated (mainly because they’re pretty much always UNICODE_STRING and those are counted). Also, no sense in looking at Data->IoStatus.Status in a preOp routine.

Anyway, looking a the bugcheck i see that it’s failed in this instruction “cmp word ptr [edx+ecx*2-2],5Ch”, which tells me it’s parsing a string, looking for a '' character. Looking at the stack from !analyze -v i don’t see any call to FltCreateFile or ZwCreateFile and i see fltMgr!FltpLegacyProcessingAfterPreCallbacksCompleted, which tells me that this failure is not your filter calling a function but rather your filter having completed the preop processing (returning something like FLT_PREOP_SUCCESS_WITH_CALLBACK). My guess is you’ve changed the name in the FILE_OBJECT->FileName and then sent the request on to NTFS instead of returning STATUS_REPARSE. In fact, I can see some place in the code where you do just that but i can’t be certain that’s where you’ve actually set this name.

Thanks,
Alex.

Alex can you explain the sentence:
"please don’t allocate buffers on the stack in the kernel (like for WCHAR fname[260]). "

Allocating buffers on the stack is dangerous because the pointer can later be used outside the function scope, but is there anything else I’m missing here thats wrong?

The reason I ask this is because you wrote “don’t allocate buffers on the stack in the kernel”.
Does the fact that it run in the kernel make any difference?

You don’t have a lot of stack space in the kernel and you could cause a stack overflow…

–Mark Caridi
OSR, Open Systems Resources, Inc.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@hotmail.com
Sent: Monday, March 07, 2011 12:26 PM
To: Windows File Systems Devs Interest List
Subject: RE:[ntfsd] check file content & redirect filehandle to a dummy file

Alex can you explain the sentence:
"please don’t allocate buffers on the stack in the kernel (like for WCHAR fname[260]). "

Allocating buffers on the stack is dangerous because the pointer can later be used outside the function scope, but is there anything else I’m missing here thats wrong?

The reason I ask this is because you wrote “don’t allocate buffers on the stack in the kernel”.
Does the fact that it run in the kernel make any difference?


NTFSD is sponsored by OSR

For our schedule of debugging and file system seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer