ObQueryNameString源码解读
ObQueryNameString返回指定内核对象的名称。该函数只返回命名对象名称和拥有查询函数的对象名称,其他所有的对象都返回空结构。
下一步是函数的步骤:
第一步.先检查内核对象是否有专门的名称查询函数QueryNameProcedure函数,有则调用此函数并返回。WIXP中只有KEY和FILE对象
有专门的名称查询函数。
第二步.检查是否为非命名对象或者命名对象的名称为NULL, 若是检查缓冲区大小后则返回空结构(ObjectNameInfo.Name为{0,0,0})。
第三步.对于命名对象,追根溯源直到根目录,获得需要的缓冲区的大小。
第四步.若用户提供的缓冲区大小不够,则返回STATUS_INFO_LENGTH_MISMATCH,并返回实际需要的缓冲区大小。
第五步.再次追根溯源到根目录,将名称拷贝至用户缓冲区中。
NTSTATUS
ObQueryNameString (
IN PVOID Object,
OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
IN ULONG Length,
OUT PULONG ReturnLength
)
/*++
Routine description:
This routine processes a query of an object's name information
Arguments:
Object - Supplies the object being queried,内核对象的指针,该值不能为NULL.
ObjectNameInfo - Supplies the buffer to store the name string nformation
一个由用户提供的存放返回值得缓冲区,若不知大小则可以为NULL,由ReturnLength返回需要的缓冲区大小。
Length - Specifies the length, in bytes, of the original object name info buffer.
缓冲区的字节数.该值必须包括OBJECT_NAME_INFORMATION结构和对象名称的长度。根据DDK上推荐该值为1024。
ReturnLength - Contains the number of bytes already used up in the object name info. On return this receives an updated byte count. 返回的数据的大小。此值包括OBJECT_NAME_INFORMATION结构和对象名称的长度。
(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
Return Value:
An appropriate status value
--*/
{
NTSTATUS Status;
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER ObjectDirectoryHeader;
POBJECT_DIRECTORY ObjectDirectory;
ULONG NameInfoSize;
PUNICODE_STRING String;
PWCH StringBuffer;
ULONG NameSize;
PAGED_CODE();
//
// Get the object header and name info record if it exists
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object ); //获得Object的头部objectHeader
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader ); //若ObjectHeader是命名对象,则取对象头部的名称;
// 否则返回NULL.
//
// 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 {
KIRQL SaveIrql;
ObpBeginTypeSpecificCallOut( SaveIrql );
ObpEndTypeSpecificCallOut( SaveIrql, "Query", ObjectHeader->Type, Object );
Status = (*ObjectHeader->Type->TypeInfo.QueryNameProcedure)( Object,
(BOOLEAN)((NameInfo != NULL) && (NameInfo->Name.Length != 0)),
ObjectNameInfo,
Length,
ReturnLength );
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
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. 第二步.若没有名称查询函数,则查看不是命名对象或者名称为空则ObQueryNameString返回空。
//
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 ) {
return( GetExceptionCode() );
}
if (Length < NameInfoSize) {
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.
//
// **** This should probably get the exception code and return
// that value
//
}
return( STATUS_SUCCESS );
}
//
// First lock the directory mutex to stop before we chase stuff down
// 第三步.对象为有值得命名对象,则先获取整个名称的长度,即需要的内存大小。
ObpEnterRootDirectoryMutex();
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
// 先判断对象是否就是根目录对象,则名称的长度为sizeof( OBJ_NAME_PATH_SEPARATOR )
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;
NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
//
// 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 = OBJECT_HEADER_TO_NAME_INFO( ObjectDirectoryHeader );
if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {
//
// This directory has a name so add it to the accomulated size and move up the tree
//
NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
ObjectDirectory = NameInfo->Directory;
} 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;
}
}
}
//
// 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
//第四步. 置返回长度的大小,若用户提供的缓冲区的大小小于此值,则返回错误STATUS_INFO_LENGTH_MISMATCH。
// 注意了,用户提供的缓冲区的大小是有函数参数Length指定的,
// 而不是有参数ObjectNameInfo.Name.MaximumLength
// 或者ObjectNameInfo.Name.Length指定的。(其实后面两个值大小无所谓)
try {
*ReturnLength = NameInfoSize;
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
leave;
}
if (Length < NameInfoSize) {
Status = STATUS_INFO_LENGTH_MISMATCH;
leave;
}
//
// **** the following IF isn't necessary because name info size is
// already guaranteed to be nonzero from about 23 lines above
//
if (NameInfoSize != 0) {
//
// 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);
//睁大眼睛:ObQueryNameString并没有利用参数ObjectNameInfo.Name.Buffer来指出
//缓冲区的地址,而是简单地认为名称紧跟着OBJECT_NAME_INFORMATION结构。
//这个问题我在博文中已经指出了。
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
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);
RtlMoveMemory( StringBuffer, String->Buffer, String->Length );
//
// While we are not at the root directory we'll keep moving up
//
ObjectDirectory = NameInfo->Directory;
while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) {
//
// Get the name information for this directory
//
ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
NameInfo = OBJECT_HEADER_TO_NAME_INFO( 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);
RtlMoveMemory( StringBuffer, String->Buffer, String->Length );
ObjectDirectory = NameInfo->Directory;
} 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);
RtlMoveMemory( StringBuffer, OBP_MISSING_NAME_LITERAL, OBP_MISSING_NAME_LITERAL_SIZE );
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;
ObjectNameInfo->Name.Length = (USHORT)NameSize;
ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize+sizeof( UNICODE_NULL ));
ObjectNameInfo->Name.Buffer = StringBuffer;
} 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
//
}
}
Status = STATUS_SUCCESS;
} finally {
ObpLeaveRootDirectoryMutex();
}
return Status;
}