📄 filemon.c
字号:
//
// Set the completion routine.
//
IoSetCompletionRoutine(irp, FilemonQueryFileComplete, 0, TRUE, TRUE, TRUE);
//
// Send it to the FSD
//
(void) IoCallDriver(DeviceObject, irp);
//
// Wait for the I/O
//
KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
//
// Done! Note that since our completion routine frees the IRP we cannot
// touch the IRP now.
//
return NT_SUCCESS( IoStatusBlock.Status );
}
//----------------------------------------------------------------------
//
// FilemonGetFullPath
//
// Takes a fileobject and filename and returns a canonical path,
// nicely formatted, in fullpathname.
//
//----------------------------------------------------------------------
VOID
FilemonGetFullPath(
BOOLEAN createPath,
PFILE_OBJECT fileObject,
PHOOK_EXTENSION hookExt,
PCHAR fullPathName
)
{
ULONG pathLen, prefixLen, slashes;
PCHAR pathOffset, ptr;
BOOLEAN gotPath;
PFILE_OBJECT relatedFileObject;
PHASH_ENTRY hashEntry, newEntry;
ANSI_STRING fileName;
ANSI_STRING relatedName;
PFILE_NAME_INFORMATION fileNameInfo;
FILE_INTERNAL_INFORMATION fileInternalInfo;
UNICODE_STRING fullUniName;
ULONGLONG mftIndex;
//
// Only do this if a GUI is active and filtering is on
//
if( fullPathName ) fullPathName[0] = 0;
if( !FilterOn || !hookExt || !hookExt->Hooked || !fullPathName) {
return;
}
//
// Lookup the object in the hash table to see if a name
// has already been generated for it
//
KeEnterCriticalRegion();
ExAcquireResourceSharedLite( &HashResource, TRUE );
hashEntry = HashTable[ HASHOBJECT( fileObject ) ];
while( hashEntry && hashEntry->FileObject != fileObject ) {
hashEntry = hashEntry->Next;
}
//
// Did we find an entry?
//
if( hashEntry ) {
//
// Yes, so get the name from the entry.
//
strcpy( fullPathName, hashEntry->FullPathName );
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
return;
}
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
//
// We didn't find the name in the hash table so let's either ask
// the file system for it or construct it from the file objects.
//
//
// Calculate prefix length
//
switch( hookExt->Type ) {
case NPFS:
prefixLen = NAMED_PIPE_PREFIX_LENGTH;
break;
case MSFS:
prefixLen = MAIL_SLOT_PREFIX_LENGTH;
break;
default:
if( !fileObject ||
fileObject->DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM ) {
prefixLen = 0;
} else {
prefixLen = 2; // "C:"
}
break;
}
//
// If there's no file object, we can't even ask for a name.
//
if( !fileObject ) {
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
return;
}
//
// Initialize variables
//
fileName.Buffer = NULL;
relatedName.Buffer = NULL;
gotPath = FALSE;
//
// Check for special case first: NTFS volume and a file object
// with no name. It might be a metadata file that we "know" the name of. This
// special case also stops us from querying NTFS for the name of a metadata
// file on versions of NTFS prior to Whistler, which is a good thing since
// that causes hangs and crashes. On Whistler metadata files have file names.
//
if( !fileObject->FileName.Buffer && hookExt->FsAttributes &&
!memcmp( hookExt->FsAttributes->FileSystemName, L"NTFS", sizeof(L"NTFS")-sizeof(WCHAR))) {
//
// The only file that is opened without a name is a volume
//
if( createPath ) {
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
//
// Return right here without inserting this into the hash table, since this might
// be the cleanup path of a metadata file and we can retrieve the metada's index
// at a later point.
//
return;
} else if( FilemonQueryFile( hookExt->FileSystem, fileObject, FileInternalInformation,
&fileInternalInfo, sizeof( fileInternalInfo ))) {
//
// Use the name in the metadata name index
//
mftIndex = fileInternalInfo.IndexNumber.QuadPart & ~0xF0000000;
if( mftIndex <= MAX_NTFS_METADATA_FILE ) {
sprintf( fullPathName, "%C:\\%s", hookExt->LogicalDrive, NtfsMetadataFileNames[ mftIndex ] );
gotPath = TRUE;
}
}
}
//
// If we are not in the create path, we can ask the file system for the name. If we
// are in the create path, we can't ask the file system for the name of the file object, since
// the file system driver hasn't even seen the file object yet.
//
if( !gotPath && !createPath ) {
//
// Ask the file system for the name of the file, which its required to be
// able to provide for the Win32 filename query function. We could use the
// undocumented ObQueryNameString, but then we'd have to worry about
// re-entrancy issues, since that call generates the IRP that we create
// manually here. Since we send the IRP to the FSD below us, we don't need
// to worry about seeing the IRP in our dispatch entry point. This can fail
// in some cases, so we fall back on constructing the name ourselves if
// we have to.
//
fileNameInfo = (PFILE_NAME_INFORMATION) ExAllocatePool( NonPagedPool,
MAXPATHLEN*sizeof(WCHAR) );
if( fileNameInfo &&
FilemonQueryFile(hookExt->FileSystem, fileObject, FileNameInformation,
fileNameInfo, (MAXPATHLEN - prefixLen - 1)*sizeof(WCHAR) )) {
fullUniName.Length = (SHORT) fileNameInfo->FileNameLength;
fullUniName.Buffer = fileNameInfo->FileName;
if( NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fullUniName, TRUE ))) {
fullPathName[ fileName.Length + prefixLen ] = 0;
if( hookExt->Type == NPFS ) {
strcpy( fullPathName, NAMED_PIPE_PREFIX );
} else if( hookExt->Type == MSFS ) {
strcpy( fullPathName, MAIL_SLOT_PREFIX );
} else if( fileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM ) {
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
} else {
//
// No prefix for network devices
//
}
memcpy( &fullPathName[prefixLen], fileName.Buffer, fileName.Length );
gotPath = TRUE;
RtlFreeAnsiString( &fileName );
fileName.Buffer = NULL;
}
}
if( fileNameInfo ) ExFreePool( fileNameInfo );
}
//
// If we don't have a name yet then we are in the create path, or we failed
// when we asked the file system for the name. In that case we'll go ahead
// and construct the name based on file object names.
//
if( !gotPath ) {
//
// If there is no file name at this point, just return "DEVICE" to indicate
// raw access to a device
//
if( !fileObject->FileName.Buffer ) {
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
return;
}
//
// Create the full path name. First, calculate the length taking into
// account space for seperators and the leading prefix
//
if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fileObject->FileName, TRUE ))) {
if( hookExt->Type == NPFS ) sprintf( fullPathName, "%s: <Out of Memory>", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: <Out of Memory>", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: <Out of Memory>", hookExt->LogicalDrive );
return;
}
pathLen = fileName.Length + prefixLen;
relatedFileObject = fileObject->RelatedFileObject;
//
// Only look at related file object if this is a relative name
//
if( fileObject->FileName.Buffer[0] != L'\\' &&
relatedFileObject && relatedFileObject->FileName.Length ) {
if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &relatedName, &relatedFileObject->FileName, TRUE ))) {
if( hookExt->Type == NPFS ) sprintf( fullPathName, "%s: <Out of Memory>", NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) sprintf( fullPathName, "%s: <Out of Memory>", MAIL_SLOT_PREFIX );
else sprintf( fullPathName, "%C: <Out of Memory>", hookExt->LogicalDrive );
RtlFreeAnsiString( &fileName );
return;
}
pathLen += relatedName.Length+1;
}
//
// Add the drive letter first at the front of the name
//
if( hookExt->Type == NPFS ) strcpy( fullPathName, NAMED_PIPE_PREFIX );
else if( hookExt->Type == MSFS ) strcpy( fullPathName, MAIL_SLOT_PREFIX );
else if( fileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM ) {
sprintf( fullPathName, "%C:", hookExt->LogicalDrive );
}
//
// If the name is too long, quit now
//
if( pathLen >= MAXPATHLEN ) {
strcat( fullPathName, " <Name Too Long>" );
} else {
//
// Now we can build the path name
//
fullPathName[ pathLen ] = 0;
pathOffset = fullPathName + pathLen - fileName.Length;
memcpy( pathOffset, fileName.Buffer, fileName.Length + 1 );
if( fileObject->FileName.Buffer[0] != L'\\' &&
relatedFileObject && relatedFileObject->FileName.Length ) {
//
// Copy the component, adding a slash separator
//
*(pathOffset - 1) = '\\';
pathOffset -= relatedName.Length + 1;
memcpy( pathOffset, relatedName.Buffer, relatedName.Length );
//
// If we've got to slashes at the front zap one
//
if( pathLen > 3 && fullPathName[2] == '\\' && fullPathName[3] == '\\' ) {
strcpy( fullPathName + 2, fullPathName + 3 );
}
}
}
}
if( fileName.Buffer ) RtlFreeAnsiString( &fileName );
if( relatedName.Buffer ) RtlFreeAnsiString( &relatedName );
//
// Network redirector names already specify a share name that we
// have to strip:
//
// \X:\computer\share\realpath
//
// And we want to present:
//
// X:\realpath
//
// to the user.
//
if( fileObject->DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM &&
strlen( fullPathName ) >= strlen("\\X:\\") ) {
//
// If this is Win2k the name is specified like this:
//
// \;X:0\computer\share\realpath
//
// so we have to handle that case as well
//
if( fullPathName[1] == ';' ) {
//
// Win2K-style name. Grab the drive letter
// and skip over the share
//
fullPathName[0] = fullPathName[2];
fullPathName[1] = ':';
fullPathName[2] = '\\';
//
// The third slash after the drive is the
// start of the real path (we start scanning
// at the ':' since we don't want to make assumptions
// about the length of the number).
//
slashes = 0;
ptr = &fullPathName[3];
while( *ptr && slashes != 3 ) {
if( *ptr == '\\' ) slashes++;
ptr++;
}
strcpy( &fullPathName[3], ptr );
} else if( fullPathName[2] == ':' ) {
//
// NT 4-style name. Skip the share name
//
fullPathName[0] = fullPathName[1];
fullPathName[1] = ':';
fullPathName[2] = '\\';
//
// The second slash after the drive's slash (x:\)
// is the start of the real path
//
slashes = 0;
ptr = &fullPathName[3];
while( *ptr && slashes != 3 ) {
if( *ptr == '\\' ) slashes++;
ptr++;
}
strcpy( &fullPathName[3], ptr );
} else {
//
// Its a UNC path, so add a leading slash
//
RtlMoveMemory( &fullPathName[1], fullPathName, strlen( fullPathName ) + 1);
fullPathName[0] = '\\';
}
}
//
// Allocate a hash entry
//
newEntry = ExAllocatePool( NonPagedPool,
sizeof(HASH_ENTRY ) + strlen( fullPathName ) + 1);
//
// If no memory for a new entry, oh well.
//
if( newEntry ) {
//
// Fill in the new entry
//
newEntry->FileObject = fileObject;
strcpy( newEntry->FullPathName, fullPathName );
//
// Put it in the hash table
//
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite( &HashResource, TRUE );
newEntry->Next = HashTable[ HASHOBJECT(fileObject) ];
HashTable[ HASHOBJECT(fileObject) ] = newEntry;
ExReleaseResourceLite( &HashResource );
KeLeaveCriticalRegion();
}
}
//----------------------------------------------------------------------
//
// FilemonGetProcessNameOffset
//
// In an effort to remain version-independent, rather than using a
// hard-coded into the KPEB (Kernel Process Environment Block), we
// scan the KPEB looking for the name, which should match that
// of the system process. This is because we are in the system process'
// context in DriverEntry, where this is called.
//
//----------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -