FileMon中获取文件全路径的方法中最为关键的技术是通过自己下发IRP给下层驱动。下发请求的cmd为FileNameInformation或其他,具体请看代码。而在filespy中的获取的全路径的方法是通过ObQueryNameString()函数得到的。ObQueryNameString这个函数只能在打开IRP(IRP_MJ_CREATE)和清除(IRP_MJ_CLEANUP)或者关闭(IRP_MJ_CLOSE)IRP的处理中使用,否则很容易锁死,而用IRP的方式却没有这个问题。通过查看wrk相关代码,发现ObQueryNameString这个函数是首先检查默认对象查询名称函数是否存在,如存在则直接调用之,否则是用一种比较复杂的方法得到,这一部分的代码,贴在下面。我们注意到在FileObject对象中有一个FileName域,但是这个域是可以被其他过滤驱动修改的,也就是说通过这个域得到的数据不一定是真实的。
FileMon获取文件全路径方法:
<textarea cols="86" rows="20" name="code" class="cpp">//---------------------------------------------------------------------- // // FilemonQueryFile // // This function retrieves the "standard" information for the // underlying file system, asking for the filename in particular. // //---------------------------------------------------------------------- BOOLEAN FilemonQueryFile( PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileQueryBuffer, ULONG FileQueryBufferLength ) { PIRP irp; KEVENT event; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION ioStackLocation; DbgPrint(("Getting file name for %x/n", FileObject)); // // Initialize the event // KeInitializeEvent(&event, SynchronizationEvent, FALSE); // // Allocate an irp for this request. This could also come from a // private pool, for instance. // irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if(!irp) { // // Failure! // return FALSE; } // // Build the IRP's main body // irp->AssociatedIrp.SystemBuffer = FileQueryBuffer; irp->UserEvent = &event; irp->UserIosb = &IoStatusBlock; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject = FileObject; irp->RequestorMode = KernelMode; irp->Flags = 0; // // Set up the I/O stack location. // ioStackLocation = IoGetNextIrpStackLocation(irp); ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION; ioStackLocation->DeviceObject = DeviceObject; ioStackLocation->FileObject = FileObject; ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength; ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass; // // 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(); } } </textarea>
ObQueryNameString中获取方法:
<textarea cols="85" rows="15" name="code" class="cpp">NTSTATUS ObpQueryNameString ( IN PVOID Object, OUT POBJECT_NAME_INFORMATION ObjectNameInfo, IN ULONG Length, OUT PULONG ReturnLength, IN KPROCESSOR_MODE Mode ) /*++ Routine description: This routine processes a query of an object's name information Arguments: Object - Supplies the object being queried ObjectNameInfo - Supplies the buffer to store the name string information Length - Specifies the length, in bytes, of the original object name info buffer. ReturnLength - Contains the number of bytes already used up in the object name info. On return this receives an updated byte count. (Length minus ReturnLength) is really now many bytes are left in the output buffer. The buffer supplied to this call may actually be offset within the original users buffer Mode - Mode of caller Return Value: An appropriate status value --*/ { NTSTATUS Status = STATUS_UNSUCCESSFUL; POBJECT_HEADER ObjectHeader; POBJECT_HEADER_NAME_INFO NameInfo; POBJECT_HEADER ObjectDirectoryHeader; POBJECT_DIRECTORY ObjectDirectory; ULONG NameInfoSize = 0; PUNICODE_STRING String; PWCH StringBuffer; ULONG NameSize; PVOID ReferencedObject = NULL; BOOLEAN DoFullQuery = TRUE; ULONG BufferLength; PWCH OriginalBuffer; BOOLEAN ForceRetry = FALSE; PAGED_CODE(); // // Get the object header and name info record if it exists // ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object ); NameInfo = ObpReferenceNameInfo( ObjectHeader ); // // If the object type has a query name callback routine then // that is how we get the name // if (ObjectHeader->Type->TypeInfo.QueryNameProcedure != NULL) { try { #if DBG KIRQL SaveIrql; #endif ObpBeginTypeSpecificCallOut( SaveIrql ); ObpEndTypeSpecificCallOut( SaveIrql, "Query", ObjectHeader->Type, Object ); Status = (*ObjectHeader->Type->TypeInfo.QueryNameProcedure)( Object, (BOOLEAN)((NameInfo != NULL) && (NameInfo->Name.Length != 0)), ObjectNameInfo, Length, ReturnLength, Mode ); } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } ObpDereferenceNameInfo( NameInfo ); return( Status ); } // // Otherwise, the object type does not specify a query name // procedure so we get to do the work. The first thing // to check is if the object doesn't even have a name. If // object doesn't have a name then we'll return an empty name // info structure. // RETRY: if ((NameInfo == NULL) || (NameInfo->Name.Buffer == NULL)) { // // Compute the length of our return buffer, set the output // if necessary and make sure the supplied buffer is large // enough // NameInfoSize = sizeof( OBJECT_NAME_INFORMATION ); try { *ReturnLength = NameInfoSize; } except( EXCEPTION_EXECUTE_HANDLER ) { ObpDereferenceNameInfo( NameInfo ); return( GetExceptionCode() ); } if (Length < NameInfoSize) { ObpDereferenceNameInfo( NameInfo ); return( STATUS_INFO_LENGTH_MISMATCH ); } // // Initialize the output buffer to be an empty string // and then return to our caller // try { ObjectNameInfo->Name.Length = 0; ObjectNameInfo->Name.MaximumLength = 0; ObjectNameInfo->Name.Buffer = NULL; } except( EXCEPTION_EXECUTE_HANDLER ) { // // Fall through, since we cannot undo what we have done. // ObpDereferenceNameInfo(NameInfo); return( GetExceptionCode() ); } ObpDereferenceNameInfo(NameInfo); return( STATUS_SUCCESS ); } try { // // The object does have a name but now see if this is // just the root directory object in which case the name size // is only the "/" character // if (Object == ObpRootDirectoryObject) { NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR ); } else { // // The named object is not the root so for every directory // working out way up we'll add its size to the name keeping // track of "/" characters inbetween each component. We first // start with the object name itself and then move on to // the directories // ObjectDirectory = NameInfo->Directory; if (ObjectDirectory) { ObfReferenceObject( ObjectDirectory ); ReferencedObject = ObjectDirectory; } NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length; ObpDereferenceNameInfo( NameInfo ); NameInfo = NULL; // // While we are not at the root we'll keep moving up // while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) { // // Get the name information for this directory // ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory ); NameInfo = ObpReferenceNameInfo( ObjectDirectoryHeader ); if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) { // // This directory has a name so add it to the accumulated // size and move up the tree // NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length; ObjectDirectory = NameInfo->Directory; if (ObjectDirectory) { ObfReferenceObject( ObjectDirectory ); } ObpDereferenceNameInfo( NameInfo ); NameInfo = NULL; ObDereferenceObject( ReferencedObject ); ReferencedObject = ObjectDirectory; // // UNICODE_STRINGs can only hold MAXUSHORT bytes. // if (NameSize > MAXUSHORT) { break; } } else { // // This directory does not have a name so we'll give it // the "..." name and stop the loop // NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + OBP_MISSING_NAME_LITERAL_SIZE; break; } } } // // UNICODE_STRINGs can only hold MAXUSHORT bytes // if (NameSize > MAXUSHORT) { Status = STATUS_NAME_TOO_LONG; DoFullQuery = FALSE; leave; } // // At this point NameSize is the number of bytes we need to store the // name of the object from the root down. The total buffer size we are // going to need will include this size, plus object name information // structure, plus an ending null character // NameInfoSize = NameSize + sizeof( OBJECT_NAME_INFORMATION ) + sizeof( UNICODE_NULL ); // // Set the output size and make sure the supplied buffer is large enough // to hold the information // try { *ReturnLength = NameInfoSize; } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); DoFullQuery = FALSE; leave; } if (Length < NameInfoSize) { Status = STATUS_INFO_LENGTH_MISMATCH; DoFullQuery = FALSE; leave; } } finally { ObpDereferenceNameInfo( NameInfo ); NameInfo = NULL; if (ReferencedObject) { ObDereferenceObject( ReferencedObject ); ReferencedObject = NULL; } } if (!DoFullQuery) { return Status; } NameInfo = ObpReferenceNameInfo( ObjectHeader ); // // Check whether someone else removed the name meanwhile // if (!NameInfo) { // // The name is gone, we need to jump to the code path that handles // empty object name // goto RETRY; } // // Set the String buffer to point to the byte right after the // last byte in the output string. This following logic actually // fills in the buffer backwards working from the name back to the // root // StringBuffer = (PWCH)ObjectNameInfo; StringBuffer = (PWCH)((PCH)StringBuffer + NameInfoSize); OriginalBuffer = (PWCH)((PCH)ObjectNameInfo + sizeof( OBJECT_NAME_INFORMATION )); try { // // Terminate the string with a null and backup one unicode // character // *--StringBuffer = UNICODE_NULL; // // If the object in question is not the root directory // then we are going to put its name in the string buffer // When we finally reach the root directory we'll append on // the final "/" // if (Object != ObpRootDirectoryObject) { // // Add in the objects name // String = &NameInfo->Name; StringBuffer = (PWCH)((PCH)StringBuffer - String->Length); if (StringBuffer <= OriginalBuffer) { ForceRetry = TRUE; leave; } RtlCopyMemory( StringBuffer, String->Buffer, String->Length ); // // While we are not at the root directory we'll keep // moving up // ObjectDirectory = NameInfo->Directory; if (ObjectDirectory) { // // Reference the directory for this object to make sure it's // valid while looking up // ObfReferenceObject( ObjectDirectory ); ReferencedObject = ObjectDirectory; } ObpDereferenceNameInfo( NameInfo ); NameInfo = NULL; while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) { // // Get the name information for this directory // ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory ); NameInfo = ObpReferenceNameInfo( ObjectDirectoryHeader ); // // Tack on the "/" between the last name we added and // this new name // *--StringBuffer = OBJ_NAME_PATH_SEPARATOR; // // Preappend the directory name, if it has one, and // move up to the next directory. // if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) { String = &NameInfo->Name; StringBuffer = (PWCH)((PCH)StringBuffer - String->Length); if (StringBuffer <= OriginalBuffer) { ForceRetry = TRUE; leave; } RtlCopyMemory( StringBuffer, String->Buffer, String->Length ); ObjectDirectory = NameInfo->Directory; if (ObjectDirectory) { ObfReferenceObject( ObjectDirectory ); } // // Dereference the name info (it must be done before dereferencing the object) // ObpDereferenceNameInfo( NameInfo ); NameInfo = NULL; ObDereferenceObject( ReferencedObject ); ReferencedObject = ObjectDirectory; } else { // // The directory is nameless so use the "..." for // its name and break out of the loop // StringBuffer = (PWCH)((PCH)StringBuffer - OBP_MISSING_NAME_LITERAL_SIZE); // // Because we don't hold the global lock any more, we can have a special case // where a directory of 1 or 2 letters name AND inserted into the root // can go away meanwhile and "..." will be too long to fit the remaining space // We already copied the buffer so we cannot rollback everything we done. // We'll return /.. if the original directory was 1 char length, // /../ for 2 char length // if (StringBuffer < OriginalBuffer) { StringBuffer = OriginalBuffer; } RtlCopyMemory( StringBuffer, OBP_MISSING_NAME_LITERAL, OBP_MISSING_NAME_LITERAL_SIZE ); // // Test if we are in the case commented above. If yes, we need to move the // current pointer to the next char, so the final assignment for / a few lines // below will take effect on the start of the block. // if (StringBuffer == OriginalBuffer) { StringBuffer++; } break; } } } // // Tack on the "/" for the root directory and then set the // output unicode string variable to have the right size // and point to the right spot. // *--StringBuffer = OBJ_NAME_PATH_SEPARATOR; BufferLength = (USHORT)((ULONG_PTR)ObjectNameInfo + NameInfoSize - (ULONG_PTR)StringBuffer); ObjectNameInfo->Name.MaximumLength = (USHORT)BufferLength; ObjectNameInfo->Name.Length = (USHORT)(BufferLength - sizeof( UNICODE_NULL )); ObjectNameInfo->Name.Buffer = OriginalBuffer; // // If one of the parent directories disappeared, the final length // will be smaller than we estimated before. We need to move the string to // the beginning and adjust the returned size. // if (OriginalBuffer != StringBuffer) { RtlMoveMemory(OriginalBuffer, StringBuffer, BufferLength); *ReturnLength = BufferLength + sizeof( OBJECT_NAME_INFORMATION ); } } except( EXCEPTION_EXECUTE_HANDLER ) { // // Fall through, since we cannot undo what we have done. // // This should probably get the exception code and return // that value. However, the caller we'll get an exception // at the first access of the ObjectNameInfo // } ObpDereferenceNameInfo( NameInfo ); if (ReferencedObject) { ObDereferenceObject( ReferencedObject ); } if (ForceRetry) { // // The query failed maybe because the object name changed during the query // NameInfo = ObpReferenceNameInfo( ObjectHeader ); ForceRetry = FALSE; goto RETRY; } return STATUS_SUCCESS; } </textarea>